diff --git a/app/internal/wm/d3d11_windows.go b/app/internal/wm/d3d11_windows.go index 78a05cae..d570a87b 100644 --- a/app/internal/wm/d3d11_windows.go +++ b/app/internal/wm/d3d11_windows.go @@ -54,6 +54,13 @@ func (c *d3d11Context) API() gpu.API { return gpu.Direct3D11{Device: unsafe.Pointer(c.dev)} } +func (c *d3d11Context) RenderTarget() gpu.RenderTarget { + return gpu.Direct3D11RenderTarget{ + RenderTarget: unsafe.Pointer(c.renderTarget), + DepthStencilView: unsafe.Pointer(c.depthView), + } +} + func (c *d3d11Context) Present() error { err := c.swchain.Present(1, 0) if err == nil { diff --git a/app/internal/wm/gl_ios.go b/app/internal/wm/gl_ios.go index 4957c0b5..74a8bddc 100644 --- a/app/internal/wm/gl_ios.go +++ b/app/internal/wm/gl_ios.go @@ -65,6 +65,10 @@ func contextAPI() gpu.OpenGL { return gpu.OpenGL{} } +func (c *context) RenderTarget() gpu.RenderTarget { + return gpu.OpenGLRenderTarget(c.frameBuffer) +} + func (c *context) API() gpu.API { return contextAPI() } diff --git a/app/internal/wm/gl_js.go b/app/internal/wm/gl_js.go index 5d2f4b5f..56227235 100644 --- a/app/internal/wm/gl_js.go +++ b/app/internal/wm/gl_js.go @@ -36,6 +36,10 @@ func newContext(w *window) (*context, error) { return c, nil } +func (c *context) RenderTarget() gpu.RenderTarget { + return gpu.OpenGLRenderTarget{} +} + func (c *context) API() gpu.API { return gpu.OpenGL{Context: gl.Context(c.ctx)} } diff --git a/app/internal/wm/gl_macos.go b/app/internal/wm/gl_macos.go index ca7ae71e..cecf5baf 100644 --- a/app/internal/wm/gl_macos.go +++ b/app/internal/wm/gl_macos.go @@ -52,6 +52,10 @@ func newContext(w *window) (*context, error) { return c, nil } +func (c *context) RenderTarget() gpu.RenderTarget { + return gpu.OpenGLRenderTarget{} +} + func (c *context) API() gpu.API { return gpu.OpenGL{} } diff --git a/app/internal/wm/window.go b/app/internal/wm/window.go index 68ebabc4..7c602361 100644 --- a/app/internal/wm/window.go +++ b/app/internal/wm/window.go @@ -68,6 +68,7 @@ type Callbacks interface { type Context interface { API() gpu.API + RenderTarget() gpu.RenderTarget Present() error Refresh() error Release() diff --git a/app/loop.go b/app/loop.go index ae2e45b2..6007df05 100644 --- a/app/loop.go +++ b/app/loop.go @@ -100,7 +100,7 @@ func (l *renderLoop) renderLoop(ctx wm.Context) error { g.Collect(frame.viewport, frame.ops) // Signal that we're done with the frame ops. l.ack <- struct{}{} - res.err = g.Frame() + res.err = g.Frame(ctx.RenderTarget()) if res.err == nil { res.err = ctx.Present() } diff --git a/gpu/api.go b/gpu/api.go index e4f4dd57..19496907 100644 --- a/gpu/api.go +++ b/gpu/api.go @@ -8,6 +8,15 @@ import "gioui.org/gpu/internal/driver" // There is an API type for each supported GPU API such as OpenGL and Direct3D. type API = driver.API +// A RenderTarget denotest the destination framebuffer for a frame. +type RenderTarget = driver.RenderTarget + +// OpenGLRenderTarget is a render target suitable for the OpenGL backend. +type OpenGLRenderTarget = driver.OpenGLRenderTarget + +// Direct3D11RenderTarget is a render target suitable for the Direct3D 11 backend. +type Direct3D11RenderTarget = driver.Direct3D11RenderTarget + // OpenGL denotes the OpenGL or OpenGL ES API. type OpenGL = driver.OpenGL diff --git a/gpu/compute.go b/gpu/compute.go index 50110545..4f69df17 100644 --- a/gpu/compute.go +++ b/gpu/compute.go @@ -551,9 +551,9 @@ func (g *compute) Clear(col color.NRGBA) { g.collector.clearColor = f32color.LinearFromSRGB(col) } -func (g *compute) Frame() error { +func (g *compute) Frame(target RenderTarget) error { viewport := g.viewport - defFBO := g.ctx.BeginFrame(g.collector.clear, viewport) + defFBO := g.ctx.BeginFrame(target, g.collector.clear, viewport) defer g.ctx.EndFrame() t := &g.timers diff --git a/gpu/gpu.go b/gpu/gpu.go index deb6f1b9..9e4a5f3c 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -42,8 +42,8 @@ type GPU interface { Clear(color color.NRGBA) // Collect the graphics operations from frame, given the viewport. Collect(viewport image.Point, frame *op.Ops) - // Frame clears the color buffer and draws the collected operations. - Frame() error + // Frame draws the collected operations to target. + Frame(target RenderTarget) error // Profile returns the last available profiling information. Profiling // information is requested when Collect sees a ProfileOp, and the result // is available through Profile at some later time. @@ -356,7 +356,7 @@ func New(api API) (GPU, error) { if err != nil { return nil, err } - d.BeginFrame(false, image.Point{}) + d.BeginFrame(nil, false, image.Point{}) defer d.EndFrame() forceCompute := os.Getenv("GIORENDERER") == "forcecompute" feats := d.Caps().Features @@ -414,9 +414,9 @@ func (g *gpu) Collect(viewport image.Point, frameOps *op.Ops) { } } -func (g *gpu) Frame() error { +func (g *gpu) Frame(target RenderTarget) error { viewport := g.renderer.blitter.viewport - defFBO := g.ctx.BeginFrame(g.drawOps.clear, viewport) + defFBO := g.ctx.BeginFrame(target, g.drawOps.clear, viewport) defer g.ctx.EndFrame() for _, img := range g.drawOps.imageOps { expandPathOp(img.path, img.clip) diff --git a/gpu/headless/driver_test.go b/gpu/headless/driver_test.go index f23e7b7a..47058503 100644 --- a/gpu/headless/driver_test.go +++ b/gpu/headless/driver_test.go @@ -174,7 +174,7 @@ func newDriver(t *testing.T) driver.Device { if err != nil { t.Fatal(err) } - b.BeginFrame(true, image.Pt(1, 1)) + b.BeginFrame(nil, true, image.Pt(1, 1)) t.Cleanup(func() { b.EndFrame() ctx.ReleaseCurrent() diff --git a/gpu/headless/headless.go b/gpu/headless/headless.go index 6d6bd63b..46cddfa7 100644 --- a/gpu/headless/headless.go +++ b/gpu/headless/headless.go @@ -112,7 +112,7 @@ func (w *Window) Frame(frame *op.Ops) error { w.dev.BindFramebuffer(w.fbo) w.gpu.Clear(color.NRGBA{}) w.gpu.Collect(w.size, frame) - return w.gpu.Frame() + return w.gpu.Frame(driver.RenderTarget(w.fbo)) }) } diff --git a/gpu/internal/d3d11/d3d11_windows.go b/gpu/internal/d3d11/d3d11_windows.go index 60506ea7..318d1155 100644 --- a/gpu/internal/d3d11/d3d11_windows.go +++ b/gpu/internal/d3d11/d3d11_windows.go @@ -165,15 +165,23 @@ func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) { return b, nil } -func (b *Backend) BeginFrame(clear bool, viewport image.Point) driver.Framebuffer { - renderTarget, depthView := b.ctx.OMGetRenderTargets() - // Assume someone else is holding on to the render targets. - if renderTarget != nil { - d3d11.IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.Vtbl.Release) - } - if depthView != nil { - d3d11.IUnknownRelease(unsafe.Pointer(depthView), depthView.Vtbl.Release) +func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Framebuffer { + var ( + renderTarget *d3d11.RenderTargetView + depthView *d3d11.DepthStencilView + ) + if target != nil { + switch t := target.(type) { + case driver.Direct3D11RenderTarget: + renderTarget = (*d3d11.RenderTargetView)(t.RenderTarget) + depthView = (*d3d11.DepthStencilView)(t.DepthStencilView) + case *Framebuffer: + renderTarget, depthView = t.renderTarget, t.depthView + default: + panic(fmt.Errorf("opengl: invalid render target type: %T", target)) + } } + b.ctx.OMSetRenderTargets(renderTarget, depthView) return &Framebuffer{ctx: b.ctx, dev: b.dev, renderTarget: renderTarget, depthView: depthView, foreign: true} } @@ -713,6 +721,8 @@ func (f *Framebuffer) Release() { } } +func (f *Framebuffer) ImplementsRenderTarget() {} + func (b *Backend) BindInputLayout(layout driver.InputLayout) { b.ctx.IASetInputLayout(layout.(*InputLayout).layout) } diff --git a/gpu/internal/driver/api.go b/gpu/internal/driver/api.go index c15fb897..10a4cf2c 100644 --- a/gpu/internal/driver/api.go +++ b/gpu/internal/driver/api.go @@ -15,6 +15,19 @@ type API interface { implementsAPI() } +type RenderTarget interface { + ImplementsRenderTarget() +} + +type OpenGLRenderTarget gl.Framebuffer + +type Direct3D11RenderTarget struct { + // RenderTarget is a *ID3D11RenderTargetView. + RenderTarget unsafe.Pointer + // DepthStencilView is a *ID3D11DepthStencilView. + DepthStencilView unsafe.Pointer +} + type OpenGL struct { // ES forces the use of ANGLE OpenGL ES libraries on macOS. It is // ignored on all other platforms. @@ -55,5 +68,7 @@ func NewDevice(api API) (Device, error) { return nil, fmt.Errorf("driver: no driver available for the API %T", api) } -func (OpenGL) implementsAPI() {} -func (Direct3D11) implementsAPI() {} +func (OpenGL) implementsAPI() {} +func (Direct3D11) implementsAPI() {} +func (OpenGLRenderTarget) ImplementsRenderTarget() {} +func (Direct3D11RenderTarget) ImplementsRenderTarget() {} diff --git a/gpu/internal/driver/driver.go b/gpu/internal/driver/driver.go index a7e58f60..48b5e400 100644 --- a/gpu/internal/driver/driver.go +++ b/gpu/internal/driver/driver.go @@ -12,7 +12,7 @@ import ( // APIs such as OpenGL, Direct3D useful for rendering Gio // operations. type Device interface { - BeginFrame(clear bool, viewport image.Point) Framebuffer + BeginFrame(target RenderTarget, clear bool, viewport image.Point) Framebuffer EndFrame() Caps() Caps NewTimer() Timer @@ -155,6 +155,7 @@ type Buffer interface { } type Framebuffer interface { + RenderTarget Invalidate() Release() ReadPixels(src image.Rectangle, pixels []byte) error diff --git a/gpu/internal/opengl/opengl.go b/gpu/internal/opengl/opengl.go index 5ce3e2cb..8f2cfc07 100644 --- a/gpu/internal/opengl/opengl.go +++ b/gpu/internal/opengl/opengl.go @@ -212,12 +212,23 @@ func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) { return b, nil } -func (b *Backend) BeginFrame(clear bool, viewport image.Point) driver.Framebuffer { +func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Framebuffer { b.clear = clear b.glstate = b.queryState() b.savedState = b.glstate b.state = state{} - renderFBO := b.glstate.drawFBO + var renderFBO gl.Framebuffer + if target != nil { + switch t := target.(type) { + case driver.OpenGLRenderTarget: + renderFBO = gl.Framebuffer(t) + case *gpuFramebuffer: + renderFBO = t.obj + default: + panic(fmt.Errorf("opengl: invalid render target type: %T", target)) + } + } + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO) if b.gles { // If the output framebuffer is not in the sRGB colorspace already, emulate it. var fbEncoding int @@ -1244,6 +1255,8 @@ func (f *gpuFramebuffer) Release() { } } +func (f *gpuFramebuffer) ImplementsRenderTarget() {} + func toTexFilter(f driver.TextureFilter) int { switch f { case driver.FilterNearest: diff --git a/internal/egl/egl.go b/internal/egl/egl.go index 7452b62c..772841f5 100644 --- a/internal/egl/egl.go +++ b/internal/egl/egl.go @@ -98,6 +98,10 @@ func NewContext(disp NativeDisplayType) (*Context, error) { return c, nil } +func (c *Context) RenderTarget() gpu.RenderTarget { + return gpu.OpenGLRenderTarget{} +} + func (c *Context) API() gpu.API { return gpu.OpenGL{} }