gpu/internal/opengl,internal: move sRGB emulation to OpenGL driver

There is only one driver but several backends (EGL, WebGL).

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-05-15 15:25:02 +02:00
parent e90c99a66c
commit 21c319ace5
14 changed files with 104 additions and 102 deletions
-26
View File
@@ -8,13 +8,11 @@ import (
"gioui.org/gpu" "gioui.org/gpu"
"gioui.org/internal/gl" "gioui.org/internal/gl"
"gioui.org/internal/srgb"
) )
type context struct { type context struct {
ctx js.Value ctx js.Value
cnv js.Value cnv js.Value
srgbFBO *srgb.FBO
} }
func newContext(w *window) (*context, error) { func newContext(w *window) (*context, error) {
@@ -43,19 +41,9 @@ func (c *context) API() gpu.API {
} }
func (c *context) Release() { func (c *context) Release() {
if c.srgbFBO != nil {
c.srgbFBO.Release()
c.srgbFBO = nil
}
} }
func (c *context) Present() error { func (c *context) Present() error {
if c.srgbFBO != nil {
c.srgbFBO.Blit()
}
if c.srgbFBO != nil {
c.srgbFBO.AfterPresent()
}
if c.ctx.Call("isContextLost").Bool() { if c.ctx.Call("isContextLost").Bool() {
return errors.New("context lost") return errors.New("context lost")
} }
@@ -67,20 +55,6 @@ func (c *context) Lock() {}
func (c *context) Unlock() {} func (c *context) Unlock() {}
func (c *context) MakeCurrent() error { func (c *context) MakeCurrent() error {
if c.srgbFBO == nil {
var err error
c.srgbFBO, err = srgb.New(gl.Context(c.ctx))
if err != nil {
c.Release()
c.srgbFBO = nil
return err
}
}
w, h := c.cnv.Get("width").Int(), c.cnv.Get("height").Int()
if err := c.srgbFBO.Refresh(w, h); err != nil {
c.Release()
return err
}
return nil return nil
} }
+1 -1
View File
@@ -280,7 +280,7 @@ func (g *compute) Frame() error {
Y: (viewport.Y + tileHeightPx - 1) / tileHeightPx, Y: (viewport.Y + tileHeightPx - 1) / tileHeightPx,
} }
defFBO := g.ctx.BeginFrame() defFBO := g.ctx.BeginFrame(viewport)
defer g.ctx.EndFrame() defer g.ctx.EndFrame()
if g.drawOps.profile && g.timers.t == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) { if g.drawOps.profile && g.timers.t == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) {
+2 -2
View File
@@ -425,9 +425,9 @@ func (g *gpu) Collect(viewport image.Point, frameOps *op.Ops) {
} }
func (g *gpu) Frame() error { func (g *gpu) Frame() error {
defFBO := g.ctx.BeginFrame()
defer g.ctx.EndFrame()
viewport := g.renderer.blitter.viewport viewport := g.renderer.blitter.viewport
defFBO := g.ctx.BeginFrame(viewport)
defer g.ctx.EndFrame()
for _, img := range g.drawOps.imageOps { for _, img := range g.drawOps.imageOps {
expandPathOp(img.path, img.clip) expandPathOp(img.path, img.clip)
} }
+1 -1
View File
@@ -174,7 +174,7 @@ func newDriver(t *testing.T) driver.Device {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
b.BeginFrame() b.BeginFrame(image.Pt(1, 1))
t.Cleanup(func() { t.Cleanup(func() {
b.EndFrame() b.EndFrame()
ctx.ReleaseCurrent() ctx.ReleaseCurrent()
+1 -1
View File
@@ -164,7 +164,7 @@ func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
return b, nil return b, nil
} }
func (b *Backend) BeginFrame() driver.Framebuffer { func (b *Backend) BeginFrame(viewport image.Point) driver.Framebuffer {
renderTarget, depthView := b.ctx.OMGetRenderTargets() renderTarget, depthView := b.ctx.OMGetRenderTargets()
// Assume someone else is holding on to the render targets. // Assume someone else is holding on to the render targets.
if renderTarget != nil { if renderTarget != nil {
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// APIs such as OpenGL, Direct3D useful for rendering Gio // APIs such as OpenGL, Direct3D useful for rendering Gio
// operations. // operations.
type Device interface { type Device interface {
BeginFrame() Framebuffer BeginFrame(viewport image.Point) Framebuffer
EndFrame() EndFrame()
Caps() Caps Caps() Caps
NewTimer() Timer NewTimer() Timer
+39 -3
View File
@@ -12,6 +12,7 @@ import (
"gioui.org/gpu/internal/driver" "gioui.org/gpu/internal/driver"
"gioui.org/internal/gl" "gioui.org/internal/gl"
"gioui.org/internal/srgb"
) )
// Backend implements driver.Device. // Backend implements driver.Device.
@@ -30,6 +31,9 @@ type Backend struct {
// Single channel alpha textures. // Single channel alpha textures.
alphaTriple textureTriple alphaTriple textureTriple
srgbaTriple textureTriple srgbaTriple textureTriple
sRGBFBO *srgb.FBO
defFBO gl.Framebuffer
} }
// State tracking. // State tracking.
@@ -169,15 +173,43 @@ func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) {
return b, nil return b, nil
} }
func (b *Backend) BeginFrame() driver.Framebuffer { func (b *Backend) BeginFrame(viewport image.Point) driver.Framebuffer {
// Assume GL state is reset between frames. // Assume GL state is reset between frames.
b.state = glstate{} b.state = glstate{}
fboID := gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING)) b.defFBO = gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING))
return &gpuFramebuffer{backend: b, obj: fboID, foreign: true} // Determine color encoding of the output framebuffer.
var fbEncoding int
if !b.defFBO.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)
}
renderFBO := b.defFBO
if fbEncoding == gl.LINEAR {
if b.sRGBFBO == nil {
// Framebuffer is not in sRGB format. Emulate it.
sfbo, err := srgb.New(b.funcs)
if err != nil {
panic(err)
}
b.sRGBFBO = sfbo
}
if err := b.sRGBFBO.Refresh(viewport); err != nil {
panic(err)
}
renderFBO = b.sRGBFBO.Framebuffer()
}
b.funcs.BindFramebuffer(gl.FRAMEBUFFER, renderFBO)
return &gpuFramebuffer{backend: b, obj: renderFBO, foreign: true}
} }
func (b *Backend) EndFrame() { func (b *Backend) EndFrame() {
b.funcs.ActiveTexture(gl.TEXTURE0) b.funcs.ActiveTexture(gl.TEXTURE0)
if b.sRGBFBO != nil {
b.funcs.BindFramebuffer(gl.FRAMEBUFFER, b.defFBO)
b.sRGBFBO.Blit()
}
b.funcs.BindFramebuffer(gl.FRAMEBUFFER, b.defFBO)
} }
func (b *Backend) Caps() driver.Caps { func (b *Backend) Caps() driver.Caps {
@@ -313,6 +345,10 @@ func glErr(f *gl.Functions) error {
} }
func (b *Backend) Release() { func (b *Backend) Release() {
if b.sRGBFBO != nil {
b.sRGBFBO.Release()
}
*b = Backend{}
} }
func (b *Backend) MemoryBarrier() { func (b *Backend) MemoryBarrier() {
+1 -32
View File
@@ -11,7 +11,6 @@ import (
"strings" "strings"
"gioui.org/gpu" "gioui.org/gpu"
"gioui.org/internal/srgb"
) )
type Context struct { type Context struct {
@@ -20,8 +19,6 @@ type Context struct {
eglSurf _EGLSurface eglSurf _EGLSurface
width, height int width, height int
refreshFBO bool refreshFBO bool
// For sRGB emulation.
srgbFBO *srgb.FBO
} }
type eglContext struct { type eglContext struct {
@@ -60,10 +57,6 @@ const (
) )
func (c *Context) Release() { func (c *Context) Release() {
if c.srgbFBO != nil {
c.srgbFBO.Release()
c.srgbFBO = nil
}
c.ReleaseSurface() c.ReleaseSurface()
if c.eglCtx != nil { if c.eglCtx != nil {
eglDestroyContext(c.disp, c.eglCtx.ctx) eglDestroyContext(c.disp, c.eglCtx.ctx)
@@ -73,15 +66,9 @@ func (c *Context) Release() {
} }
func (c *Context) Present() error { func (c *Context) Present() error {
if c.srgbFBO != nil {
c.srgbFBO.Blit()
}
if !eglSwapBuffers(c.disp, c.eglSurf) { if !eglSwapBuffers(c.disp, c.eglSurf) {
return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError()) return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError())
} }
if c.srgbFBO != nil {
c.srgbFBO.AfterPresent()
}
return nil return nil
} }
@@ -151,24 +138,6 @@ func (c *Context) MakeCurrent() error {
if !eglMakeCurrent(c.disp, c.eglSurf, c.eglSurf, c.eglCtx.ctx) { if !eglMakeCurrent(c.disp, c.eglSurf, c.eglSurf, c.eglCtx.ctx) {
return fmt.Errorf("eglMakeCurrent error 0x%x", eglGetError()) return fmt.Errorf("eglMakeCurrent error 0x%x", eglGetError())
} }
if c.eglCtx.srgb || c.eglSurf == nilEGLSurface {
return nil
}
if c.srgbFBO == nil {
var err error
c.srgbFBO, err = srgb.New(nil)
if err != nil {
c.ReleaseCurrent()
return err
}
}
if c.refreshFBO {
c.refreshFBO = false
if err := c.srgbFBO.Refresh(c.width, c.height); err != nil {
c.ReleaseCurrent()
return err
}
}
return nil return nil
} }
@@ -268,7 +237,7 @@ func createSurface(disp _EGLDisplay, eglCtx *eglContext, win NativeWindowType) (
surfAttribs = append(surfAttribs, _EGL_NONE) surfAttribs = append(surfAttribs, _EGL_NONE)
eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs) eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
if eglSurf == nilEGLSurface && eglCtx.srgb { if eglSurf == nilEGLSurface && eglCtx.srgb {
// Try again without sRGB // Try again without sRGB.
eglCtx.srgb = false eglCtx.srgb = false
surfAttribs = []_EGLint{_EGL_NONE} surfAttribs = []_EGLint{_EGL_NONE}
eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs) eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
+1
View File
@@ -10,6 +10,7 @@ type (
const ( const (
ALL_BARRIER_BITS = 0xffffffff ALL_BARRIER_BITS = 0xffffffff
ARRAY_BUFFER = 0x8892 ARRAY_BUFFER = 0x8892
BACK = 0x0405
BLEND = 0xbe2 BLEND = 0xbe2
CLAMP_TO_EDGE = 0x812f CLAMP_TO_EDGE = 0x812f
COLOR_ATTACHMENT0 = 0x8ce0 COLOR_ATTACHMENT0 = 0x8ce0
+9
View File
@@ -436,6 +436,15 @@ func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname
return int(f.ints[0]) return int(f.ints[0])
} }
func (f *Functions) GetInteger4(pname Enum) [4]int {
C.glGetIntegerv(C.GLenum(pname), &f.ints[0])
var r [4]int
for i := range r {
r[i] = int(f.ints[i])
}
return r
}
func (f *Functions) GetInteger(pname Enum) int { func (f *Functions) GetInteger(pname Enum) int {
C.glGetIntegerv(C.GLenum(pname), &f.ints[0]) C.glGetIntegerv(C.GLenum(pname), &f.ints[0])
return int(f.ints[0]) return int(f.ints[0])
+8
View File
@@ -292,6 +292,14 @@ func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname
p, _, _ := syscall.Syscall(_glGetFramebufferAttachmentParameteri.Addr(), 3, uintptr(target), uintptr(attachment), uintptr(pname)) p, _, _ := syscall.Syscall(_glGetFramebufferAttachmentParameteri.Addr(), 3, uintptr(target), uintptr(attachment), uintptr(pname))
return int(p) return int(p)
} }
func (c *Functions) GetInteger4(pname Enum) [4]int {
syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
var r [4]int
for i := range r {
r[i] = int(c.int32s[i])
}
return r
}
func (c *Functions) GetInteger(pname Enum) int { func (c *Functions) GetInteger(pname Enum) int {
syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
return int(c.int32s[0]) return int(c.int32s[0])
+4
View File
@@ -14,6 +14,10 @@ type (
Object struct{ V uint } Object struct{ V uint }
) )
func (u Framebuffer) Valid() bool {
return u.V != 0
}
func (u Uniform) Valid() bool { func (u Uniform) Valid() bool {
return u.V != -1 return u.V != -1
} }
+4
View File
@@ -16,6 +16,10 @@ type (
Object js.Value Object js.Value
) )
func (f Framebuffer) Valid() bool {
return !js.Value(f).IsUndefined() && !js.Value(f).IsNull()
}
func (p Program) Valid() bool { func (p Program) Valid() bool {
return !js.Value(p).IsUndefined() && !js.Value(p).IsNull() return !js.Value(p).IsUndefined() && !js.Value(p).IsNull()
} }
+23 -26
View File
@@ -3,7 +3,9 @@
package srgb package srgb
import ( import (
"errors"
"fmt" "fmt"
"image"
"runtime" "runtime"
"strings" "strings"
@@ -16,8 +18,8 @@ import (
// sRGB enabled native framebuffers. // sRGB enabled native framebuffers.
type FBO struct { type FBO struct {
c *gl.Functions c *gl.Functions
width, height int viewport image.Point
frameBuffer gl.Framebuffer srgbBuffer gl.Framebuffer
depthBuffer gl.Renderbuffer depthBuffer gl.Renderbuffer
colorTex gl.Texture colorTex gl.Texture
blitted bool blitted bool
@@ -26,11 +28,7 @@ type FBO struct {
gl3 bool gl3 bool
} }
func New(ctx gl.Context) (*FBO, error) { func New(f *gl.Functions) (*FBO, error) {
f, err := gl.NewFunctions(ctx)
if err != nil {
return nil, err
}
var gl3 bool var gl3 bool
glVer := f.GetString(gl.VERSION) glVer := f.GetString(gl.VERSION)
ver, _, err := gl.ParseGLVersion(glVer) ver, _, err := gl.ParseGLVersion(glVer)
@@ -48,7 +46,7 @@ func New(ctx gl.Context) (*FBO, error) {
s := &FBO{ s := &FBO{
c: f, c: f,
gl3: gl3, gl3: gl3,
frameBuffer: f.CreateFramebuffer(), srgbBuffer: f.CreateFramebuffer(),
colorTex: f.CreateTexture(), colorTex: f.CreateTexture(),
depthBuffer: f.CreateRenderbuffer(), depthBuffer: f.CreateRenderbuffer(),
} }
@@ -81,7 +79,6 @@ func (s *FBO) Blit() {
s.c.BufferSubData(gl.ARRAY_BUFFER, 0, coords) s.c.BufferSubData(gl.ARRAY_BUFFER, 0, coords)
s.blitted = true s.blitted = true
} }
s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{})
s.c.UseProgram(s.prog) s.c.UseProgram(s.prog)
s.c.BindTexture(gl.TEXTURE_2D, s.colorTex) s.c.BindTexture(gl.TEXTURE_2D, s.colorTex)
s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad) s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad)
@@ -93,38 +90,38 @@ func (s *FBO) Blit() {
s.c.BindTexture(gl.TEXTURE_2D, gl.Texture{}) s.c.BindTexture(gl.TEXTURE_2D, gl.Texture{})
s.c.DisableVertexAttribArray(0) s.c.DisableVertexAttribArray(0)
s.c.DisableVertexAttribArray(1) s.c.DisableVertexAttribArray(1)
s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer) s.c.BindFramebuffer(gl.FRAMEBUFFER, s.srgbBuffer)
s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0) s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT) s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT)
// The Android emulator requires framebuffer 0 bound at eglSwapBuffer time.
// Bind the sRGB framebuffer again in afterPresent.
s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{})
} }
func (s *FBO) AfterPresent() { func (s *FBO) Framebuffer() gl.Framebuffer {
s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer) return s.srgbBuffer
} }
func (s *FBO) Refresh(w, h int) error { func (s *FBO) Refresh(viewport image.Point) error {
s.width, s.height = w, h if viewport.X == 0 || viewport.Y == 0 {
if w == 0 || h == 0 { return errors.New("srgb: zero-sized framebuffer")
}
if s.viewport == viewport {
return nil return nil
} }
s.viewport = viewport
s.c.BindTexture(gl.TEXTURE_2D, s.colorTex) s.c.BindTexture(gl.TEXTURE_2D, s.colorTex)
if s.gl3 { if s.gl3 {
s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, w, h, gl.RGBA, gl.UNSIGNED_BYTE) s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, viewport.X, viewport.Y, gl.RGBA, gl.UNSIGNED_BYTE)
} else /* EXT_sRGB */ { } else /* EXT_sRGB */ {
s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB_ALPHA_EXT, w, h, gl.SRGB_ALPHA_EXT, gl.UNSIGNED_BYTE) 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)) currentRB := gl.Renderbuffer(s.c.GetBinding(gl.RENDERBUFFER_BINDING))
s.c.BindRenderbuffer(gl.RENDERBUFFER, s.depthBuffer) s.c.BindRenderbuffer(gl.RENDERBUFFER, s.depthBuffer)
s.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h) s.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, viewport.X, viewport.Y)
s.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB) s.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB)
s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer) s.c.BindFramebuffer(gl.FRAMEBUFFER, s.srgbBuffer)
s.c.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, s.colorTex, 0) 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) s.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, s.depthBuffer)
if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError()) return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", viewport.X, viewport.Y, st, s.c.GetError())
} }
if runtime.GOOS == "js" { if runtime.GOOS == "js" {
@@ -136,9 +133,9 @@ func (s *FBO) Refresh(w, h int) error {
var pixel [4]byte var pixel [4]byte
s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:]) s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:])
if pixel[0] == 128 { // Correct sRGB color value is ~188 if pixel[0] == 128 { // Correct sRGB color value is ~188
s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, gl.RGBA, gl.UNSIGNED_BYTE) s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, viewport.X, viewport.Y, gl.RGBA, gl.UNSIGNED_BYTE)
if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError()) return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", viewport.X, viewport.Y, st, s.c.GetError())
} }
} }
} }
@@ -147,7 +144,7 @@ func (s *FBO) Refresh(w, h int) error {
} }
func (s *FBO) Release() { func (s *FBO) Release() {
s.c.DeleteFramebuffer(s.frameBuffer) s.c.DeleteFramebuffer(s.srgbBuffer)
s.c.DeleteTexture(s.colorTex) s.c.DeleteTexture(s.colorTex)
s.c.DeleteRenderbuffer(s.depthBuffer) s.c.DeleteRenderbuffer(s.depthBuffer)
if s.blitted { if s.blitted {