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
+2 -28
View File
@@ -8,13 +8,11 @@ import (
"gioui.org/gpu"
"gioui.org/internal/gl"
"gioui.org/internal/srgb"
)
type context struct {
ctx js.Value
cnv js.Value
srgbFBO *srgb.FBO
ctx js.Value
cnv js.Value
}
func newContext(w *window) (*context, error) {
@@ -43,19 +41,9 @@ func (c *context) API() gpu.API {
}
func (c *context) Release() {
if c.srgbFBO != nil {
c.srgbFBO.Release()
c.srgbFBO = nil
}
}
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() {
return errors.New("context lost")
}
@@ -67,20 +55,6 @@ func (c *context) Lock() {}
func (c *context) Unlock() {}
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
}
+1 -1
View File
@@ -280,7 +280,7 @@ func (g *compute) Frame() error {
Y: (viewport.Y + tileHeightPx - 1) / tileHeightPx,
}
defFBO := g.ctx.BeginFrame()
defFBO := g.ctx.BeginFrame(viewport)
defer g.ctx.EndFrame()
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 {
defFBO := g.ctx.BeginFrame()
defer g.ctx.EndFrame()
viewport := g.renderer.blitter.viewport
defFBO := g.ctx.BeginFrame(viewport)
defer g.ctx.EndFrame()
for _, img := range g.drawOps.imageOps {
expandPathOp(img.path, img.clip)
}
+1 -1
View File
@@ -174,7 +174,7 @@ func newDriver(t *testing.T) driver.Device {
if err != nil {
t.Fatal(err)
}
b.BeginFrame()
b.BeginFrame(image.Pt(1, 1))
t.Cleanup(func() {
b.EndFrame()
ctx.ReleaseCurrent()
+1 -1
View File
@@ -164,7 +164,7 @@ func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
return b, nil
}
func (b *Backend) BeginFrame() driver.Framebuffer {
func (b *Backend) BeginFrame(viewport image.Point) driver.Framebuffer {
renderTarget, depthView := b.ctx.OMGetRenderTargets()
// Assume someone else is holding on to the render targets.
if renderTarget != nil {
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// APIs such as OpenGL, Direct3D useful for rendering Gio
// operations.
type Device interface {
BeginFrame() Framebuffer
BeginFrame(viewport image.Point) Framebuffer
EndFrame()
Caps() Caps
NewTimer() Timer
+39 -3
View File
@@ -12,6 +12,7 @@ import (
"gioui.org/gpu/internal/driver"
"gioui.org/internal/gl"
"gioui.org/internal/srgb"
)
// Backend implements driver.Device.
@@ -30,6 +31,9 @@ type Backend struct {
// Single channel alpha textures.
alphaTriple textureTriple
srgbaTriple textureTriple
sRGBFBO *srgb.FBO
defFBO gl.Framebuffer
}
// State tracking.
@@ -169,15 +173,43 @@ func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) {
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.
b.state = glstate{}
fboID := gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING))
return &gpuFramebuffer{backend: b, obj: fboID, foreign: true}
b.defFBO = gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING))
// 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() {
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 {
@@ -313,6 +345,10 @@ func glErr(f *gl.Functions) error {
}
func (b *Backend) Release() {
if b.sRGBFBO != nil {
b.sRGBFBO.Release()
}
*b = Backend{}
}
func (b *Backend) MemoryBarrier() {
+1 -32
View File
@@ -11,7 +11,6 @@ import (
"strings"
"gioui.org/gpu"
"gioui.org/internal/srgb"
)
type Context struct {
@@ -20,8 +19,6 @@ type Context struct {
eglSurf _EGLSurface
width, height int
refreshFBO bool
// For sRGB emulation.
srgbFBO *srgb.FBO
}
type eglContext struct {
@@ -60,10 +57,6 @@ const (
)
func (c *Context) Release() {
if c.srgbFBO != nil {
c.srgbFBO.Release()
c.srgbFBO = nil
}
c.ReleaseSurface()
if c.eglCtx != nil {
eglDestroyContext(c.disp, c.eglCtx.ctx)
@@ -73,15 +66,9 @@ func (c *Context) Release() {
}
func (c *Context) Present() error {
if c.srgbFBO != nil {
c.srgbFBO.Blit()
}
if !eglSwapBuffers(c.disp, c.eglSurf) {
return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError())
}
if c.srgbFBO != nil {
c.srgbFBO.AfterPresent()
}
return nil
}
@@ -151,24 +138,6 @@ func (c *Context) MakeCurrent() error {
if !eglMakeCurrent(c.disp, c.eglSurf, c.eglSurf, c.eglCtx.ctx) {
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
}
@@ -268,7 +237,7 @@ func createSurface(disp _EGLDisplay, eglCtx *eglContext, win NativeWindowType) (
surfAttribs = append(surfAttribs, _EGL_NONE)
eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
if eglSurf == nilEGLSurface && eglCtx.srgb {
// Try again without sRGB
// Try again without sRGB.
eglCtx.srgb = false
surfAttribs = []_EGLint{_EGL_NONE}
eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
+1
View File
@@ -10,6 +10,7 @@ type (
const (
ALL_BARRIER_BITS = 0xffffffff
ARRAY_BUFFER = 0x8892
BACK = 0x0405
BLEND = 0xbe2
CLAMP_TO_EDGE = 0x812f
COLOR_ATTACHMENT0 = 0x8ce0
+9
View File
@@ -436,6 +436,15 @@ func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname
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 {
C.glGetIntegerv(C.GLenum(pname), &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))
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 {
syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
return int(c.int32s[0])
+4
View File
@@ -14,6 +14,10 @@ type (
Object struct{ V uint }
)
func (u Framebuffer) Valid() bool {
return u.V != 0
}
func (u Uniform) Valid() bool {
return u.V != -1
}
+4
View File
@@ -16,6 +16,10 @@ type (
Object js.Value
)
func (f Framebuffer) Valid() bool {
return !js.Value(f).IsUndefined() && !js.Value(f).IsNull()
}
func (p Program) Valid() bool {
return !js.Value(p).IsUndefined() && !js.Value(p).IsNull()
}
+30 -33
View File
@@ -3,7 +3,9 @@
package srgb
import (
"errors"
"fmt"
"image"
"runtime"
"strings"
@@ -15,22 +17,18 @@ import (
// for gamma-correct rendering on platforms without
// sRGB enabled native framebuffers.
type FBO struct {
c *gl.Functions
width, height int
frameBuffer gl.Framebuffer
depthBuffer gl.Renderbuffer
colorTex gl.Texture
blitted bool
quad gl.Buffer
prog gl.Program
gl3 bool
c *gl.Functions
viewport image.Point
srgbBuffer gl.Framebuffer
depthBuffer gl.Renderbuffer
colorTex gl.Texture
blitted bool
quad gl.Buffer
prog gl.Program
gl3 bool
}
func New(ctx gl.Context) (*FBO, error) {
f, err := gl.NewFunctions(ctx)
if err != nil {
return nil, err
}
func New(f *gl.Functions) (*FBO, error) {
var gl3 bool
glVer := f.GetString(gl.VERSION)
ver, _, err := gl.ParseGLVersion(glVer)
@@ -48,7 +46,7 @@ func New(ctx gl.Context) (*FBO, error) {
s := &FBO{
c: f,
gl3: gl3,
frameBuffer: f.CreateFramebuffer(),
srgbBuffer: f.CreateFramebuffer(),
colorTex: f.CreateTexture(),
depthBuffer: f.CreateRenderbuffer(),
}
@@ -81,7 +79,6 @@ func (s *FBO) Blit() {
s.c.BufferSubData(gl.ARRAY_BUFFER, 0, coords)
s.blitted = true
}
s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{})
s.c.UseProgram(s.prog)
s.c.BindTexture(gl.TEXTURE_2D, s.colorTex)
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.DisableVertexAttribArray(0)
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.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() {
s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer)
func (s *FBO) Framebuffer() gl.Framebuffer {
return s.srgbBuffer
}
func (s *FBO) Refresh(w, h int) error {
s.width, s.height = w, h
if w == 0 || h == 0 {
func (s *FBO) Refresh(viewport image.Point) error {
if viewport.X == 0 || viewport.Y == 0 {
return errors.New("srgb: zero-sized framebuffer")
}
if s.viewport == viewport {
return nil
}
s.viewport = viewport
s.c.BindTexture(gl.TEXTURE_2D, s.colorTex)
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 */ {
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))
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.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.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, s.depthBuffer)
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" {
@@ -136,9 +133,9 @@ func (s *FBO) Refresh(w, h int) error {
var pixel [4]byte
s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:])
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 {
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() {
s.c.DeleteFramebuffer(s.frameBuffer)
s.c.DeleteFramebuffer(s.srgbBuffer)
s.c.DeleteTexture(s.colorTex)
s.c.DeleteRenderbuffer(s.depthBuffer)
if s.blitted {