gpu,gpu/headless,app/internal/wm: add explicit RenderTarget API

Both the OpenGL and the Direct3D API are stateful and gpu.GPU renders to
the render target current when Frame is called.

Modern GPU API such as Metal don't have a concept of a current render
target, and the target even changes each frame.

Add RenderTarget and add an explicit target argument to GPU.Frame as
well as the underlying driver.Device.BeginFrame.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-08-04 14:10:07 +02:00
parent 0bdc2e0432
commit 7d84e419c9
16 changed files with 95 additions and 23 deletions
+9
View File
@@ -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
+2 -2
View File
@@ -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
+5 -5
View File
@@ -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)
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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))
})
}
+18 -8
View File
@@ -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)
}
+17 -2
View File
@@ -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() {}
+2 -1
View File
@@ -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
+15 -2
View File
@@ -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: