gpu,app,internal/glimpl: update GL backend for the compute renderer

Modern graphics APIs have immutable objects, with mutable data. For example,
a texture's dimensions are immutable, while the texture contents is not.
Change the GPU API abstraction to match.

Clearing a Texture is convenient to do with a plain []byte. Generalize
Texture.Upload to take a plain byte slice and introduce a helper function for
uploading *image.RGBA data.

Add TextureFormatRGBA8 a format for the linear RGB colorspace.

Add OpenGL ES 3.1 functions for compute programs.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2020-12-30 16:48:22 +01:00
parent 7d24b790a2
commit bfe2d04c60
11 changed files with 400 additions and 89 deletions
+39 -16
View File
@@ -121,12 +121,7 @@ func NewDevice() (*Device, error) {
_IUnknownRelease(unsafe.Pointer(d3dctx), d3dctx.vtbl.Release)
return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl)
}
floatFormat, ok := detectFloatFormat(d3ddev)
if !ok {
_IUnknownRelease(unsafe.Pointer(d3ddev), d3ddev.vtbl.Release)
_IUnknownRelease(unsafe.Pointer(d3dctx), d3dctx.vtbl.Release)
return nil, fmt.Errorf("d3d11: no available floating point formats")
}
floatFormat, _ := detectFloatFormat(d3ddev)
dev.floatFormat = floatFormat
dev.depthStates = make(map[depthState]*_ID3D11DepthStencilState)
dev.blendStates = make(map[blendState]*_ID3D11BlendState)
@@ -253,6 +248,9 @@ func NewBackend(d *Device) (*Backend, error) {
caps := backend.Caps{
MaxTextureSize: 2048, // 9.1 maximum
}
if d.floatFormat != 0 {
caps.Features |= backend.FeatureFloatRenderTargets
}
switch {
case d.featLvl >= _D3D_FEATURE_LEVEL_11_0:
caps.MaxTextureSize = 16384
@@ -524,6 +522,10 @@ func (b *Backend) NewImmutableBuffer(typ backend.BufferBinding, data []byte) (ba
return &Buffer{backend: b, buf: buf, bind: bind, immutable: true}, nil
}
func (b *Backend) NewComputeProgram(shader backend.ShaderSources) (backend.Program, error) {
panic("not implemented")
}
func (b *Backend) NewProgram(vertexShader, fragmentShader backend.ShaderSources) (backend.Program, error) {
vs, err := b.dev.dev.CreateVertexShader(vertexShader.HLSL)
if err != nil {
@@ -667,17 +669,30 @@ func (b *Backend) BlendFunc(sfactor, dfactor backend.BlendFactor) {
b.blendState.dfactor = dfactor
}
func (t *Texture) Upload(img *image.RGBA) {
b := img.Bounds()
w := b.Dx()
if img.Stride != w*4 {
panic("unsupported stride")
func (b *Backend) BindImageTexture(unit int, tex backend.Texture, access backend.AccessBits, f backend.TextureFormat) {
panic("not implemented")
}
func (b *Backend) MemoryBarrier() {
panic("not implemented")
}
func (b *Backend) DispatchCompute(x, y, z int) {
panic("not implemented")
}
func (t *Texture) Upload(offset, size image.Point, pixels []byte) {
stride := size.X * 4
dst := &_D3D11_BOX{
left: uint32(offset.X),
top: uint32(offset.Y),
right: uint32(offset.X + size.X),
bottom: uint32(offset.Y + size.Y),
front: 0,
back: 1,
}
start := (b.Min.X + b.Min.Y*w) * 4
end := (b.Max.X + (b.Max.Y-1)*w) * 4
pixels := img.Pix[start:end]
res := (*_ID3D11Resource)(unsafe.Pointer(t.tex))
t.backend.dev.ctx.UpdateSubresource(res, uint32(img.Stride), uint32(len(pixels)), pixels)
t.backend.dev.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels)
}
func (t *Texture) Release() {
@@ -710,6 +725,10 @@ func (p *Program) Release() {
p.frag.shader = nil
}
func (p *Program) SetStorageBuffer(binding int, buffer backend.Buffer) {
panic("not implemented")
}
func (p *Program) SetVertexUniforms(buf backend.Buffer) {
p.vert.uniforms = buf.(*Buffer)
}
@@ -726,8 +745,12 @@ func (b *Backend) BindIndexBuffer(buf backend.Buffer) {
b.dev.ctx.IASetIndexBuffer(buf.(*Buffer).buf, _DXGI_FORMAT_R16_UINT, 0)
}
func (b *Buffer) Download(data []byte) error {
panic("not implemented")
}
func (b *Buffer) Upload(data []byte) {
b.backend.dev.ctx.UpdateSubresource((*_ID3D11Resource)(unsafe.Pointer(b.buf)), 0, 0, data)
b.backend.dev.ctx.UpdateSubresource((*_ID3D11Resource)(unsafe.Pointer(b.buf)), nil, 0, 0, data)
}
func (b *Buffer) Release() {
+2 -2
View File
@@ -1127,14 +1127,14 @@ func (c *_ID3D11DeviceContext) PSSetShader(s *_ID3D11PixelShader) {
)
}
func (c *_ID3D11DeviceContext) UpdateSubresource(res *_ID3D11Resource, rowPitch, depthPitch uint32, data []byte) {
func (c *_ID3D11DeviceContext) UpdateSubresource(res *_ID3D11Resource, dstBox *_D3D11_BOX, rowPitch, depthPitch uint32, data []byte) {
syscall.Syscall9(
c.vtbl.UpdateSubresource,
7,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(res)),
0, // DstSubresource
0, // pDstBox
uintptr(unsafe.Pointer(dstBox)),
uintptr(unsafe.Pointer(&data[0])),
uintptr(rowPitch),
uintptr(depthPitch),
+11 -11
View File
@@ -71,14 +71,14 @@ func (s *FBO) Blit() {
s.c.Uniform1i(s.c.GetUniformLocation(prog, "tex"), 0)
s.quad = s.c.CreateBuffer()
s.c.BindBuffer(glimpl.ARRAY_BUFFER, s.quad)
s.c.BufferData(glimpl.ARRAY_BUFFER,
unsafe.BytesView([]float32{
-1, +1, 0, 1,
+1, +1, 1, 1,
-1, -1, 0, 0,
+1, -1, 1, 0,
}),
glimpl.STATIC_DRAW)
coords := unsafe.BytesView([]float32{
-1, +1, 0, 1,
+1, +1, 1, 1,
-1, -1, 0, 0,
+1, -1, 1, 0,
})
s.c.BufferData(glimpl.ARRAY_BUFFER, len(coords), glimpl.STATIC_DRAW)
s.c.BufferSubData(glimpl.ARRAY_BUFFER, 0, coords)
s.blitted = true
}
s.c.BindFramebuffer(glimpl.FRAMEBUFFER, glimpl.Framebuffer{})
@@ -112,9 +112,9 @@ func (s *FBO) Refresh(w, h int) error {
}
s.c.BindTexture(glimpl.TEXTURE_2D, s.colorTex)
if s.gl3 {
s.c.TexImage2D(glimpl.TEXTURE_2D, 0, glimpl.SRGB8_ALPHA8, w, h, glimpl.RGBA, glimpl.UNSIGNED_BYTE, nil)
s.c.TexImage2D(glimpl.TEXTURE_2D, 0, glimpl.SRGB8_ALPHA8, w, h, glimpl.RGBA, glimpl.UNSIGNED_BYTE)
} else /* EXT_sRGB */ {
s.c.TexImage2D(glimpl.TEXTURE_2D, 0, glimpl.SRGB_ALPHA_EXT, w, h, glimpl.SRGB_ALPHA_EXT, glimpl.UNSIGNED_BYTE, nil)
s.c.TexImage2D(glimpl.TEXTURE_2D, 0, glimpl.SRGB_ALPHA_EXT, w, h, glimpl.SRGB_ALPHA_EXT, glimpl.UNSIGNED_BYTE)
}
currentRB := glimpl.Renderbuffer(s.c.GetBinding(glimpl.RENDERBUFFER_BINDING))
s.c.BindRenderbuffer(glimpl.RENDERBUFFER, s.depthBuffer)
@@ -136,7 +136,7 @@ func (s *FBO) Refresh(w, h int) error {
var pixel [4]byte
s.c.ReadPixels(0, 0, 1, 1, glimpl.RGBA, glimpl.UNSIGNED_BYTE, pixel[:])
if pixel[0] == 128 { // Correct sRGB color value is ~188
s.c.TexImage2D(glimpl.TEXTURE_2D, 0, glimpl.RGBA, w, h, glimpl.RGBA, glimpl.UNSIGNED_BYTE, nil)
s.c.TexImage2D(glimpl.TEXTURE_2D, 0, glimpl.RGBA, w, h, glimpl.RGBA, glimpl.UNSIGNED_BYTE)
if st := s.c.CheckFramebufferStatus(glimpl.FRAMEBUFFER); st != glimpl.FRAMEBUFFER_COMPLETE {
return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError())
}
+6 -2
View File
@@ -94,8 +94,12 @@ func (c *d3d11Context) Release() {
if c.fbo != nil {
c.fbo.Release()
}
c.swchain.Release()
c.Device.Release()
if c.swchain != nil {
c.swchain.Release()
}
if c.Device != nil {
c.Device.Release()
}
c.fbo = nil
c.swchain = nil
c.Device = nil
+48 -10
View File
@@ -23,6 +23,7 @@ type Device interface {
NewFramebuffer(tex Texture, depthBits int) (Framebuffer, error)
NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error)
NewBuffer(typ BufferBinding, size int) (Buffer, error)
NewComputeProgram(shader ShaderSources) (Program, error)
NewProgram(vertexShader, fragmentShader ShaderSources) (Program, error)
NewInputLayout(vertexShader ShaderSources, layout []InputDesc) (InputLayout, error)
@@ -43,17 +44,29 @@ type Device interface {
BindTexture(unit int, t Texture)
BindVertexBuffer(b Buffer, stride, offset int)
BindIndexBuffer(b Buffer)
BindImageTexture(unit int, texture Texture, access AccessBits, format TextureFormat)
MemoryBarrier()
DispatchCompute(x, y, z int)
}
type ShaderSources struct {
GLSL100ES string
GLSL300ES string
GLSL130 string
GLSL150 string
HLSL []byte
Uniforms UniformsReflection
Inputs []InputLocation
Textures []TextureBinding
Name string
GLSL100ES string
GLSL300ES string
GLSL310ES string
GLSL130 string
GLSL150 string
HLSL []byte
Uniforms UniformsReflection
Inputs []InputLocation
Textures []TextureBinding
StorageBuffers []StorageBufferBinding
}
type StorageBufferBinding struct {
Binding int
BlockSize int
}
type UniformsReflection struct {
@@ -105,6 +118,8 @@ type InputLayout interface {
Release()
}
type AccessBits uint8
type BlendFactor uint8
type DrawMode uint8
@@ -127,6 +142,7 @@ type Caps struct {
type Program interface {
Release()
SetStorageBuffer(binding int, buf Buffer)
SetVertexUniforms(buf Buffer)
SetFragmentUniforms(buf Buffer)
}
@@ -134,6 +150,7 @@ type Program interface {
type Buffer interface {
Release()
Upload(data []byte)
Download(data []byte) error
}
type Framebuffer interface {
@@ -150,7 +167,7 @@ type Timer interface {
}
type Texture interface {
Upload(img *image.RGBA)
Upload(offset, size image.Point, pixels []byte)
Release()
}
@@ -171,11 +188,18 @@ const (
BufferBindingUniforms
BufferBindingTexture
BufferBindingFramebuffer
BufferBindingShaderStorage
)
const (
TextureFormatSRGB TextureFormat = iota
TextureFormatFloat
TextureFormatRGBA8
)
const (
AccessRead AccessBits = 1 + iota
AccessWrite
)
const (
@@ -184,7 +208,9 @@ const (
)
const (
FeatureTimers Features = iota
FeatureTimers Features = 1 << iota
FeatureFloatRenderTargets
FeatureCompute
)
const (
@@ -202,3 +228,15 @@ const (
func (f Features) Has(feats Features) bool {
return f&feats == feats
}
func UploadImage(t Texture, offset image.Point, img *image.RGBA) {
var pixels []byte
size := img.Bounds().Size()
if img.Stride != size.X*4 {
panic("unsupported stride")
}
start := img.PixOffset(0, 0)
end := img.PixOffset(size.X, size.Y-1)
pixels = img.Pix[start:end]
t.Upload(offset, size, pixels)
}
+127 -24
View File
@@ -37,7 +37,7 @@ type glstate struct {
// nattr is the current number of enabled vertex arrays.
nattr int
prog *gpuProgram
texUnits [2]*gpuTexture
texUnits [4]*gpuTexture
layout *gpuInputLayout
buffer bufferBinding
}
@@ -87,6 +87,7 @@ type gpuProgram struct {
nattr int
vertUniforms uniformsTracker
fragUniforms uniformsTracker
storage [storageBindings]*gpuBuffer
}
type uniformsTracker struct {
@@ -111,13 +112,17 @@ type gpuInputLayout struct {
// textureTriple holds the type settings for
// a TexImage2D call.
type textureTriple struct {
internalFormat int
internalFormat glimpl.Enum
format glimpl.Enum
typ glimpl.Enum
}
type Context = glimpl.Context
const (
storageBindings = 32
)
// NewBackend returns a new Backend.
//
// Pass a WebGL context if GOOS is "js", otherwise pass nil for the current
@@ -133,24 +138,28 @@ func NewBackend(ctx Context) (*Backend, error) {
if err != nil {
return nil, err
}
floatTriple, err := floatTripleFor(f, ver, exts)
if err != nil {
return nil, err
}
floatTriple, ffboErr := floatTripleFor(f, ver, exts)
srgbaTriple, err := srgbaTripleFor(ver, exts)
if err != nil {
return nil, err
}
ubo := ver[0] >= 3 && gles
gles30 := gles && ver[0] >= 3
gles31 := gles && (ver[0] > 3 || (ver[0] == 3 && ver[1] >= 1))
b := &Backend{
glver: ver,
gles: gles,
ubo: ubo,
ubo: gles30,
funcs: f,
floatTriple: floatTriple,
alphaTriple: alphaTripleFor(ver),
srgbaTriple: srgbaTriple,
}
if ffboErr == nil {
b.feats.Features |= backend.FeatureFloatRenderTargets
}
if gles31 {
b.feats.Features |= backend.FeatureCompute
}
if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") {
b.feats.Features |= backend.FeatureTimers
}
@@ -232,6 +241,8 @@ func (b *Backend) NewTexture(format backend.TextureFormat, width, height int, mi
tex.triple = b.floatTriple
case backend.TextureFormatSRGB:
tex.triple = b.srgbaTriple
case backend.TextureFormatRGBA8:
tex.triple = textureTriple{glimpl.RGBA8, glimpl.RGBA, glimpl.UNSIGNED_BYTE}
default:
return nil, errors.New("unsupported texture format")
}
@@ -240,7 +251,12 @@ func (b *Backend) NewTexture(format backend.TextureFormat, width, height int, mi
b.funcs.TexParameteri(glimpl.TEXTURE_2D, glimpl.TEXTURE_MIN_FILTER, toTexFilter(minFilter))
b.funcs.TexParameteri(glimpl.TEXTURE_2D, glimpl.TEXTURE_WRAP_S, glimpl.CLAMP_TO_EDGE)
b.funcs.TexParameteri(glimpl.TEXTURE_2D, glimpl.TEXTURE_WRAP_T, glimpl.CLAMP_TO_EDGE)
b.funcs.TexImage2D(glimpl.TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ, nil)
if b.gles && b.glver[0] >= 3 {
// Immutable textures are required for BindImageTexture, and can't hurt otherwise.
b.funcs.TexStorage2D(glimpl.TEXTURE_2D, 1, tex.triple.internalFormat, width, height)
} else {
b.funcs.TexImage2D(glimpl.TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ)
}
if err := glErr(b.funcs); err != nil {
tex.Release()
return nil, err
@@ -267,6 +283,9 @@ func (b *Backend) NewBuffer(typ backend.BufferBinding, size int) (backend.Buffer
buf.Release()
return nil, err
}
firstBinding := firstBufferType(typ)
b.funcs.BindBuffer(firstBinding, buf.obj)
b.funcs.BufferData(firstBinding, size, glimpl.DYNAMIC_DRAW)
}
return buf, nil
}
@@ -275,6 +294,9 @@ func (b *Backend) NewImmutableBuffer(typ backend.BufferBinding, data []byte) (ba
glErr(b.funcs)
obj := b.funcs.CreateBuffer()
buf := &gpuBuffer{backend: b, obj: obj, typ: typ, size: len(data), hasBuffer: true}
firstBinding := firstBufferType(typ)
b.funcs.BindBuffer(firstBinding, buf.obj)
b.funcs.BufferData(firstBinding, len(data), glimpl.STATIC_DRAW)
buf.Upload(data)
buf.immutable = true
if err := glErr(b.funcs); err != nil {
@@ -291,6 +313,40 @@ func glErr(f *glimpl.Functions) error {
return nil
}
func (b *Backend) MemoryBarrier() {
b.funcs.MemoryBarrier(glimpl.ALL_BARRIER_BITS)
}
func (b *Backend) DispatchCompute(x, y, z int) {
if p := b.state.prog; p != nil {
for binding, buf := range p.storage {
if buf != nil {
b.funcs.BindBufferBase(glimpl.SHADER_STORAGE_BUFFER, binding, buf.obj)
}
}
}
b.funcs.DispatchCompute(x, y, z)
}
func (b *Backend) BindImageTexture(unit int, tex backend.Texture, access backend.AccessBits, f backend.TextureFormat) {
t := tex.(*gpuTexture)
var acc glimpl.Enum
switch access {
case backend.AccessWrite:
acc = glimpl.WRITE_ONLY
default:
panic("unsupported access bits")
}
var format glimpl.Enum
switch f {
case backend.TextureFormatRGBA8:
format = glimpl.RGBA8
default:
panic("unsupported format")
}
b.funcs.BindImageTexture(unit, t.obj, 0, false, 0, acc, format)
}
func (b *Backend) bindTexture(unit int, t *gpuTexture) {
if b.state.texUnits[unit] != t {
b.funcs.ActiveTexture(glimpl.TEXTURE0 + glimpl.Enum(unit))
@@ -433,6 +489,18 @@ func (b *Backend) NewInputLayout(vs backend.ShaderSources, layout []backend.Inpu
}, nil
}
func (b *Backend) NewComputeProgram(src backend.ShaderSources) (backend.Program, error) {
p, err := glimpl.CreateComputeProgram(b.funcs, src.GLSL310ES)
if err != nil {
return nil, fmt.Errorf("%s: %v", src.Name, err)
}
gpuProg := &gpuProgram{
backend: b,
obj: p,
}
return gpuProg, nil
}
func (b *Backend) NewProgram(vertShader, fragShader backend.ShaderSources) (backend.Program, error) {
attr := make([]string, len(vertShader.Inputs))
for _, inp := range vertShader.Inputs {
@@ -503,6 +571,14 @@ func lookupUniform(funcs *glimpl.Functions, p glimpl.Program, loc backend.Unifor
return uniformLocation{uniform: u, offset: loc.Offset, typ: loc.Type, size: loc.Size}
}
func (p *gpuProgram) SetStorageBuffer(binding int, buffer backend.Buffer) {
buf := buffer.(*gpuBuffer)
if buf.typ&backend.BufferBindingShaderStorage == 0 {
panic("not a shader storage buffer")
}
p.storage[binding] = buf
}
func (p *gpuProgram) SetVertexUniforms(buffer backend.Buffer) {
p.vertUniforms.setBuffer(buffer)
}
@@ -600,10 +676,31 @@ func (b *gpuBuffer) Upload(data []byte) {
if b.hasBuffer {
firstBinding := firstBufferType(b.typ)
b.backend.funcs.BindBuffer(firstBinding, b.obj)
b.backend.funcs.BufferData(firstBinding, data, glimpl.STATIC_DRAW)
b.backend.funcs.BufferSubData(firstBinding, 0, data)
}
}
func (b *gpuBuffer) Download(data []byte) error {
if len(data) > b.size {
panic("buffer size overflow")
}
if !b.hasBuffer {
copy(data, b.data)
return nil
}
firstBinding := firstBufferType(b.typ)
b.backend.funcs.BindBuffer(firstBinding, b.obj)
bufferMap := b.backend.funcs.MapBufferRange(firstBinding, 0, len(data), glimpl.MAP_READ_BIT)
if bufferMap == nil {
return fmt.Errorf("MapBufferRange: error %#x", b.backend.funcs.GetError())
}
copy(data, bufferMap)
if !b.backend.funcs.UnmapBuffer(firstBinding) {
return errors.New("buffer content lost")
}
return nil
}
func (b *gpuBuffer) Release() {
if b.hasBuffer {
b.backend.funcs.DeleteBuffer(b.obj)
@@ -649,10 +746,20 @@ func (b *Backend) BindIndexBuffer(buf backend.Buffer) {
b.funcs.BindBuffer(glimpl.ELEMENT_ARRAY_BUFFER, gbuf.obj)
}
func (b *Backend) BlitFramebuffer(dst, src backend.Framebuffer, srect, drect image.Rectangle) {
b.funcs.BindFramebuffer(glimpl.DRAW_FRAMEBUFFER, dst.(*gpuFramebuffer).obj)
b.funcs.BindFramebuffer(glimpl.READ_FRAMEBUFFER, src.(*gpuFramebuffer).obj)
b.funcs.BlitFramebuffer(
srect.Min.X, srect.Min.Y, srect.Max.X, srect.Max.Y,
drect.Min.X, drect.Min.Y, drect.Max.X, drect.Max.Y,
glimpl.COLOR_BUFFER_BIT|glimpl.DEPTH_BUFFER_BIT|glimpl.STENCIL_BUFFER_BIT,
glimpl.NEAREST)
}
func (f *gpuFramebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
glErr(f.backend.funcs)
f.backend.BindFramebuffer(f)
if len(pixels) < src.Dx()*src.Dy() {
if len(pixels) < src.Dx()*src.Dy()*4 {
return errors.New("unexpected RGBA size")
}
f.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, src.Dx(), src.Dy(), glimpl.RGBA, glimpl.UNSIGNED_BYTE, pixels)
@@ -714,18 +821,12 @@ func (t *gpuTexture) Release() {
t.backend.funcs.DeleteTexture(t.obj)
}
func (t *gpuTexture) Upload(img *image.RGBA) {
t.backend.BindTexture(0, t)
var pixels []byte
b := img.Bounds()
w, h := b.Dx(), b.Dy()
if img.Stride != w*4 {
panic("unsupported stride")
func (t *gpuTexture) Upload(offset, size image.Point, pixels []byte) {
if min := size.X * size.Y * 4; min > len(pixels) {
panic(fmt.Errorf("size %d larger than data %d", min, len(pixels)))
}
start := (b.Min.X + b.Min.Y*w) * 4
end := (b.Max.X + (b.Max.Y-1)*w) * 4
pixels = img.Pix[start:end]
t.backend.funcs.TexImage2D(glimpl.TEXTURE_2D, 0, t.triple.internalFormat, w, h, t.triple.format, t.triple.typ, pixels)
t.backend.BindTexture(0, t)
t.backend.funcs.TexSubImage2D(glimpl.TEXTURE_2D, 0, offset.X, offset.Y, size.X, size.Y, t.triple.format, t.triple.typ, pixels)
}
func (t *gpuTimer) Begin() {
@@ -792,7 +893,7 @@ func floatTripleFor(f *glimpl.Functions, ver [2]int, exts []string) (textureTrip
var attempts []string
for _, tt := range triples {
const size = 256
f.TexImage2D(glimpl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ, nil)
f.TexImage2D(glimpl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ)
f.FramebufferTexture2D(glimpl.FRAMEBUFFER, glimpl.COLOR_ATTACHMENT0, glimpl.TEXTURE_2D, tex, 0)
st := f.CheckFramebufferStatus(glimpl.FRAMEBUFFER)
if st == glimpl.FRAMEBUFFER_COMPLETE {
@@ -815,7 +916,7 @@ func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) {
}
func alphaTripleFor(ver [2]int) textureTriple {
intf, f := glimpl.R8, glimpl.Enum(glimpl.RED)
intf, f := glimpl.Enum(glimpl.R8), glimpl.Enum(glimpl.RED)
if ver[0] < 3 {
// R8, RED not supported on OpenGL ES 2.0.
intf, f = glimpl.LUMINANCE, glimpl.Enum(glimpl.LUMINANCE)
@@ -840,6 +941,8 @@ func firstBufferType(typ backend.BufferBinding) glimpl.Enum {
return glimpl.ARRAY_BUFFER
case typ&backend.BufferBindingUniforms != 0:
return glimpl.UNIFORM_BUFFER
case typ&backend.BufferBindingShaderStorage != 0:
return glimpl.SHADER_STORAGE_BUFFER
default:
panic("unsupported buffer type")
}
+1 -1
View File
@@ -479,7 +479,7 @@ func (r *renderer) texHandle(cache *resourceCache, data imageOpData) backend.Tex
if err != nil {
panic(err)
}
handle.Upload(data.src)
backend.UploadImage(handle, image.Pt(0, 0), data.src)
tex.tex = handle
return tex.tex
}
+22 -2
View File
@@ -8,19 +8,24 @@ type (
)
const (
ALL_BARRIER_BITS = 0xffffffff
ARRAY_BUFFER = 0x8892
BLEND = 0xbe2
CLAMP_TO_EDGE = 0x812f
COLOR_ATTACHMENT0 = 0x8ce0
COLOR_BUFFER_BIT = 0x4000
COMPILE_STATUS = 0x8b81
COMPUTE_SHADER = 0x91B9
DEPTH_BUFFER_BIT = 0x100
DEPTH_ATTACHMENT = 0x8d00
DEPTH_COMPONENT16 = 0x81a5
DEPTH_COMPONENT24 = 0x81A6
DEPTH_COMPONENT32F = 0x8CAC
DEPTH_TEST = 0xb71
DRAW_FRAMEBUFFER = 0x8CA9
DST_COLOR = 0x306
DYNAMIC_DRAW = 0x88E8
DYNAMIC_READ = 0x88E9
ELEMENT_ARRAY_BUFFER = 0x8893
EXTENSIONS = 0x1f03
FALSE = 0
@@ -39,6 +44,7 @@ const (
LINEAR = 0x2601
LINK_STATUS = 0x8b82
LUMINANCE = 0x1909
MAP_READ_BIT = 0x0001
MAX_TEXTURE_SIZE = 0xd33
NEAREST = 0x2600
NO_ERROR = 0x0
@@ -50,6 +56,8 @@ const (
R16F = 0x822d
R8 = 0x8229
READ_FRAMEBUFFER = 0x8ca8
READ_ONLY = 0x88B8
READ_WRITE = 0x88BA
RED = 0x1903
RENDERER = 0x1F01
RENDERBUFFER = 0x8d41
@@ -59,12 +67,14 @@ const (
RGB = 0x1907
RGBA = 0x1908
RGBA8 = 0x8058
SHADER_STORAGE_BUFFER = 0x90D2
SHORT = 0x1402
SRGB = 0x8c40
SRGB_ALPHA_EXT = 0x8c42
SRGB8 = 0x8c41
SRGB8_ALPHA8 = 0x8c43
STATIC_DRAW = 0x88e4
STENCIL_BUFFER_BIT = 0x00000400
TEXTURE_2D = 0xde1
TEXTURE_MAG_FILTER = 0x2800
TEXTURE_MIN_FILTER = 0x2801
@@ -81,6 +91,7 @@ const (
UNSIGNED_SHORT = 0x1403
VERSION = 0x1f02
VERTEX_SHADER = 0x8b31
WRITE_ONLY = 0x88B9
ZERO = 0x0
// EXT_disjoint_timer_query
@@ -96,11 +107,14 @@ var _ interface {
BindBuffer(target Enum, b Buffer)
BindBufferBase(target Enum, index int, buffer Buffer)
BindFramebuffer(target Enum, fb Framebuffer)
BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum)
BindRenderbuffer(target Enum, fb Renderbuffer)
BindTexture(target Enum, t Texture)
BlendEquation(mode Enum)
BlendFunc(sfactor, dfactor Enum)
BufferData(target Enum, src []byte, usage Enum)
BlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int, mask Enum, filter Enum)
BufferData(target Enum, size int, usage Enum)
BufferSubData(target Enum, offset int, src []byte)
CheckFramebufferStatus(target Enum) Enum
Clear(mask Enum)
ClearColor(red, green, blue, alpha float32)
@@ -124,6 +138,7 @@ var _ interface {
DepthMask(mask bool)
DisableVertexAttribArray(a Attrib)
Disable(cap Enum)
DispatchCompute(x, y, z int)
DrawArrays(mode Enum, first, count int)
DrawElements(mode Enum, count int, ty Enum, offset int)
Enable(cap Enum)
@@ -144,11 +159,15 @@ var _ interface {
GetUniformLocation(p Program, name string) Uniform
InvalidateFramebuffer(target, attachment Enum)
LinkProgram(p Program)
MapBufferRange(target Enum, offset, length int, access Enum) []byte
MemoryBarrier(barriers Enum)
ReadPixels(x, y, width, height int, format, ty Enum, data []byte)
RenderbufferStorage(target, internalformat Enum, width, height int)
ShaderSource(s Shader, src string)
TexImage2D(target Enum, level int, internalFormat int, width, height int, format, ty Enum, data []byte)
TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum)
TexParameteri(target, pname Enum, param int)
TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int)
TexSubImage2D(target Enum, level, xoff, yoff int, width, height int, format, ty Enum, data []byte)
UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint)
Uniform1f(dst Uniform, v float32)
Uniform1i(dst Uniform, v int)
@@ -156,6 +175,7 @@ var _ interface {
Uniform3f(dst Uniform, v0, v1, v2 float32)
Uniform4f(dst Uniform, v0, v1, v2, v3 float32)
UseProgram(p Program)
UnmapBuffer(target Enum) bool
VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int)
Viewport(x, y, width, height int)
} = (*Functions)(nil)
+93 -8
View File
@@ -51,6 +51,13 @@ static void (*_glEndQuery)(GLenum target);
static void (*_glGenQueries)(GLsizei n, GLuint *ids);
static void (*_glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params);
static const GLubyte* (*_glGetStringi)(GLenum name, GLuint index);
static void (*_glMemoryBarrier)(GLbitfield barriers);
static void (*_glDispatchCompute)(GLuint x, GLuint y, GLuint z);
static void* (*_glMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
static GLboolean (*_glUnmapBuffer)(GLenum target);
static void (*_glBindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);
static void (*_glTexStorage2D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
static void (*_glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
// The pointer-free version of glVertexAttribPointer, to avoid the Cgo pointer checks.
__attribute__ ((visibility ("hidden"))) void gio_glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, uintptr_t offset) {
@@ -108,6 +115,34 @@ __attribute__ ((visibility ("hidden"))) void gio_glGetQueryObjectuiv(GLuint id,
_glGetQueryObjectuiv(id, pname, params);
}
__attribute__ ((visibility ("hidden"))) void gio_glMemoryBarrier(GLbitfield barriers) {
_glMemoryBarrier(barriers);
}
__attribute__ ((visibility ("hidden"))) void gio_glDispatchCompute(GLuint x, GLuint y, GLuint z) {
_glDispatchCompute(x, y, z);
}
__attribute__ ((visibility ("hidden"))) void *gio_glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {
return _glMapBufferRange(target, offset, length, access);
}
__attribute__ ((visibility ("hidden"))) GLboolean gio_glUnmapBuffer(GLenum target) {
return _glUnmapBuffer(target);
}
__attribute__ ((visibility ("hidden"))) void gio_glBindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) {
_glBindImageTexture(unit, texture, level, layered, layer, access, format);
}
__attribute__ ((visibility ("hidden"))) void gio_glTexStorage2D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height) {
_glTexStorage2D(target, levels, internalFormat, width, height);
}
__attribute__ ((visibility ("hidden"))) void gio_glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
_glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
__attribute__((constructor)) static void gio_loadGLFunctions() {
#ifdef __APPLE__
#if TARGET_OS_IPHONE
@@ -122,6 +157,7 @@ __attribute__((constructor)) static void gio_loadGLFunctions() {
_glGetUniformBlockIndex = glGetUniformBlockIndex;
_glUniformBlockBinding = glUniformBlockBinding;
_glGetStringi = glGetStringi;
_glTexStorage2D = glTexStorage2D;
#else
// Load libGLESv3 if available.
dlopen("libGLESv3.so", RTLD_NOW | RTLD_GLOBAL);
@@ -150,6 +186,14 @@ __attribute__((constructor)) static void gio_loadGLFunctions() {
_glGetQueryObjectuiv = dlsym(RTLD_DEFAULT, "glGetQueryObjectuiv");
if (_glGetQueryObjectuiv == NULL)
_glGetQueryObjectuiv = dlsym(RTLD_DEFAULT, "glGetQueryObjectuivEXT");
_glMemoryBarrier = dlsym(RTLD_DEFAULT, "glMemoryBarrier");
_glDispatchCompute = dlsym(RTLD_DEFAULT, "glDispatchCompute");
_glMapBufferRange = dlsym(RTLD_DEFAULT, "glMapBufferRange");
_glUnmapBuffer = dlsym(RTLD_DEFAULT, "glUnmapBuffer");
_glBindImageTexture = dlsym(RTLD_DEFAULT, "glBindImageTexture");
_glTexStorage2D = dlsym(RTLD_DEFAULT, "glTexStorage2D");
_glBlitFramebuffer = dlsym(RTLD_DEFAULT, "glBlitFramebuffer");
#endif
}
*/
@@ -204,6 +248,14 @@ func (f *Functions) BindRenderbuffer(target Enum, fb Renderbuffer) {
C.glBindRenderbuffer(C.GLenum(target), C.GLuint(fb.V))
}
func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
l := C.GLboolean(C.GL_FALSE)
if layered {
l = C.GL_TRUE
}
C.gio_glBindImageTexture(C.GLuint(unit), C.GLuint(t.V), C.GLint(level), l, C.GLint(layer), C.GLenum(access), C.GLenum(format))
}
func (f *Functions) BindTexture(target Enum, t Texture) {
C.glBindTexture(C.GLenum(target), C.GLuint(t.V))
}
@@ -216,12 +268,24 @@ func (f *Functions) BlendFunc(sfactor, dfactor Enum) {
C.glBlendFunc(C.GLenum(sfactor), C.GLenum(dfactor))
}
func (f *Functions) BufferData(target Enum, src []byte, usage Enum) {
func (f *Functions) BlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int, mask Enum, filter Enum) {
C.gio_glBlitFramebuffer(
C.GLint(sx0), C.GLint(sy0), C.GLint(sx1), C.GLint(sy1),
C.GLint(dx0), C.GLint(dy0), C.GLint(dx1), C.GLint(dy1),
C.GLenum(mask), C.GLenum(filter),
)
}
func (f *Functions) BufferData(target Enum, size int, usage Enum) {
C.glBufferData(C.GLenum(target), C.GLsizeiptr(size), nil, C.GLenum(usage))
}
func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
var p unsafe.Pointer
if len(src) > 0 {
p = unsafe.Pointer(&src[0])
}
C.glBufferData(C.GLenum(target), C.GLsizeiptr(len(src)), p, C.GLenum(usage))
C.glBufferSubData(C.GLenum(target), C.GLintptr(offset), C.GLsizeiptr(len(src)), p)
}
func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
@@ -338,6 +402,10 @@ func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
C.gio_glDrawElements(C.GLenum(mode), C.GLsizei(count), C.GLenum(ty), C.uintptr_t(offset))
}
func (f *Functions) DispatchCompute(x, y, z int) {
C.gio_glDispatchCompute(C.GLuint(x), C.GLuint(y), C.GLuint(z))
}
func (f *Functions) Enable(cap Enum) {
C.glEnable(C.GLenum(cap))
}
@@ -464,6 +532,18 @@ func (f *Functions) PixelStorei(pname Enum, param int32) {
C.glPixelStorei(C.GLenum(pname), C.GLint(param))
}
func (f *Functions) MemoryBarrier(barriers Enum) {
C.gio_glMemoryBarrier(C.GLbitfield(barriers))
}
func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
p := C.gio_glMapBufferRange(C.GLenum(target), C.GLintptr(offset), C.GLsizeiptr(length), C.GLbitfield(access))
if p == nil {
return nil
}
return (*([1 << 30]byte))(p)[:length:length]
}
func (f *Functions) Scissor(x, y, width, height int32) {
C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
}
@@ -487,12 +567,12 @@ func (f *Functions) ShaderSource(s Shader, src string) {
C.glShaderSource(C.GLuint(s.V), 1, &csrc, &strlen)
}
func (f *Functions) TexImage2D(target Enum, level int, internalFormat int, width int, height int, format Enum, ty Enum, data []byte) {
var p unsafe.Pointer
if len(data) > 0 {
p = unsafe.Pointer(&data[0])
}
C.glTexImage2D(C.GLenum(target), C.GLint(level), C.GLint(internalFormat), C.GLsizei(width), C.GLsizei(height), 0, C.GLenum(format), C.GLenum(ty), p)
func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) {
C.glTexImage2D(C.GLenum(target), C.GLint(level), C.GLint(internalFormat), C.GLsizei(width), C.GLsizei(height), 0, C.GLenum(format), C.GLenum(ty), nil)
}
func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
C.gio_glTexStorage2D(C.GLenum(target), C.GLsizei(levels), C.GLenum(internalFormat), C.GLsizei(width), C.GLsizei(height))
}
func (f *Functions) TexSubImage2D(target Enum, level int, x int, y int, width int, height int, format Enum, ty Enum, data []byte) {
@@ -535,6 +615,11 @@ func (f *Functions) UseProgram(p Program) {
C.glUseProgram(C.GLuint(p.V))
}
func (f *Functions) UnmapBuffer(target Enum) bool {
r := C.gio_glUnmapBuffer(C.GLenum(target))
return r == C.GL_TRUE
}
func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride int, offset int) {
var n C.GLboolean = C.GL_FALSE
if normalized {
+31 -13
View File
@@ -27,6 +27,7 @@ var (
_glBlendEquation = LibGLESv2.NewProc("glBlendEquation")
_glBlendFunc = LibGLESv2.NewProc("glBlendFunc")
_glBufferData = LibGLESv2.NewProc("glBufferData")
_glBufferSubData = LibGLESv2.NewProc("glBufferSubData")
_glCheckFramebufferStatus = LibGLESv2.NewProc("glCheckFramebufferStatus")
_glClear = LibGLESv2.NewProc("glClear")
_glClearColor = LibGLESv2.NewProc("glClearColor")
@@ -78,6 +79,7 @@ var (
_glScissor = LibGLESv2.NewProc("glScissor")
_glShaderSource = LibGLESv2.NewProc("glShaderSource")
_glTexImage2D = LibGLESv2.NewProc("glTexImage2D")
_glTexStorage2D = LibGLESv2.NewProc("glTexStorage2D")
_glTexSubImage2D = LibGLESv2.NewProc("glTexSubImage2D")
_glTexParameteri = LibGLESv2.NewProc("glTexParameteri")
_glUniformBlockBinding = LibGLESv2.NewProc("glUniformBlockBinding")
@@ -132,6 +134,9 @@ func (c *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
func (c *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0)
}
func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
panic("not implemented")
}
func (c *Functions) BindTexture(target Enum, t Texture) {
syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0)
}
@@ -141,12 +146,16 @@ func (c *Functions) BlendEquation(mode Enum) {
func (c *Functions) BlendFunc(sfactor, dfactor Enum) {
syscall.Syscall(_glBlendFunc.Addr(), 2, uintptr(sfactor), uintptr(dfactor), 0)
}
func (c *Functions) BufferData(target Enum, src []byte, usage Enum) {
if n := len(src); n == 0 {
syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), 0, 0, uintptr(usage), 0, 0)
} else {
func (f *Functions) BlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int, mask Enum, filter Enum) {
panic("not implemented")
}
func (c *Functions) BufferData(target Enum, size int, usage Enum) {
syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(size), 0, uintptr(usage), 0, 0)
}
func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
if n := len(src); n > 0 {
s0 := &src[0]
syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(n), uintptr(unsafe.Pointer(s0)), uintptr(usage), 0, 0)
syscall.Syscall6(_glBufferSubData.Addr(), 4, uintptr(target), uintptr(offset), uintptr(n), uintptr(unsafe.Pointer(s0)), 0, 0)
issue34474KeepAlive(s0)
}
}
@@ -242,6 +251,9 @@ func (c *Functions) DrawArrays(mode Enum, first, count int) {
func (c *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0)
}
func (f *Functions) DispatchCompute(x, y, z int) {
panic("not implemented")
}
func (c *Functions) Enable(cap Enum) {
syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0)
}
@@ -335,6 +347,12 @@ func (c *Functions) LinkProgram(p Program) {
func (c *Functions) PixelStorei(pname Enum, param int32) {
syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0)
}
func (f *Functions) MemoryBarrier(barriers Enum) {
panic("not implemented")
}
func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
panic("not implemented")
}
func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
d0 := &data[0]
syscall.Syscall9(_glReadPixels.Addr(), 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)), 0, 0)
@@ -352,14 +370,11 @@ func (c *Functions) ShaderSource(s Shader, src string) {
syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0)
issue34474KeepAlive(psrc)
}
func (c *Functions) TexImage2D(target Enum, level int, internalFormat int, width, height int, format, ty Enum, data []byte) {
if len(data) == 0 {
syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0)
} else {
d0 := &data[0]
syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)))
issue34474KeepAlive(d0)
}
func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) {
syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0)
}
func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
syscall.Syscall6(_glTexStorage2D.Addr(), 5, uintptr(target), uintptr(levels), uintptr(internalFormat), uintptr(width), uintptr(height), 0)
}
func (c *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
d0 := &data[0]
@@ -390,6 +405,9 @@ func (c *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
func (c *Functions) UseProgram(p Program) {
syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0)
}
func (f *Functions) UnmapBuffer(target Enum) bool {
panic("not implemented")
}
func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) {
var norm uintptr
if normalized {
+20
View File
@@ -37,6 +37,26 @@ func CreateProgram(ctx *Functions, vsSrc, fsSrc string, attribs []string) (Progr
return prog, nil
}
func CreateComputeProgram(ctx *Functions, src string) (Program, error) {
cs, err := createShader(ctx, COMPUTE_SHADER, src)
if err != nil {
return Program{}, err
}
defer ctx.DeleteShader(cs)
prog := ctx.CreateProgram()
if !prog.Valid() {
return Program{}, errors.New("glCreateProgram failed")
}
ctx.AttachShader(prog, cs)
ctx.LinkProgram(prog)
if ctx.GetProgrami(prog, LINK_STATUS) == 0 {
log := ctx.GetProgramInfoLog(prog)
ctx.DeleteProgram(prog)
return Program{}, fmt.Errorf("program link failed: %s", strings.TrimSpace(log))
}
return prog, nil
}
func createShader(ctx *Functions, typ Enum, src string) (Shader, error) {
sh := ctx.CreateShader(typ)
if !sh.Valid() {