diff --git a/gpu/gpu.go b/gpu/gpu.go index 0e44f2fc..9616e8af 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -365,6 +365,8 @@ func New(api API) (GPU, error) { if err != nil { return nil, err } + d.BeginFrame(false, image.Point{}) + defer d.EndFrame() forceCompute := os.Getenv("GIORENDERER") == "forcecompute" feats := d.Caps().Features switch { diff --git a/gpu/internal/opengl/opengl.go b/gpu/internal/opengl/opengl.go index 969c5e71..a8305216 100644 --- a/gpu/internal/opengl/opengl.go +++ b/gpu/internal/opengl/opengl.go @@ -12,15 +12,16 @@ import ( "gioui.org/gpu/internal/driver" "gioui.org/internal/gl" - "gioui.org/internal/srgb" ) // Backend implements driver.Device. type Backend struct { funcs *gl.Functions - clear bool - state glstate + clear bool + glstate glState + state state + savedState glState glver [2]int gles bool @@ -33,27 +34,61 @@ type Backend struct { alphaTriple textureTriple srgbaTriple textureTriple - sRGBFBO *srgb.FBO - enabledSRGB bool - defFBO gl.Framebuffer + sRGBFBO *SRGBFBO - // defVertArray is bound during a frame. We don't need it, but + // vertArray is bound during a frame. We don't need it, but // core desktop OpenGL profile 3.3 requires some array bound. - defVertArray gl.VertexArray + vertArray gl.VertexArray } // State tracking. -type glstate struct { - // nattr is the current number of enabled vertex arrays. - nattr int - prog *gpuProgram - texUnits [4]*gpuTexture - layout *gpuInputLayout - buffer bufferBinding +type glState struct { + drawFBO gl.Framebuffer + readFBO gl.Framebuffer + renderBuf gl.Renderbuffer + vertAttribs [5]struct { + obj gl.Buffer + enabled bool + size int + typ gl.Enum + normalized bool + stride int + offset uintptr + } + prog gl.Program + texUnits struct { + active gl.Enum + binds [2]gl.Texture + } + arrayBuf gl.Buffer + elemBuf gl.Buffer + uniBuf gl.Buffer + uniBufs [2]gl.Buffer + storeBuf gl.Buffer + storeBufs [4]gl.Buffer + vertArray gl.VertexArray + depthMask bool + depthFunc gl.Enum + srgb bool + blend struct { + enable bool + srcRGB, dstRGB gl.Enum + srcA, dstA gl.Enum + } + depthTest bool + clearColor [4]float32 + clearDepth float32 + viewport [4]int +} + +type state struct { + prog *gpuProgram + layout *gpuInputLayout + buffer bufferBinding } type bufferBinding struct { - buf *gpuBuffer + obj gl.Buffer offset int stride int } @@ -94,7 +129,6 @@ type gpuBuffer struct { type gpuProgram struct { backend *Backend obj gl.Program - nattr int vertUniforms uniformsTracker fragUniforms uniformsTracker storage [storageBindings]*gpuBuffer @@ -179,21 +213,21 @@ func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) { func (b *Backend) BeginFrame(clear bool, viewport image.Point) driver.Framebuffer { b.clear = clear - // Assume GL state is reset between frames. - b.state = glstate{} - b.defFBO = gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING)) - renderFBO := b.defFBO + b.glstate = b.queryState() + b.savedState = b.glstate + b.state = state{} + renderFBO := b.glstate.drawFBO if b.gles { // If the output framebuffer is not in the sRGB colorspace already, emulate it. var fbEncoding int - if !b.defFBO.Valid() { + if !renderFBO.Valid() { fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) } else { fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) } - if fbEncoding == gl.LINEAR { + if fbEncoding == gl.LINEAR && viewport != (image.Point{}) { if b.sRGBFBO == nil { - sfbo, err := srgb.New(b.funcs) + sfbo, err := NewSRGBFBO(b.funcs, &b.glstate) if err != nil { panic(err) } @@ -205,16 +239,13 @@ func (b *Backend) BeginFrame(clear bool, viewport image.Point) driver.Framebuffe renderFBO = b.sRGBFBO.Framebuffer() } } else { - b.enabledSRGB = !b.funcs.IsEnabled(gl.FRAMEBUFFER_SRGB) - if b.enabledSRGB { - b.funcs.Enable(gl.FRAMEBUFFER_SRGB) + b.glstate.set(b.funcs, gl.FRAMEBUFFER_SRGB, true) + if !b.vertArray.Valid() { + b.vertArray = b.funcs.CreateVertexArray() } - if !b.defVertArray.Valid() { - b.defVertArray = b.funcs.CreateVertexArray() - } - b.funcs.BindVertexArray(b.defVertArray) + b.glstate.bindVertexArray(b.funcs, b.vertArray) } - b.funcs.BindFramebuffer(gl.FRAMEBUFFER, renderFBO) + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO) if b.sRGBFBO != nil && !clear { b.Clear(0, 0, 0, 0) } @@ -222,9 +253,8 @@ func (b *Backend) BeginFrame(clear bool, viewport image.Point) driver.Framebuffe } func (b *Backend) EndFrame() { - b.funcs.ActiveTexture(gl.TEXTURE0) if b.sRGBFBO != nil { - b.funcs.BindFramebuffer(gl.FRAMEBUFFER, b.defFBO) + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, b.savedState.drawFBO) if b.clear { b.SetBlend(false) } else { @@ -233,15 +263,376 @@ func (b *Backend) EndFrame() { } b.sRGBFBO.Blit() } - b.SetBlend(false) - b.funcs.BindFramebuffer(gl.FRAMEBUFFER, b.defFBO) - if b.enabledSRGB { - b.funcs.Disable(gl.FRAMEBUFFER_SRGB) - } + b.restoreState(b.savedState) // For single-buffered framebuffers such as on macOS. b.funcs.Flush() } +func (b *Backend) queryState() glState { + s := glState{ + prog: gl.Program(b.funcs.GetBinding(gl.CURRENT_PROGRAM)), + arrayBuf: gl.Buffer(b.funcs.GetBinding(gl.ARRAY_BUFFER_BINDING)), + elemBuf: gl.Buffer(b.funcs.GetBinding(gl.ELEMENT_ARRAY_BUFFER_BINDING)), + drawFBO: gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING)), + depthMask: b.funcs.GetInteger(gl.DEPTH_WRITEMASK) != gl.FALSE, + depthTest: b.funcs.IsEnabled(gl.DEPTH_TEST), + depthFunc: gl.Enum(b.funcs.GetInteger(gl.DEPTH_FUNC)), + clearDepth: b.funcs.GetFloat(gl.DEPTH_CLEAR_VALUE), + clearColor: b.funcs.GetFloat4(gl.COLOR_CLEAR_VALUE), + viewport: b.funcs.GetInteger4(gl.VIEWPORT), + } + s.blend.enable = b.funcs.IsEnabled(gl.BLEND) + s.blend.srcRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_RGB)) + s.blend.dstRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_RGB)) + s.blend.srcA = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_ALPHA)) + s.blend.dstA = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_ALPHA)) + s.texUnits.active = gl.Enum(b.funcs.GetInteger(gl.ACTIVE_TEXTURE)) + if !b.gles { + s.srgb = b.funcs.IsEnabled(gl.FRAMEBUFFER_SRGB) + } + if !b.gles || b.glver[0] >= 3 { + s.vertArray = gl.VertexArray(b.funcs.GetBinding(gl.VERTEX_ARRAY_BINDING)) + s.readFBO = gl.Framebuffer(b.funcs.GetBinding(gl.READ_FRAMEBUFFER_BINDING)) + s.uniBuf = gl.Buffer(b.funcs.GetBinding(gl.UNIFORM_BUFFER_BINDING)) + for i := range s.uniBufs { + s.uniBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.UNIFORM_BUFFER_BINDING, i)) + } + } + if b.gles && (b.glver[0] > 3 || (b.glver[0] == 3 && b.glver[1] >= 1)) { + s.storeBuf = gl.Buffer(b.funcs.GetBinding(gl.SHADER_STORAGE_BUFFER_BINDING)) + for i := range s.storeBufs { + s.storeBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.SHADER_STORAGE_BUFFER_BINDING, i)) + } + } + for i := range s.texUnits.binds { + s.activeTexture(b.funcs, gl.TEXTURE0+gl.Enum(i)) + s.texUnits.binds[i] = gl.Texture(b.funcs.GetBinding(gl.TEXTURE_BINDING_2D)) + } + for i := range s.vertAttribs { + a := &s.vertAttribs[i] + a.enabled = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED) != gl.FALSE + a.obj = gl.Buffer(b.funcs.GetVertexAttribBinding(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED)) + a.size = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_SIZE) + a.typ = gl.Enum(b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_TYPE)) + a.normalized = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED) != gl.FALSE + a.stride = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_STRIDE) + a.offset = b.funcs.GetVertexAttribPointer(i, gl.VERTEX_ATTRIB_ARRAY_POINTER) + } + return s +} + +func (b *Backend) restoreState(dst glState) { + src := b.glstate + f := b.funcs + for i, unit := range dst.texUnits.binds { + src.bindTexture(f, i, unit) + } + src.activeTexture(f, dst.texUnits.active) + src.bindFramebuffer(f, gl.FRAMEBUFFER, dst.drawFBO) + src.bindFramebuffer(f, gl.READ_FRAMEBUFFER, dst.readFBO) + src.set(f, gl.BLEND, dst.blend.enable) + bf := dst.blend + src.setBlendFuncSeparate(f, bf.srcRGB, bf.dstRGB, bf.srcA, bf.dstA) + src.set(f, gl.DEPTH_TEST, dst.depthTest) + src.setDepthFunc(f, dst.depthFunc) + src.set(f, gl.FRAMEBUFFER_SRGB, dst.srgb) + src.bindVertexArray(f, dst.vertArray) + src.useProgram(f, dst.prog) + src.bindBuffer(f, gl.ELEMENT_ARRAY_BUFFER, dst.elemBuf) + for i, b := range dst.uniBufs { + src.bindBufferBase(f, gl.UNIFORM_BUFFER, i, b) + } + src.bindBuffer(f, gl.UNIFORM_BUFFER, dst.uniBuf) + for i, b := range dst.storeBufs { + src.bindBufferBase(f, gl.SHADER_STORAGE_BUFFER, i, b) + } + src.bindBuffer(f, gl.SHADER_STORAGE_BUFFER, dst.storeBuf) + src.setDepthMask(f, dst.depthMask) + src.setClearDepth(f, dst.clearDepth) + col := dst.clearColor + src.setClearColor(f, col[0], col[1], col[2], col[3]) + for i, attr := range dst.vertAttribs { + src.setVertexAttribArray(f, i, attr.enabled) + src.vertexAttribPointer(f, attr.obj, i, attr.size, attr.typ, attr.normalized, attr.stride, int(attr.offset)) + } + src.bindBuffer(f, gl.ARRAY_BUFFER, dst.arrayBuf) + v := dst.viewport + src.setViewport(f, v[0], v[1], v[2], v[3]) +} + +func (s *glState) setVertexAttribArray(f *gl.Functions, idx int, enabled bool) { + a := &s.vertAttribs[idx] + if enabled != a.enabled { + if enabled { + f.EnableVertexAttribArray(gl.Attrib(idx)) + } else { + f.DisableVertexAttribArray(gl.Attrib(idx)) + } + a.enabled = enabled + } +} + +func (s *glState) vertexAttribPointer(f *gl.Functions, buf gl.Buffer, idx, size int, typ gl.Enum, normalized bool, stride, offset int) { + s.bindBuffer(f, gl.ARRAY_BUFFER, buf) + a := &s.vertAttribs[idx] + a.obj = buf + a.size = size + a.typ = typ + a.normalized = normalized + a.stride = stride + a.offset = uintptr(offset) + f.VertexAttribPointer(gl.Attrib(idx), a.size, a.typ, a.normalized, a.stride, int(a.offset)) +} + +func (s *glState) activeTexture(f *gl.Functions, unit gl.Enum) { + if unit != s.texUnits.active { + f.ActiveTexture(unit) + s.texUnits.active = unit + } +} + +func (s *glState) bindRenderbuffer(f *gl.Functions, target gl.Enum, r gl.Renderbuffer) { + if !r.Equal(s.renderBuf) { + f.BindRenderbuffer(gl.RENDERBUFFER, r) + s.renderBuf = r + } +} + +func (s *glState) bindTexture(f *gl.Functions, unit int, t gl.Texture) { + s.activeTexture(f, gl.TEXTURE0+gl.Enum(unit)) + if !t.Equal(s.texUnits.binds[unit]) { + f.BindTexture(gl.TEXTURE_2D, t) + s.texUnits.binds[unit] = t + } +} + +func (s *glState) bindVertexArray(f *gl.Functions, a gl.VertexArray) { + if !a.Equal(s.vertArray) { + f.BindVertexArray(a) + s.vertArray = a + } +} + +func (s *glState) deleteRenderbuffer(f *gl.Functions, r gl.Renderbuffer) { + f.DeleteRenderbuffer(r) + if r.Equal(s.renderBuf) { + s.renderBuf = gl.Renderbuffer{} + } +} + +func (s *glState) deleteFramebuffer(f *gl.Functions, fbo gl.Framebuffer) { + f.DeleteFramebuffer(fbo) + if fbo.Equal(s.drawFBO) { + s.drawFBO = gl.Framebuffer{} + } + if fbo.Equal(s.readFBO) { + s.readFBO = gl.Framebuffer{} + } +} + +func (s *glState) deleteBuffer(f *gl.Functions, b gl.Buffer) { + f.DeleteBuffer(b) + if b.Equal(s.arrayBuf) { + s.arrayBuf = gl.Buffer{} + } + if b.Equal(s.elemBuf) { + s.elemBuf = gl.Buffer{} + } + if b.Equal(s.uniBuf) { + s.uniBuf = gl.Buffer{} + } + if b.Equal(s.storeBuf) { + s.uniBuf = gl.Buffer{} + } + for i, b2 := range s.storeBufs { + if b.Equal(b2) { + s.storeBufs[i] = gl.Buffer{} + } + } + for i, b2 := range s.uniBufs { + if b.Equal(b2) { + s.uniBufs[i] = gl.Buffer{} + } + } +} + +func (s *glState) deleteProgram(f *gl.Functions, p gl.Program) { + f.DeleteProgram(p) + if p.Equal(s.prog) { + s.prog = gl.Program{} + } +} + +func (s *glState) deleteVertexArray(f *gl.Functions, a gl.VertexArray) { + f.DeleteVertexArray(a) + if a.Equal(s.vertArray) { + s.vertArray = gl.VertexArray{} + } +} + +func (s *glState) deleteTexture(f *gl.Functions, t gl.Texture) { + f.DeleteTexture(t) + binds := &s.texUnits.binds + for i, obj := range binds { + if t.Equal(obj) { + binds[i] = gl.Texture{} + } + } +} + +func (s *glState) useProgram(f *gl.Functions, p gl.Program) { + if !p.Equal(s.prog) { + f.UseProgram(p) + s.prog = p + } +} + +func (s *glState) bindFramebuffer(f *gl.Functions, target gl.Enum, fbo gl.Framebuffer) { + switch target { + case gl.FRAMEBUFFER: + if fbo.Equal(s.drawFBO) && fbo.Equal(s.readFBO) { + return + } + s.drawFBO = fbo + s.readFBO = fbo + case gl.READ_FRAMEBUFFER: + if fbo.Equal(s.readFBO) { + return + } + s.readFBO = fbo + case gl.DRAW_FRAMEBUFFER: + if fbo.Equal(s.drawFBO) { + return + } + s.drawFBO = fbo + default: + panic("unknown target") + } + f.BindFramebuffer(target, fbo) +} + +func (s *glState) bindBufferBase(f *gl.Functions, target gl.Enum, idx int, buf gl.Buffer) { + switch target { + case gl.UNIFORM_BUFFER: + if buf.Equal(s.uniBuf) && buf.Equal(s.uniBufs[idx]) { + return + } + s.uniBuf = buf + s.uniBufs[idx] = buf + case gl.SHADER_STORAGE_BUFFER: + if buf.Equal(s.storeBuf) && buf.Equal(s.storeBufs[idx]) { + return + } + s.storeBuf = buf + s.storeBufs[idx] = buf + default: + panic("unknown buffer target") + } + f.BindBufferBase(target, idx, buf) +} + +func (s *glState) bindBuffer(f *gl.Functions, target gl.Enum, buf gl.Buffer) { + switch target { + case gl.ARRAY_BUFFER: + if buf.Equal(s.arrayBuf) { + return + } + s.arrayBuf = buf + case gl.ELEMENT_ARRAY_BUFFER: + if buf.Equal(s.elemBuf) { + return + } + s.elemBuf = buf + case gl.UNIFORM_BUFFER: + if buf.Equal(s.uniBuf) { + return + } + s.uniBuf = buf + case gl.SHADER_STORAGE_BUFFER: + if buf.Equal(s.storeBuf) { + return + } + s.storeBuf = buf + default: + panic("unknown buffer target") + } + f.BindBuffer(target, buf) +} + +func (s *glState) setClearDepth(f *gl.Functions, d float32) { + if d != s.clearDepth { + f.ClearDepthf(d) + s.clearDepth = d + } +} + +func (s *glState) setClearColor(f *gl.Functions, r, g, b, a float32) { + col := [4]float32{r, g, b, a} + if col != s.clearColor { + f.ClearColor(r, g, b, a) + s.clearColor = col + } +} + +func (s *glState) setViewport(f *gl.Functions, x, y, width, height int) { + view := [4]int{x, y, width, height} + if view != s.viewport { + f.Viewport(x, y, width, height) + s.viewport = view + } +} + +func (s *glState) setDepthFunc(f *gl.Functions, df gl.Enum) { + if df != s.depthFunc { + f.DepthFunc(df) + s.depthFunc = df + } +} + +func (s *glState) setBlendFuncSeparate(f *gl.Functions, srcRGB, dstRGB, srcA, dstA gl.Enum) { + if srcRGB != s.blend.srcRGB || dstRGB != s.blend.dstRGB || srcA != s.blend.srcA || dstA != s.blend.dstA { + s.blend.srcRGB = srcRGB + s.blend.dstRGB = dstRGB + s.blend.srcA = srcA + s.blend.dstA = dstA + f.BlendFuncSeparate(srcA, dstA, srcA, dstA) + } +} + +func (s *glState) setDepthMask(f *gl.Functions, enable bool) { + if enable != s.depthMask { + f.DepthMask(enable) + s.depthMask = enable + } +} + +func (s *glState) set(f *gl.Functions, target gl.Enum, enable bool) { + switch target { + case gl.FRAMEBUFFER_SRGB: + if s.srgb == enable { + return + } + s.srgb = enable + case gl.BLEND: + if enable == s.blend.enable { + return + } + s.blend.enable = enable + case gl.DEPTH_TEST: + if enable == s.depthTest { + return + } + s.depthTest = enable + default: + panic("unknown enable") + } + if enable { + f.Enable(target) + } else { + f.Disable(target) + } +} + func (b *Backend) Caps() driver.Caps { return b.feats } @@ -277,7 +668,7 @@ func (b *Backend) NewFramebuffer(tex driver.Texture, depthBits int) (driver.Fram size = gl.DEPTH_COMPONENT24 } depthBuf := b.funcs.CreateRenderbuffer() - b.funcs.BindRenderbuffer(gl.RENDERBUFFER, depthBuf) + b.glstate.bindRenderbuffer(b.funcs, gl.RENDERBUFFER, depthBuf) b.funcs.RenderbufferStorage(gl.RENDERBUFFER, size, gltex.width, gltex.height) b.funcs.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuf) fbo.depthBuf = depthBuf @@ -345,7 +736,7 @@ func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, return nil, err } firstBinding := firstBufferType(typ) - b.funcs.BindBuffer(firstBinding, buf.obj) + b.glstate.bindBuffer(b.funcs, firstBinding, buf.obj) b.funcs.BufferData(firstBinding, size, gl.DYNAMIC_DRAW) } return buf, nil @@ -356,7 +747,7 @@ func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (dri 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.glstate.bindBuffer(b.funcs, firstBinding, buf.obj) b.funcs.BufferData(firstBinding, len(data), gl.STATIC_DRAW) buf.Upload(data) buf.immutable = true @@ -378,8 +769,8 @@ func (b *Backend) Release() { if b.sRGBFBO != nil { b.sRGBFBO.Release() } - if b.defVertArray.Valid() { - b.funcs.DeleteVertexArray(b.defVertArray) + if b.vertArray.Valid() { + b.glstate.deleteVertexArray(b.funcs, b.vertArray) } *b = Backend{} } @@ -392,7 +783,7 @@ 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(gl.SHADER_STORAGE_BUFFER, binding, buf.obj) + b.glstate.bindBufferBase(b.funcs, gl.SHADER_STORAGE_BUFFER, binding, buf.obj) } } } @@ -420,43 +811,18 @@ func (b *Backend) BindImageTexture(unit int, tex driver.Texture, access driver.A 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(gl.TEXTURE0 + gl.Enum(unit)) - b.funcs.BindTexture(gl.TEXTURE_2D, t.obj) - b.state.texUnits[unit] = t - } -} - func (b *Backend) useProgram(p *gpuProgram) { - if b.state.prog != p { - p.backend.funcs.UseProgram(p.obj) - b.state.prog = p - } -} - -func (b *Backend) enableVertexArrays(n int) { - // Enable needed arrays. - for i := b.state.nattr; i < n; i++ { - b.funcs.EnableVertexAttribArray(gl.Attrib(i)) - } - // Disable extra arrays. - for i := n; i < b.state.nattr; i++ { - b.funcs.DisableVertexAttribArray(gl.Attrib(i)) - } - b.state.nattr = n + b.glstate.useProgram(b.funcs, p.obj) + b.state.prog = p } func (b *Backend) SetDepthTest(enable bool) { - if enable { - b.funcs.Enable(gl.DEPTH_TEST) - } else { - b.funcs.Disable(gl.DEPTH_TEST) - } + b.glstate.set(b.funcs, gl.DEPTH_TEST, enable) } func (b *Backend) BlendFunc(sfactor, dfactor driver.BlendFactor) { - b.funcs.BlendFunc(toGLBlendFactor(sfactor), toGLBlendFactor(dfactor)) + src, dst := toGLBlendFactor(sfactor), toGLBlendFactor(dfactor) + b.glstate.setBlendFuncSeparate(b.funcs, src, dst, src, dst) } func toGLBlendFactor(f driver.BlendFactor) gl.Enum { @@ -475,15 +841,11 @@ func toGLBlendFactor(f driver.BlendFactor) gl.Enum { } func (b *Backend) DepthMask(mask bool) { - b.funcs.DepthMask(mask) + b.glstate.setDepthMask(b.funcs, mask) } func (b *Backend) SetBlend(enable bool) { - if enable { - b.funcs.Enable(gl.BLEND) - } else { - b.funcs.Disable(gl.BLEND) - } + b.glstate.set(b.funcs, gl.BLEND, enable) } func (b *Backend) DrawElements(mode driver.DrawMode, off, count int) { @@ -499,14 +861,12 @@ func (b *Backend) DrawArrays(mode driver.DrawMode, off, count int) { } func (b *Backend) prepareDraw() { - nattr := b.state.prog.nattr - b.enableVertexArrays(nattr) - if nattr > 0 { - b.setupVertexArrays() - } - if p := b.state.prog; p != nil { - p.updateUniforms() + p := b.state.prog + if p == nil { + return } + b.setupVertexArrays() + p.updateUniforms() } func toGLDrawMode(mode driver.DrawMode) gl.Enum { @@ -521,16 +881,16 @@ func toGLDrawMode(mode driver.DrawMode) gl.Enum { } func (b *Backend) Viewport(x, y, width, height int) { - b.funcs.Viewport(x, y, width, height) + b.glstate.setViewport(b.funcs, x, y, width, height) } func (b *Backend) Clear(colR, colG, colB, colA float32) { - b.funcs.ClearColor(colR, colG, colB, colA) + b.glstate.setClearColor(b.funcs, colR, colG, colB, colA) b.funcs.Clear(gl.COLOR_BUFFER_BIT) } func (b *Backend) ClearDepth(d float32) { - b.funcs.ClearDepthf(d) + b.glstate.setClearDepth(b.funcs, d) b.funcs.Clear(gl.DEPTH_BUFFER_BIT) } @@ -544,7 +904,7 @@ func (b *Backend) DepthFunc(f driver.DepthFunc) { default: panic("unsupported depth func") } - b.funcs.DepthFunc(glfunc) + b.glstate.setDepthFunc(b.funcs, glfunc) } func (b *Backend) NewInputLayout(vs driver.ShaderSources, layout []driver.InputDesc) (driver.InputLayout, error) { @@ -599,7 +959,6 @@ func (b *Backend) NewProgram(vertShader, fragShader driver.ShaderSources) (drive gpuProg := &gpuProgram{ backend: b, obj: p, - nattr: len(attr), } b.BindProgram(gpuProg) // Bind texture uniforms. @@ -667,10 +1026,10 @@ func (p *gpuProgram) updateUniforms() { f := p.backend.funcs if p.backend.ubo { if b := p.vertUniforms.buf; b != nil { - f.BindBufferBase(gl.UNIFORM_BUFFER, 0, b.obj) + p.backend.glstate.bindBufferBase(f, gl.UNIFORM_BUFFER, 0, b.obj) } if b := p.fragUniforms.buf; b != nil { - f.BindBufferBase(gl.UNIFORM_BUFFER, 1, b.obj) + p.backend.glstate.bindBufferBase(f, gl.UNIFORM_BUFFER, 1, b.obj) } } else { p.vertUniforms.update(f) @@ -684,7 +1043,7 @@ func (b *Backend) BindProgram(prog driver.Program) { } func (p *gpuProgram) Release() { - p.backend.funcs.DeleteProgram(p.obj) + p.backend.glstate.deleteProgram(p.backend.funcs, p.obj) } func (u *uniformsTracker) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []driver.UniformLocation) { @@ -751,7 +1110,7 @@ func (b *gpuBuffer) Upload(data []byte) { copy(b.data, data) if b.hasBuffer { firstBinding := firstBufferType(b.typ) - b.backend.funcs.BindBuffer(firstBinding, b.obj) + b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj) if len(data) == b.size { // the iOS GL implementation doesn't recognize when BufferSubData // clears the entire buffer. Tell it and avoid GPU stalls. @@ -771,7 +1130,7 @@ func (b *gpuBuffer) Download(data []byte) error { return nil } firstBinding := firstBufferType(b.typ) - b.backend.funcs.BindBuffer(firstBinding, b.obj) + b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj) bufferMap := b.backend.funcs.MapBufferRange(firstBinding, 0, len(data), gl.MAP_READ_BIT) if bufferMap == nil { return fmt.Errorf("MapBufferRange: error %#x", b.backend.funcs.GetError()) @@ -785,7 +1144,7 @@ func (b *gpuBuffer) Download(data []byte) error { func (b *gpuBuffer) Release() { if b.hasBuffer { - b.backend.funcs.DeleteBuffer(b.obj) + b.backend.glstate.deleteBuffer(b.backend.funcs, b.obj) b.hasBuffer = false } } @@ -795,7 +1154,7 @@ func (b *Backend) BindVertexBuffer(buf driver.Buffer, stride, offset int) { if gbuf.typ&driver.BufferBindingVertices == 0 { panic("not a vertex buffer") } - b.state.buffer = bufferBinding{buf: gbuf, stride: stride, offset: offset} + b.state.buffer = bufferBinding{obj: gbuf.obj, stride: stride, offset: offset} } func (b *Backend) setupVertexArrays() { @@ -803,8 +1162,9 @@ func (b *Backend) setupVertexArrays() { if layout == nil { return } + const max = len(b.glstate.vertAttribs) + var enabled [max]bool buf := b.state.buffer - b.funcs.BindBuffer(gl.ARRAY_BUFFER, buf.buf.obj) for i, inp := range layout.inputs { l := layout.layout[i] var gltyp gl.Enum @@ -816,7 +1176,11 @@ func (b *Backend) setupVertexArrays() { default: panic("unsupported data type") } - b.funcs.VertexAttribPointer(gl.Attrib(inp.Location), l.Size, gltyp, false, buf.stride, buf.offset+l.Offset) + enabled[inp.Location] = true + b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, buf.stride, buf.offset+l.Offset) + } + for i := 0; i < max; i++ { + b.glstate.setVertexAttribArray(b.funcs, i, enabled[i]) } } @@ -825,12 +1189,12 @@ func (b *Backend) BindIndexBuffer(buf driver.Buffer) { if gbuf.typ&driver.BufferBindingIndices == 0 { panic("not an index buffer") } - b.funcs.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gbuf.obj) + b.glstate.bindBuffer(b.funcs, gl.ELEMENT_ARRAY_BUFFER, gbuf.obj) } func (b *Backend) BlitFramebuffer(dst, src driver.Framebuffer, srect, drect image.Rectangle) { - b.funcs.BindFramebuffer(gl.DRAW_FRAMEBUFFER, dst.(*gpuFramebuffer).obj) - b.funcs.BindFramebuffer(gl.READ_FRAMEBUFFER, src.(*gpuFramebuffer).obj) + b.glstate.bindFramebuffer(b.funcs, gl.DRAW_FRAMEBUFFER, dst.(*gpuFramebuffer).obj) + b.glstate.bindFramebuffer(b.funcs, gl.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, @@ -849,7 +1213,7 @@ func (f *gpuFramebuffer) ReadPixels(src image.Rectangle, pixels []byte) error { } func (b *Backend) BindFramebuffer(fbo driver.Framebuffer) { - b.funcs.BindFramebuffer(gl.FRAMEBUFFER, fbo.(*gpuFramebuffer).obj) + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo.(*gpuFramebuffer).obj) } func (f *gpuFramebuffer) Invalidate() { @@ -861,9 +1225,9 @@ func (f *gpuFramebuffer) Release() { if f.foreign { panic("framebuffer not created by NewFramebuffer") } - f.backend.funcs.DeleteFramebuffer(f.obj) + f.backend.glstate.deleteFramebuffer(f.backend.funcs, f.obj) if f.hasDepth { - f.backend.funcs.DeleteRenderbuffer(f.depthBuf) + f.backend.glstate.deleteRenderbuffer(f.backend.funcs, f.depthBuf) } } @@ -879,11 +1243,11 @@ func toTexFilter(f driver.TextureFilter) int { } func (b *Backend) BindTexture(unit int, t driver.Texture) { - b.bindTexture(unit, t.(*gpuTexture)) + b.glstate.bindTexture(b.funcs, unit, t.(*gpuTexture).obj) } func (t *gpuTexture) Release() { - t.backend.funcs.DeleteTexture(t.obj) + t.backend.glstate.deleteTexture(t.backend.funcs, t.obj) } func (t *gpuTexture) Upload(offset, size image.Point, pixels []byte) { @@ -945,6 +1309,8 @@ func floatTripleFor(f *gl.Functions, ver [2]int, exts []string) (textureTriple, } tex := f.CreateTexture() defer f.DeleteTexture(tex) + defTex := gl.Texture(f.GetBinding(gl.TEXTURE_BINDING_2D)) + defer f.BindTexture(gl.TEXTURE_2D, defTex) f.BindTexture(gl.TEXTURE_2D, tex) f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) diff --git a/internal/srgb/srgb.go b/gpu/internal/opengl/srgb.go similarity index 76% rename from internal/srgb/srgb.go rename to gpu/internal/opengl/srgb.go index d72e4859..51e8eae2 100644 --- a/internal/srgb/srgb.go +++ b/gpu/internal/opengl/srgb.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT -package srgb +package opengl import ( "errors" @@ -13,11 +13,12 @@ import ( "gioui.org/internal/gl" ) -// FBO implements an intermediate sRGB FBO +// SRGBFBO implements an intermediate sRGB FBO // for gamma-correct rendering on platforms without // sRGB enabled native framebuffers. -type FBO struct { +type SRGBFBO struct { c *gl.Functions + state *glState viewport image.Point srgbBuffer gl.Framebuffer depthBuffer gl.Renderbuffer @@ -28,7 +29,7 @@ type FBO struct { gl3 bool } -func New(f *gl.Functions) (*FBO, error) { +func NewSRGBFBO(f *gl.Functions, state *glState) (*SRGBFBO, error) { var gl3 bool glVer := f.GetString(gl.VERSION) ver, _, err := gl.ParseGLVersion(glVer) @@ -43,14 +44,15 @@ func New(f *gl.Functions) (*FBO, error) { return nil, fmt.Errorf("no support for OpenGL ES 3 nor EXT_sRGB") } } - s := &FBO{ + s := &SRGBFBO{ c: f, + state: state, gl3: gl3, srgbBuffer: f.CreateFramebuffer(), colorTex: f.CreateTexture(), depthBuffer: f.CreateRenderbuffer(), } - f.BindTexture(gl.TEXTURE_2D, s.colorTex) + state.bindTexture(f, 0, s.colorTex) f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) @@ -58,17 +60,17 @@ func New(f *gl.Functions) (*FBO, error) { return s, nil } -func (s *FBO) Blit() { +func (s *SRGBFBO) Blit() { if !s.blitted { prog, err := gl.CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"}) if err != nil { panic(err) } s.prog = prog - s.c.UseProgram(prog) + s.state.useProgram(s.c, prog) s.c.Uniform1i(s.c.GetUniformLocation(prog, "tex"), 0) s.quad = s.c.CreateBuffer() - s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad) + s.state.bindBuffer(s.c, gl.ARRAY_BUFFER, s.quad) coords := byteslice.Slice([]float32{ -1, +1, 0, 1, +1, +1, 1, 1, @@ -79,27 +81,23 @@ func (s *FBO) Blit() { s.c.BufferSubData(gl.ARRAY_BUFFER, 0, coords) s.blitted = true } - s.c.UseProgram(s.prog) - s.c.BindTexture(gl.TEXTURE_2D, s.colorTex) - s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad) - s.c.VertexAttribPointer(0 /* pos */, 2, gl.FLOAT, false, 4*4, 0) - s.c.VertexAttribPointer(1 /* uv */, 2, gl.FLOAT, false, 4*4, 4*2) - s.c.EnableVertexAttribArray(0) - s.c.EnableVertexAttribArray(1) + s.state.useProgram(s.c, s.prog) + s.state.bindTexture(s.c, 0, s.colorTex) + s.state.vertexAttribPointer(s.c, s.quad, 0 /* pos */, 2, gl.FLOAT, false, 4*4, 0) + s.state.vertexAttribPointer(s.c, s.quad, 1 /* uv */, 2, gl.FLOAT, false, 4*4, 4*2) + s.state.setVertexAttribArray(s.c, 0, true) + s.state.setVertexAttribArray(s.c, 1, true) s.c.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) - s.c.BindTexture(gl.TEXTURE_2D, gl.Texture{}) - s.c.DisableVertexAttribArray(0) - s.c.DisableVertexAttribArray(1) - s.c.BindFramebuffer(gl.FRAMEBUFFER, s.srgbBuffer) + s.state.bindFramebuffer(s.c, gl.FRAMEBUFFER, s.srgbBuffer) s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0) s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT) } -func (s *FBO) Framebuffer() gl.Framebuffer { +func (s *SRGBFBO) Framebuffer() gl.Framebuffer { return s.srgbBuffer } -func (s *FBO) Refresh(viewport image.Point) error { +func (s *SRGBFBO) Refresh(viewport image.Point) error { if viewport.X == 0 || viewport.Y == 0 { return errors.New("srgb: zero-sized framebuffer") } @@ -107,17 +105,15 @@ func (s *FBO) Refresh(viewport image.Point) error { return nil } s.viewport = viewport - s.c.BindTexture(gl.TEXTURE_2D, s.colorTex) + s.state.bindTexture(s.c, 0, s.colorTex) if s.gl3 { s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, viewport.X, viewport.Y, gl.RGBA, gl.UNSIGNED_BYTE) } else /* EXT_sRGB */ { s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB_ALPHA_EXT, viewport.X, viewport.Y, gl.SRGB_ALPHA_EXT, gl.UNSIGNED_BYTE) } - currentRB := gl.Renderbuffer(s.c.GetBinding(gl.RENDERBUFFER_BINDING)) - s.c.BindRenderbuffer(gl.RENDERBUFFER, s.depthBuffer) + s.state.bindRenderbuffer(s.c, gl.RENDERBUFFER, s.depthBuffer) s.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, viewport.X, viewport.Y) - s.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB) - s.c.BindFramebuffer(gl.FRAMEBUFFER, s.srgbBuffer) + s.state.bindFramebuffer(s.c, gl.FRAMEBUFFER, s.srgbBuffer) s.c.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, s.colorTex, 0) s.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, s.depthBuffer) if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { @@ -128,7 +124,7 @@ func (s *FBO) Refresh(viewport image.Point) error { // With macOS Safari, rendering to and then reading from a SRGB8_ALPHA8 // texture result in twice gamma corrected colors. Using a plain RGBA // texture seems to work. - s.c.ClearColor(.5, .5, .5, 1.0) + s.state.setClearColor(s.c, .5, .5, .5, 1.0) s.c.Clear(gl.COLOR_BUFFER_BIT) var pixel [4]byte s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:]) @@ -143,13 +139,13 @@ func (s *FBO) Refresh(viewport image.Point) error { return nil } -func (s *FBO) Release() { - s.c.DeleteFramebuffer(s.srgbBuffer) - s.c.DeleteTexture(s.colorTex) - s.c.DeleteRenderbuffer(s.depthBuffer) +func (s *SRGBFBO) Release() { + s.state.deleteFramebuffer(s.c, s.srgbBuffer) + s.state.deleteTexture(s.c, s.colorTex) + s.state.deleteRenderbuffer(s.c, s.depthBuffer) if s.blitted { - s.c.DeleteBuffer(s.quad) - s.c.DeleteProgram(s.prog) + s.state.deleteBuffer(s.c, s.quad) + s.state.deleteProgram(s.c, s.prog) } s.c = nil } diff --git a/internal/gl/gl.go b/internal/gl/gl.go index 53f31238..6b4a3dd7 100644 --- a/internal/gl/gl.go +++ b/internal/gl/gl.go @@ -8,26 +8,38 @@ type ( ) const ( + ACTIVE_TEXTURE = 0x84E0 ALL_BARRIER_BITS = 0xffffffff ARRAY_BUFFER = 0x8892 + ARRAY_BUFFER_BINDING = 0x8894 BACK = 0x0405 BLEND = 0xbe2 + BLEND_DST_RGB = 0x80C8 + BLEND_SRC_RGB = 0x80C9 + BLEND_DST_ALPHA = 0x80CA + BLEND_SRC_ALPHA = 0x80CB CLAMP_TO_EDGE = 0x812f COLOR_ATTACHMENT0 = 0x8ce0 COLOR_BUFFER_BIT = 0x4000 + COLOR_CLEAR_VALUE = 0x0C22 COMPILE_STATUS = 0x8b81 COMPUTE_SHADER = 0x91B9 - DEPTH_BUFFER_BIT = 0x100 + CURRENT_PROGRAM = 0x8B8D DEPTH_ATTACHMENT = 0x8d00 + DEPTH_BUFFER_BIT = 0x100 + DEPTH_CLEAR_VALUE = 0x0B73 DEPTH_COMPONENT16 = 0x81a5 DEPTH_COMPONENT24 = 0x81A6 DEPTH_COMPONENT32F = 0x8CAC + DEPTH_FUNC = 0x0B74 DEPTH_TEST = 0xb71 + DEPTH_WRITEMASK = 0x0B72 DRAW_FRAMEBUFFER = 0x8CA9 DST_COLOR = 0x306 DYNAMIC_DRAW = 0x88E8 DYNAMIC_READ = 0x88E9 ELEMENT_ARRAY_BUFFER = 0x8893 + ELEMENT_ARRAY_BUFFER_BINDING = 0x8895 EXTENSIONS = 0x1f03 FALSE = 0 FLOAT = 0x1406 @@ -59,6 +71,7 @@ const ( R16F = 0x822d R8 = 0x8229 READ_FRAMEBUFFER = 0x8ca8 + READ_FRAMEBUFFER_BINDING = 0x8CAA READ_ONLY = 0x88B8 READ_WRITE = 0x88BA RED = 0x1903 @@ -71,6 +84,7 @@ const ( RGBA = 0x1908 RGBA8 = 0x8058 SHADER_STORAGE_BUFFER = 0x90D2 + SHADER_STORAGE_BUFFER_BINDING = 0x90D3 SHORT = 0x1402 SRGB = 0x8c40 SRGB_ALPHA_EXT = 0x8c42 @@ -79,6 +93,7 @@ const ( STATIC_DRAW = 0x88e4 STENCIL_BUFFER_BIT = 0x00000400 TEXTURE_2D = 0xde1 + TEXTURE_BINDING_2D = 0x8069 TEXTURE_MAG_FILTER = 0x2800 TEXTURE_MIN_FILTER = 0x2801 TEXTURE_WRAP_S = 0x2802 @@ -89,11 +104,21 @@ const ( TRIANGLES = 0x4 TRUE = 1 UNIFORM_BUFFER = 0x8A11 + UNIFORM_BUFFER_BINDING = 0x8A28 UNPACK_ALIGNMENT = 0xcf5 UNSIGNED_BYTE = 0x1401 UNSIGNED_SHORT = 0x1403 + VIEWPORT = 0x0BA2 VERSION = 0x1f02 + VERTEX_ARRAY_BINDING = 0x85B5 VERTEX_SHADER = 0x8b31 + VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F + VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622 + VERTEX_ATTRIB_ARRAY_POINTER = 0x8645 + VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A + VERTEX_ATTRIB_ARRAY_SIZE = 0x8623 + VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624 + VERTEX_ATTRIB_ARRAY_TYPE = 0x8625 WRITE_ONLY = 0x88B9 ZERO = 0x0 diff --git a/internal/gl/gl_js.go b/internal/gl/gl_js.go index 2f6c9261..1ccec79f 100644 --- a/internal/gl/gl_js.go +++ b/internal/gl/gl_js.go @@ -101,8 +101,8 @@ func (f *Functions) BindVertexArray(a VertexArray) { func (f *Functions) BlendEquation(mode Enum) { f.Ctx.Call("blendEquation", int(mode)) } -func (f *Functions) BlendFunc(sfactor, dfactor Enum) { - f.Ctx.Call("blendFunc", int(sfactor), int(dfactor)) +func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { + f.Ctx.Call("blendFunc", int(srcRGB), int(dstRGB), int(srcA), int(dstA)) } func (f *Functions) BlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int, mask Enum, filter Enum) { panic("not implemented") @@ -241,11 +241,41 @@ func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname return paramVal(f.Ctx.Call("getFramebufferAttachmentParameter", int(target), int(attachment), int(pname))) } func (f *Functions) GetBinding(pname Enum) Object { - return Object(f.Ctx.Call("getParameter", int(pname))) + obj := f.Ctx.Call("getParameter", int(pname)) + if !obj.Truthy() { + return Object{} + } + return Object(obj) +} +func (f *Functions) GetBindingi(pname Enum, idx int) Object { + obj := f.Ctx.Call("getIndexedParameter", int(pname), idx) + if !obj.Truthy() { + return Object{} + } + return Object(obj) } func (f *Functions) GetInteger(pname Enum) int { return paramVal(f.Ctx.Call("getParameter", int(pname))) } +func (f *Functions) GetFloat(pname Enum) float32 { + return float32(f.Ctx.Call("getParameter", int(pname)).Float()) +} +func (f *Functions) GetInteger4(pname Enum) [4]int { + arr := f.Ctx.Call("getParameter", int(pname)) + var res [4]int + for i := range res { + res[i] = arr.Index(i).Int() + } + return res +} +func (f *Functions) GetFloat4(pname Enum) [4]float32 { + arr := f.Ctx.Call("getParameter", int(pname)) + var res [4]float32 + for i := range res { + res[i] = float32(arr.Index(i).Float()) + } + return res +} func (f *Functions) GetProgrami(p Program, pname Enum) int { return paramVal(f.Ctx.Call("getProgramParameter", js.Value(p), int(pname))) } @@ -284,6 +314,19 @@ func (f *Functions) GetUniformBlockIndex(p Program, name string) uint { func (f *Functions) GetUniformLocation(p Program, name string) Uniform { return Uniform(f.Ctx.Call("getUniformLocation", js.Value(p), name)) } +func (f *Functions) GetVertexAttrib(index int, pname Enum) int { + return paramVal(f.Ctx.Call("getVertexAttrib", index, int(pname))) +} +func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object { + obj := f.Ctx.Call("getVertexAttrib", index, int(pname)) + if !obj.Truthy() { + return Object{} + } + return Object(obj) +} +func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr { + return uintptr(f.Ctx.Call("getVertexAttribOffset", index, int(pname)).Int()) +} func (f *Functions) InvalidateFramebuffer(target, attachment Enum) { fn := f.Ctx.Get("invalidateFramebuffer") if !fn.IsUndefined() { diff --git a/internal/gl/gl_unix.go b/internal/gl/gl_unix.go index d7bfbbc0..93f550c9 100644 --- a/internal/gl/gl_unix.go +++ b/internal/gl/gl_unix.go @@ -42,7 +42,7 @@ typedef struct { void (*glBindRenderbuffer)(GLenum target, GLuint renderbuffer); void (*glBindTexture)(GLenum target, GLuint texture); void (*glBlendEquation)(GLenum mode); - void (*glBlendFunc)(GLenum sfactor, GLenum dfactor); + void (*glBlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcA, GLenum dstA); void (*glBufferData)(GLenum target, GLsizeiptr size, const void *data, GLenum usage); void (*glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data); GLenum (*glCheckFramebufferStatus)(GLenum target); @@ -76,7 +76,9 @@ typedef struct { void (*glGenTextures)(GLsizei n, GLuint *textures); GLenum (*glGetError)(void); void (*glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint *params); + void (*glGetFloatv)(GLenum pname, GLfloat *data); void (*glGetIntegerv)(GLenum pname, GLint *data); + void (*glGetIntegeri_v)(GLenum pname, GLuint idx, GLint *data); void (*glGetProgramiv)(GLuint program, GLenum pname, GLint *params); void (*glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); void (*glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint *params); @@ -84,6 +86,8 @@ typedef struct { void (*glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); const GLubyte *(*glGetString)(GLenum name); GLint (*glGetUniformLocation)(GLuint program, const GLchar *name); + void (*glGetVertexAttribiv)(GLuint index, GLenum pname, GLint *params); + void (*glGetVertexAttribPointerv)(GLuint index, GLenum pname, void **params); GLboolean (*glIsEnabled)(GLenum cap); void (*glLinkProgram)(GLuint program); void (*glPixelStorei)(GLenum pname, GLint param); @@ -162,8 +166,8 @@ static void glBlendEquation(glFunctions *f, GLenum mode) { f->glBlendEquation(mode); } -static void glBlendFunc(glFunctions *f, GLenum sfactor, GLenum dfactor) { - f->glBlendFunc(sfactor, dfactor); +static void glBlendFuncSeparate(glFunctions *f, GLenum srcRGB, GLenum dstRGB, GLenum srcA, GLenum dstA) { + f->glBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA); } static void glBufferData(glFunctions *f, GLenum target, GLsizeiptr size, const void *data, GLenum usage) { @@ -303,6 +307,14 @@ static void glGetIntegerv(glFunctions *f, GLenum pname, GLint *data) { f->glGetIntegerv(pname, data); } +static void glGetFloatv(glFunctions *f, GLenum pname, GLfloat *data) { + f->glGetFloatv(pname, data); +} + +static void glGetIntegeri_v(glFunctions *f, GLenum pname, GLuint idx, GLint *data) { + f->glGetIntegeri_v(pname, idx, data); +} + static void glGetProgramiv(glFunctions *f, GLuint program, GLenum pname, GLint *params) { f->glGetProgramiv(program, pname, params); } @@ -331,6 +343,17 @@ static GLint glGetUniformLocation(glFunctions *f, GLuint program, const GLchar * return f->glGetUniformLocation(program, name); } +static void glGetVertexAttribiv(glFunctions *f, GLuint index, GLenum pname, GLint *data) { + f->glGetVertexAttribiv(index, pname, data); +} + +// Return uintptr_t to avoid Cgo pointer check. +static uintptr_t glGetVertexAttribPointerv(glFunctions *f, GLuint index, GLenum pname) { + void *ptrs; + f->glGetVertexAttribPointerv(index, pname, &ptrs); + return (uintptr_t)ptrs; +} + static GLboolean glIsEnabled(glFunctions *f, GLenum cap) { return f->glIsEnabled(cap); } @@ -493,8 +516,9 @@ type Context interface{} type Functions struct { // Query caches. - uints [100]C.GLuint - ints [100]C.GLint + uints [100]C.GLuint + ints [100]C.GLint + floats [100]C.GLfloat f C.glFunctions } @@ -571,7 +595,7 @@ func (f *Functions) load(forceES bool) error { f.f.glBindRenderbuffer = must("glBindRenderbuffer") f.f.glBindTexture = must("glBindTexture") f.f.glBlendEquation = must("glBlendEquation") - f.f.glBlendFunc = must("glBlendFunc") + f.f.glBlendFuncSeparate = must("glBlendFuncSeparate") f.f.glBufferData = must("glBufferData") f.f.glBufferSubData = must("glBufferSubData") f.f.glCheckFramebufferStatus = must("glCheckFramebufferStatus") @@ -606,6 +630,7 @@ func (f *Functions) load(forceES bool) error { f.f.glGetError = must("glGetError") f.f.glGetFramebufferAttachmentParameteriv = must("glGetFramebufferAttachmentParameteriv") f.f.glGetIntegerv = must("glGetIntegerv") + f.f.glGetFloatv = must("glGetFloatv") f.f.glGetProgramiv = must("glGetProgramiv") f.f.glGetProgramInfoLog = must("glGetProgramInfoLog") f.f.glGetRenderbufferParameteriv = must("glGetRenderbufferParameteriv") @@ -613,6 +638,8 @@ func (f *Functions) load(forceES bool) error { f.f.glGetShaderInfoLog = must("glGetShaderInfoLog") f.f.glGetString = must("glGetString") f.f.glGetUniformLocation = must("glGetUniformLocation") + f.f.glGetVertexAttribiv = must("glGetVertexAttribiv") + f.f.glGetVertexAttribPointerv = must("glGetVertexAttribPointerv") f.f.glIsEnabled = must("glIsEnabled") f.f.glLinkProgram = must("glLinkProgram") f.f.glPixelStorei = must("glPixelStorei") @@ -634,7 +661,8 @@ func (f *Functions) load(forceES bool) error { // Extensions and GL ES 3 functions. f.f.glBindBufferBase = load("glBindBufferBase") - f.f.glBindVertexArray = must("glBindVertexArray") + f.f.glBindVertexArray = load("glBindVertexArray") + f.f.glGetIntegeri_v = load("glGetIntegeri_v") f.f.glGetUniformBlockIndex = load("glGetUniformBlockIndex") f.f.glUniformBlockBinding = load("glUniformBlockBinding") f.f.glInvalidateFramebuffer = load("glInvalidateFramebuffer") @@ -733,8 +761,8 @@ func (f *Functions) BlendEquation(mode Enum) { C.glBlendEquation(&f.f, C.GLenum(mode)) } -func (f *Functions) BlendFunc(sfactor, dfactor Enum) { - C.glBlendFunc(&f.f, C.GLenum(sfactor), C.GLenum(dfactor)) +func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { + C.glBlendFuncSeparate(&f.f, C.GLenum(srcRGB), C.GLenum(dstRGB), C.GLenum(srcA), C.GLenum(dstA)) } func (f *Functions) BlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int, mask Enum, filter Enum) { @@ -917,6 +945,10 @@ func (c *Functions) GetBinding(pname Enum) Object { return Object{uint(c.GetInteger(pname))} } +func (c *Functions) GetBindingi(pname Enum, idx int) Object { + return Object{uint(c.GetIntegeri(pname, idx))} +} + func (f *Functions) GetError() Enum { return Enum(C.glGetError(&f.f)) } @@ -931,6 +963,20 @@ func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname return int(f.ints[0]) } +func (f *Functions) GetFloat4(pname Enum) [4]float32 { + C.glGetFloatv(&f.f, C.GLenum(pname), &f.floats[0]) + var r [4]float32 + for i := range r { + r[i] = float32(f.floats[i]) + } + return r +} + +func (f *Functions) GetFloat(pname Enum) float32 { + C.glGetFloatv(&f.f, C.GLenum(pname), &f.floats[0]) + return float32(f.floats[0]) +} + func (f *Functions) GetInteger4(pname Enum) [4]int { C.glGetIntegerv(&f.f, C.GLenum(pname), &f.ints[0]) var r [4]int @@ -945,6 +991,11 @@ func (f *Functions) GetInteger(pname Enum) int { return int(f.ints[0]) } +func (f *Functions) GetIntegeri(pname Enum, idx int) int { + C.glGetIntegeri_v(&f.f, C.GLenum(pname), C.GLuint(idx), &f.ints[0]) + return int(f.ints[0]) +} + func (f *Functions) GetProgrami(p Program, pname Enum) int { C.glGetProgramiv(&f.f, C.GLuint(p.V), C.GLenum(pname), &f.ints[0]) return int(f.ints[0]) @@ -1023,6 +1074,20 @@ func (f *Functions) GetUniformLocation(p Program, name string) Uniform { return Uniform{int(C.glGetUniformLocation(&f.f, C.GLuint(p.V), cname))} } +func (f *Functions) GetVertexAttrib(index int, pname Enum) int { + C.glGetVertexAttribiv(&f.f, C.GLuint(index), C.GLenum(pname), &f.ints[0]) + return int(f.ints[0]) +} + +func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object { + return Object{uint(f.GetVertexAttrib(index, pname))} +} + +func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr { + ptr := C.glGetVertexAttribPointerv(&f.f, C.GLuint(index), C.GLenum(pname)) + return uintptr(ptr) +} + func (f *Functions) InvalidateFramebuffer(target, attachment Enum) { C.glInvalidateFramebuffer(&f.f, C.GLenum(target), C.GLenum(attachment)) } diff --git a/internal/gl/gl_windows.go b/internal/gl/gl_windows.go index e5305b14..434513e3 100644 --- a/internal/gl/gl_windows.go +++ b/internal/gl/gl_windows.go @@ -24,7 +24,7 @@ var ( _glBindTexture = LibGLESv2.NewProc("glBindTexture") _glBindVertexArray = LibGLESv2.NewProc("glBindVertexArray") _glBlendEquation = LibGLESv2.NewProc("glBlendEquation") - _glBlendFunc = LibGLESv2.NewProc("glBlendFunc") + _glBlendFuncSeparate = LibGLESv2.NewProc("glBlendFuncSeparate") _glBufferData = LibGLESv2.NewProc("glBufferData") _glBufferSubData = LibGLESv2.NewProc("glBufferSubData") _glCheckFramebufferStatus = LibGLESv2.NewProc("glCheckFramebufferStatus") @@ -64,8 +64,10 @@ var ( _glGenQueries = LibGLESv2.NewProc("glGenQueries") _glGetError = LibGLESv2.NewProc("glGetError") _glGetRenderbufferParameteriv = LibGLESv2.NewProc("glGetRenderbufferParameteriv") + _glGetFloatv = LibGLESv2.NewProc("glGetFloatv") _glGetFramebufferAttachmentParameteriv = LibGLESv2.NewProc("glGetFramebufferAttachmentParameteriv") _glGetIntegerv = LibGLESv2.NewProc("glGetIntegerv") + _glGetIntegeri_v = LibGLESv2.NewProc("glGetIntegeri_v") _glGetProgramiv = LibGLESv2.NewProc("glGetProgramiv") _glGetProgramInfoLog = LibGLESv2.NewProc("glGetProgramInfoLog") _glGetQueryObjectuiv = LibGLESv2.NewProc("glGetQueryObjectuiv") @@ -73,6 +75,8 @@ var ( _glGetShaderInfoLog = LibGLESv2.NewProc("glGetShaderInfoLog") _glGetString = LibGLESv2.NewProc("glGetString") _glGetUniformLocation = LibGLESv2.NewProc("glGetUniformLocation") + _glGetVertexAttribiv = LibGLESv2.NewProc("glGetVertexAttribiv") + _glGetVertexAttribPointerv = LibGLESv2.NewProc("glGetVertexAttribPointerv") _glInvalidateFramebuffer = LibGLESv2.NewProc("glInvalidateFramebuffer") _glIsEnabled = LibGLESv2.NewProc("glIsEnabled") _glLinkProgram = LibGLESv2.NewProc("glLinkProgram") @@ -98,7 +102,9 @@ var ( type Functions struct { // Query caches. - int32s [100]int32 + int32s [100]int32 + float32s [100]float32 + uintptrs [100]uintptr } type Context interface{} @@ -149,8 +155,8 @@ func (c *Functions) BindVertexArray(a VertexArray) { func (c *Functions) BlendEquation(mode Enum) { syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0) } -func (c *Functions) BlendFunc(sfactor, dfactor Enum) { - syscall.Syscall(_glBlendFunc.Addr(), 2, uintptr(sfactor), uintptr(dfactor), 0) +func (c *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { + syscall.Syscall6(_glBlendFuncSeparate.Addr(), 4, uintptr(srcRGB), uintptr(dstRGB), uintptr(srcA), uintptr(dstA), 0, 0) } func (f *Functions) BlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int, mask Enum, filter Enum) { panic("not implemented") @@ -299,6 +305,9 @@ func (f *Functions) GetUniformBlockIndex(p Program, name string) uint { func (c *Functions) GetBinding(pname Enum) Object { return Object{uint(c.GetInteger(pname))} } +func (c *Functions) GetBindingi(pname Enum, idx int) Object { + return Object{uint(c.GetIntegeri(pname, idx))} +} func (c *Functions) GetError() Enum { e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0) return Enum(e) @@ -323,6 +332,20 @@ func (c *Functions) GetInteger(pname Enum) int { syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) return int(c.int32s[0]) } +func (c *Functions) GetIntegeri(pname Enum, idx int) int { + syscall.Syscall(_glGetIntegeri_v.Addr(), 3, uintptr(pname), uintptr(idx), uintptr(unsafe.Pointer(&c.int32s[0]))) + return int(c.int32s[0]) +} +func (c *Functions) GetFloat(pname Enum) float32 { + syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0) + return c.float32s[0] +} +func (c *Functions) GetFloat4(pname Enum) [4]float32 { + syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0) + var r [4]float32 + copy(r[:], c.float32s[:]) + return r +} func (c *Functions) GetProgrami(p Program, pname Enum) int { syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) return int(c.int32s[0]) @@ -358,6 +381,19 @@ func (c *Functions) GetUniformLocation(p Program, name string) Uniform { issue34474KeepAlive(c0) return Uniform{int(u)} } +func (c *Functions) GetVertexAttrib(index int, pname Enum) int { + syscall.Syscall(_glGetVertexAttribiv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) + return int(c.int32s[0]) +} + +func (c *Functions) GetVertexAttribBinding(index int, pname Enum) Object { + return Object{uint(c.GetVertexAttrib(index, pname))} +} + +func (c *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr { + syscall.Syscall(_glGetVertexAttribPointerv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.uintptrs[0]))) + return c.uintptrs[0] +} func (c *Functions) InvalidateFramebuffer(target, attachment Enum) { addr := _glInvalidateFramebuffer.Addr() if addr == 0 { diff --git a/internal/gl/types.go b/internal/gl/types.go index f7b04b48..821d3f69 100644 --- a/internal/gl/types.go +++ b/internal/gl/types.go @@ -3,20 +3,28 @@ package gl type ( - Buffer struct{ V uint } - Framebuffer struct{ V uint } - Program struct{ V uint } - Renderbuffer struct{ V uint } - Shader struct{ V uint } - Texture struct{ V uint } - Query struct{ V uint } - Uniform struct{ V int } - VertexArray struct{ V uint } Object struct{ V uint } + Buffer Object + Framebuffer Object + Program Object + Renderbuffer Object + Shader Object + Texture Object + Query Object + Uniform struct{ V int } + VertexArray Object ) +func (o Object) valid() bool { + return o.V != 0 +} + +func (o Object) equal(o2 Object) bool { + return o == o2 +} + func (u Framebuffer) Valid() bool { - return u.V != 0 + return Object(u).valid() } func (u Uniform) Valid() bool { @@ -24,13 +32,45 @@ func (u Uniform) Valid() bool { } func (p Program) Valid() bool { - return p.V != 0 + return Object(p).valid() } func (s Shader) Valid() bool { - return s.V != 0 + return Object(s).valid() } func (a VertexArray) Valid() bool { - return a.V != 0 + return Object(a).valid() +} + +func (f Framebuffer) Equal(f2 Framebuffer) bool { + return Object(f).equal(Object(f2)) +} + +func (p Program) Equal(p2 Program) bool { + return Object(p).equal(Object(p2)) +} + +func (s Shader) Equal(s2 Shader) bool { + return Object(s).equal(Object(s2)) +} + +func (u Uniform) Equal(u2 Uniform) bool { + return u == u2 +} + +func (a VertexArray) Equal(a2 VertexArray) bool { + return Object(a).equal(Object(a2)) +} + +func (r Renderbuffer) Equal(r2 Renderbuffer) bool { + return Object(r).equal(Object(r2)) +} + +func (t Texture) Equal(t2 Texture) bool { + return Object(t).equal(Object(t2)) +} + +func (b Buffer) Equal(b2 Buffer) bool { + return Object(b).equal(Object(b2)) } diff --git a/internal/gl/types_js.go b/internal/gl/types_js.go index 392b3c0d..8d91a6bd 100644 --- a/internal/gl/types_js.go +++ b/internal/gl/types_js.go @@ -5,34 +5,86 @@ package gl import "syscall/js" type ( - Buffer js.Value - Framebuffer js.Value - Program js.Value - Renderbuffer js.Value - Shader js.Value - Texture js.Value - Query js.Value - Uniform js.Value - VertexArray js.Value Object js.Value + Buffer Object + Framebuffer Object + Program Object + Renderbuffer Object + Shader Object + Texture Object + Query Object + Uniform Object + VertexArray Object ) +func (o Object) valid() bool { + return js.Value(o).Truthy() +} + +func (o Object) equal(o2 Object) bool { + return js.Value(o).Equal(js.Value(o2)) +} + +func (b Buffer) Valid() bool { + return Object(b).valid() +} + func (f Framebuffer) Valid() bool { - return !js.Value(f).IsUndefined() && !js.Value(f).IsNull() + return Object(f).valid() } func (p Program) Valid() bool { - return !js.Value(p).IsUndefined() && !js.Value(p).IsNull() + return Object(p).valid() +} + +func (r Renderbuffer) Valid() bool { + return Object(r).valid() } func (s Shader) Valid() bool { - return !js.Value(s).IsUndefined() && !js.Value(s).IsNull() + return Object(s).valid() +} + +func (t Texture) Valid() bool { + return Object(t).valid() } func (u Uniform) Valid() bool { - return !js.Value(u).IsUndefined() && !js.Value(u).IsNull() + return Object(u).valid() } func (a VertexArray) Valid() bool { - return !js.Value(a).IsUndefined() && !js.Value(a).IsNull() + return Object(a).valid() +} + +func (f Framebuffer) Equal(f2 Framebuffer) bool { + return Object(f).equal(Object(f2)) +} + +func (p Program) Equal(p2 Program) bool { + return Object(p).equal(Object(p2)) +} + +func (s Shader) Equal(s2 Shader) bool { + return Object(s).equal(Object(s2)) +} + +func (u Uniform) Equal(u2 Uniform) bool { + return Object(u).equal(Object(u2)) +} + +func (a VertexArray) Equal(a2 VertexArray) bool { + return Object(a).equal(Object(a2)) +} + +func (r Renderbuffer) Equal(r2 Renderbuffer) bool { + return Object(r).equal(Object(r2)) +} + +func (t Texture) Equal(t2 Texture) bool { + return Object(t).equal(Object(t2)) +} + +func (b Buffer) Equal(b2 Buffer) bool { + return Object(b).equal(Object(b2)) }