diff --git a/app/headless/headless.go b/app/headless/headless.go index ee6682cf..04b3e9dc 100644 --- a/app/headless/headless.go +++ b/app/headless/headless.go @@ -14,13 +14,15 @@ import ( // Window is a headless window. type Window struct { - size image.Point - ctx backend - gpu *gpu.GPU + size image.Point + ctx backend + backend gpu.Backend + gpu *gpu.GPU + fboTex gpu.Texture + fbo gpu.Framebuffer } type backend interface { - Screenshot(width, height int, pixels []byte) error Backend() (gpu.Backend, error) MakeCurrent() error ReleaseCurrent() @@ -40,18 +42,38 @@ func NewWindow(width, height int) (*Window, error) { err = contextDo(ctx, func() error { backend, err := ctx.Backend() if err != nil { - ctx.Release() return err } - gpu, err := gpu.New(backend) + fboTex, err := backend.NewTexture( + gpu.TextureFormatSRGB, + width, height, + gpu.FilterNearest, gpu.FilterNearest, + gpu.BufferBindingFramebuffer, + ) if err != nil { - ctx.Release() + return nil + } + const depthBits = 16 + fbo, err := backend.NewFramebuffer(fboTex, depthBits) + if err != nil { + fboTex.Release() return err } - w.gpu = gpu + fbo.Bind() + gp, err := gpu.New(backend) + if err != nil { + fbo.Release() + fboTex.Release() + return err + } + w.fboTex = fboTex + w.fbo = fbo + w.gpu = gp + w.backend = backend return err }) if err != nil { + ctx.Release() return nil, err } return w, nil @@ -60,6 +82,14 @@ func NewWindow(width, height int) (*Window, error) { // Release resources associated with the window. func (w *Window) Release() { contextDo(w.ctx, func() error { + if w.fbo != nil { + w.fbo.Release() + w.fbo = nil + } + if w.fboTex != nil { + w.fboTex.Release() + w.fboTex = nil + } if w.gpu != nil { w.gpu.Release() w.gpu = nil @@ -87,7 +117,10 @@ func (w *Window) Frame(frame *op.Ops) { func (w *Window) Screenshot() (*image.RGBA, error) { img := image.NewRGBA(image.Rectangle{Max: w.size}) contextDo(w.ctx, func() error { - return w.ctx.Screenshot(w.size.X, w.size.Y, img.Pix) + return w.fbo.ReadPixels( + image.Rectangle{ + Max: image.Point{X: w.size.X, Y: w.size.Y}, + }, img.Pix) }) // Flip image in y-direction. OpenGL's origin is in the lower // left corner. diff --git a/app/headless/headless_darwin.go b/app/headless/headless_darwin.go index 46342b37..d569d4df 100644 --- a/app/headless/headless_darwin.go +++ b/app/headless/headless_darwin.go @@ -22,7 +22,7 @@ type nsContext struct { prepared bool } -func newGLContext() (glContext, error) { +func newGLContext() (backend, error) { ctx := C.gio_headless_newContext() return &nsContext{ctx: ctx, c: new(glimpl.Functions)}, nil } diff --git a/app/headless/headless_egl.go b/app/headless/headless_egl.go index 10817e1a..cc12b78f 100644 --- a/app/headless/headless_egl.go +++ b/app/headless/headless_egl.go @@ -8,6 +8,6 @@ import ( "gioui.org/app/internal/egl" ) -func newGLContext() (glContext, error) { +func newGLContext() (backend, error) { return egl.NewContext(egl.EGL_DEFAULT_DISPLAY) } diff --git a/app/headless/headless_gl.go b/app/headless/headless_gl.go index 450a0f4f..73c62124 100644 --- a/app/headless/headless_gl.go +++ b/app/headless/headless_gl.go @@ -2,72 +2,6 @@ package headless -import ( - "fmt" - "runtime" - - "gioui.org/app/internal/glimpl" - "gioui.org/app/internal/srgb" - "gioui.org/gpu" - "gioui.org/gpu/gl" -) - -type glContext interface { - Functions() *glimpl.Functions - Backend() (gpu.Backend, error) - MakeCurrent() error - ReleaseCurrent() - Release() -} - -type glBackend struct { - glContext - srgb *srgb.FBO -} - func newContext(width, height int) (backend, error) { - glctx, err := newGLContext() - if err != nil { - return nil, err - } - // Create the back buffer FBO after locking the thread and making - // the context current. - runtime.LockOSThread() - defer runtime.UnlockOSThread() - if err := glctx.MakeCurrent(); err != nil { - glctx.Release() - return nil, err - } - defer glctx.ReleaseCurrent() - fbo, err := srgb.New(glctx.Functions()) - if err != nil { - glctx.Release() - return nil, err - } - if err := fbo.Refresh(width, height); err != nil { - fbo.Release() - glctx.Release() - return nil, err - } - return &glBackend{glctx, fbo}, nil -} - -func (b *glBackend) Screenshot(width, height int, pixels []byte) error { - if len(pixels) != width*height*4 { - panic("unexpected RGBA size") - } - f := b.Functions() - f.ReadPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels) - if glErr := f.GetError(); glErr != gl.NO_ERROR { - return fmt.Errorf("glReadPixels failed: %d", glErr) - } - return nil -} - -func (b *glBackend) Release() { - if b.srgb != nil { - b.srgb.Release() - b.srgb = nil - } - b.glContext.Release() + return newGLContext() } diff --git a/app/headless/headless_js.go b/app/headless/headless_js.go index 4264ef5f..ec03f43c 100644 --- a/app/headless/headless_js.go +++ b/app/headless/headless_js.go @@ -16,7 +16,7 @@ type jsContext struct { f *glimpl.Functions } -func newGLContext() (glContext, error) { +func newGLContext() (backend, error) { version := 2 doc := js.Global().Get("document") cnv := doc.Call("createElement", "canvas")