gpu,gpu/backend: implement generic backend.NewDevice

NewDevice creates a Device given an API, which is the necessary GPU
resources for a backend.

Convert gpu.New to take an API instead of a backend.Device directly.

In turn, this frees us to later unexport the backend package along with
the backend implementations (for now just gioui.org/gpu/gl for OpenGL).
It also allows programs that embed Gio (such as gioui.org/example/glfw)
to freely choose a backend, not just OpenGL.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-03-04 20:51:47 +01:00
parent 0e723fa192
commit 8ff6546285
19 changed files with 139 additions and 65 deletions
+1 -1
View File
@@ -170,7 +170,7 @@ func newBackend(t *testing.T) backend.Device {
if err := ctx.MakeCurrent(); err != nil { if err := ctx.MakeCurrent(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
b, err := ctx.Backend() b, err := backend.NewDevice(ctx.API())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+4 -3
View File
@@ -25,7 +25,7 @@ type Window struct {
} }
type context interface { type context interface {
Backend() (backend.Device, error) API() gpu.API
MakeCurrent() error MakeCurrent() error
ReleaseCurrent() ReleaseCurrent()
Release() Release()
@@ -42,7 +42,8 @@ func NewWindow(width, height int) (*Window, error) {
ctx: ctx, ctx: ctx,
} }
err = contextDo(ctx, func() error { err = contextDo(ctx, func() error {
dev, err := ctx.Backend() api := ctx.API()
dev, err := backend.NewDevice(api)
if err != nil { if err != nil {
return err return err
} }
@@ -62,7 +63,7 @@ func NewWindow(width, height int) (*Window, error) {
fboTex.Release() fboTex.Release()
return err return err
} }
gp, err := gpu.New(dev) gp, err := gpu.New(api)
if err != nil { if err != nil {
fbo.Release() fbo.Release()
fboTex.Release() fboTex.Release()
+5 -7
View File
@@ -3,9 +3,7 @@
package headless package headless
import ( import (
"gioui.org/gpu/backend" "gioui.org/gpu"
"gioui.org/gpu/gl"
_ "gioui.org/app/internal/cocoainit" _ "gioui.org/app/internal/cocoainit"
) )
@@ -32,6 +30,10 @@ func newGLContext() (context, error) {
return &nsContext{ctx: ctx}, nil return &nsContext{ctx: ctx}, nil
} }
func (c *nsContext) API() gpu.API {
return gpu.OpenGL{}
}
func (c *nsContext) MakeCurrent() error { func (c *nsContext) MakeCurrent() error {
C.gio_headless_makeCurrentContext(c.ctx) C.gio_headless_makeCurrentContext(c.ctx)
if !c.prepared { if !c.prepared {
@@ -45,10 +47,6 @@ func (c *nsContext) ReleaseCurrent() {
C.gio_headless_clearCurrentContext(c.ctx) C.gio_headless_clearCurrentContext(c.ctx)
} }
func (c *nsContext) Backend() (backend.Device, error) {
return gl.NewBackend(nil)
}
func (d *nsContext) Release() { func (d *nsContext) Release() {
if d.ctx != 0 { if d.ctx != 0 {
C.gio_headless_releaseContext(d.ctx) C.gio_headless_releaseContext(d.ctx)
+3 -4
View File
@@ -6,8 +6,7 @@ import (
"errors" "errors"
"syscall/js" "syscall/js"
"gioui.org/gpu/backend" "gioui.org/gpu"
"gioui.org/gpu/gl"
"gioui.org/internal/glimpl" "gioui.org/internal/glimpl"
) )
@@ -31,8 +30,8 @@ func newGLContext() (context, error) {
return c, nil return c, nil
} }
func (c *jsContext) Backend() (backend.Device, error) { func (c *jsContext) API() gpu.API {
return gl.NewBackend(glimpl.Context(c.ctx)) return gpu.OpenGL{Context: glimpl.Context(c.ctx)}
} }
func (c *jsContext) Release() { func (c *jsContext) Release() {
+5 -7
View File
@@ -3,8 +3,10 @@
package headless package headless
import ( import (
"unsafe"
"gioui.org/gpu"
"gioui.org/app/internal/d3d11" "gioui.org/app/internal/d3d11"
"gioui.org/gpu/backend"
) )
type d3d11Context struct { type d3d11Context struct {
@@ -19,12 +21,8 @@ func newContext() (context, error) {
return &d3d11Context{dev: dev}, nil return &d3d11Context{dev: dev}, nil
} }
func (c *d3d11Context) Backend() (backend.Device, error) { func (c *d3d11Context) API() gpu.API {
backend, err := d3d11.NewBackend(c.dev.Handle) return gpu.Direct3D11{Device: unsafe.Pointer(c.dev.Handle)}
if err != nil {
return nil, err
}
return backend, nil
} }
func (c *d3d11Context) MakeCurrent() error { func (c *d3d11Context) MakeCurrent() error {
+6 -1
View File
@@ -109,6 +109,10 @@ type SwapChain struct {
fbo *Framebuffer fbo *Framebuffer
} }
func init() {
backend.NewDirect3D11Device = newDirect3D11Device
}
func NewDevice() (*Device, error) { func NewDevice() (*Device, error) {
var flags uint32 var flags uint32
if debug { if debug {
@@ -237,7 +241,8 @@ func (s *SwapChain) Present() error {
return s.swchain.Present(1, 0) return s.swchain.Present(1, 0)
} }
func NewBackend(dev *_ID3D11Device) (*Backend, error) { func newDirect3D11Device(api backend.Direct3D11) (backend.Device, error) {
dev := (*_ID3D11Device)(api.Device)
b := &Backend{ b := &Backend{
dev: dev, dev: dev,
ctx: dev.GetImmediateContext(), ctx: dev.GetImmediateContext(),
+3 -4
View File
@@ -11,8 +11,7 @@ import (
"strings" "strings"
"gioui.org/app/internal/srgb" "gioui.org/app/internal/srgb"
"gioui.org/gpu/backend" "gioui.org/gpu"
"gioui.org/gpu/gl"
"gioui.org/internal/glimpl" "gioui.org/internal/glimpl"
) )
@@ -118,8 +117,8 @@ func NewContext(disp NativeDisplayType) (*Context, error) {
return c, nil return c, nil
} }
func (c *Context) Backend() (backend.Device, error) { func (c *Context) API() gpu.API {
return gl.NewBackend(nil) return gpu.OpenGL{}
} }
func (c *Context) ReleaseSurface() { func (c *Context) ReleaseSurface() {
+5 -3
View File
@@ -3,8 +3,10 @@
package window package window
import ( import (
"unsafe"
"gioui.org/gpu"
"gioui.org/app/internal/d3d11" "gioui.org/app/internal/d3d11"
"gioui.org/gpu/backend"
) )
type d3d11Context struct { type d3d11Context struct {
@@ -34,8 +36,8 @@ func init() {
}) })
} }
func (c *d3d11Context) Backend() (backend.Device, error) { func (c *d3d11Context) API() gpu.API {
return d3d11.NewBackend(c.dev.Handle) return gpu.Direct3D11{Device: unsafe.Pointer(c.dev.Handle)}
} }
func (c *d3d11Context) Present() error { func (c *d3d11Context) Present() error {
+3 -4
View File
@@ -21,8 +21,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"gioui.org/gpu/backend" "gioui.org/gpu"
"gioui.org/gpu/gl"
"gioui.org/internal/glimpl" "gioui.org/internal/glimpl"
) )
@@ -56,8 +55,8 @@ func newContext(w *window) (*context, error) {
return c, nil return c, nil
} }
func (c *context) Backend() (backend.Device, error) { func (c *context) API() gpu.API {
return gl.NewBackend(nil) return gpu.OpenGL{}
} }
func (c *context) Release() { func (c *context) Release() {
+3 -4
View File
@@ -7,8 +7,7 @@ import (
"syscall/js" "syscall/js"
"gioui.org/app/internal/srgb" "gioui.org/app/internal/srgb"
"gioui.org/gpu/backend" "gioui.org/gpu"
"gioui.org/gpu/gl"
"gioui.org/internal/glimpl" "gioui.org/internal/glimpl"
) )
@@ -39,8 +38,8 @@ func newContext(w *window) (*context, error) {
return c, nil return c, nil
} }
func (c *context) Backend() (backend.Device, error) { func (c *context) API() gpu.API {
return gl.NewBackend(glimpl.Context(c.ctx)) return gpu.OpenGL{Context: glimpl.Context(c.ctx)}
} }
func (c *context) Release() { func (c *context) Release() {
+3 -4
View File
@@ -5,8 +5,7 @@
package window package window
import ( import (
"gioui.org/gpu/backend" "gioui.org/gpu"
"gioui.org/gpu/gl"
"gioui.org/internal/glimpl" "gioui.org/internal/glimpl"
) )
@@ -48,8 +47,8 @@ func newContext(w *window) (*context, error) {
return c, nil return c, nil
} }
func (c *context) Backend() (backend.Device, error) { func (c *context) API() gpu.API {
return gl.NewBackend(nil) return gpu.OpenGL{}
} }
func (c *context) Release() { func (c *context) Release() {
+2 -2
View File
@@ -7,7 +7,7 @@ package window
import ( import (
"errors" "errors"
"gioui.org/gpu/backend" "gioui.org/gpu"
"gioui.org/io/event" "gioui.org/io/event"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/system" "gioui.org/io/system"
@@ -33,7 +33,7 @@ type Callbacks interface {
} }
type Context interface { type Context interface {
Backend() (backend.Device, error) API() gpu.API
Present() error Present() error
MakeCurrent() error MakeCurrent() error
Release() Release()
+1 -6
View File
@@ -67,12 +67,7 @@ func (l *renderLoop) renderLoop(ctx window.Context) error {
initErr <- err initErr <- err
return return
} }
b, err := ctx.Backend() g, err := gpu.New(ctx.API())
if err != nil {
initErr <- err
return
}
g, err := gpu.New(b)
if err != nil { if err != nil {
initErr <- err initErr <- err
return return
+3 -2
View File
@@ -7,8 +7,6 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"golang.org/x/sync/errgroup"
"golang.org/x/tools/go/packages"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -18,6 +16,9 @@ import (
"strconv" "strconv"
"strings" "strings"
"text/template" "text/template"
"golang.org/x/sync/errgroup"
"golang.org/x/tools/go/packages"
) )
type androidTools struct { type androidTools struct {
+4 -3
View File
@@ -4,9 +4,6 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"github.com/akavel/rsrc/binutil"
"github.com/akavel/rsrc/coff"
"golang.org/x/text/encoding/unicode"
"image/png" "image/png"
"io" "io"
"math" "math"
@@ -17,6 +14,10 @@ import (
"strconv" "strconv"
"strings" "strings"
"text/template" "text/template"
"github.com/akavel/rsrc/binutil"
"github.com/akavel/rsrc/coff"
"golang.org/x/text/encoding/unicode"
) )
func buildWindows(tmpDir string, bi *buildInfo) error { func buildWindows(tmpDir string, bi *buildInfo) error {
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Unlicense OR MIT
package gpu
import "gioui.org/gpu/backend"
// An API carries the necessary GPU API specific resources to create a Device.
// There is an API type for each supported GPU API such as OpenGL and Direct3D.
type API = backend.API
// OpenGL denotes the OpenGL or OpenGL ES API.
type OpenGL = backend.OpenGL
// Direct3D11 denotes the Direct3D API.
type Direct3D11 = backend.Direct3D11
+56
View File
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Unlicense OR MIT
package backend
import (
"fmt"
"unsafe"
"gioui.org/internal/glimpl"
)
// See gpu/api.go for documentation for the API types
type API interface {
implementsAPI()
}
type OpenGL struct {
// Context contains the WebGL context for WebAssembly platforms. It is
// empty for all other platforms; an OpenGL context is assumed current when
// calling NewDevice.
Context glimpl.Context
}
type Direct3D11 struct {
// Device contains a *ID3D11Device.
Device unsafe.Pointer
}
// API specific device constructors.
var (
NewOpenGLDevice func(api OpenGL) (Device, error)
NewDirect3D11Device func(api Direct3D11) (Device, error)
)
// NewDevice creates a new Device given the api.
//
// Note that the device does not assume ownership of the resources contained in
// api; the caller must ensure the resources are valid until the device is
// released.
func NewDevice(api API) (Device, error) {
switch api := api.(type) {
case OpenGL:
if NewOpenGLDevice != nil {
return NewOpenGLDevice(api)
}
case Direct3D11:
if NewDirect3D11Device != nil {
return NewDirect3D11Device(api)
}
}
return nil, fmt.Errorf("backend: no backend available for the API %T", api)
}
func (OpenGL) implementsAPI() {}
func (Direct3D11) implementsAPI() {}
+6 -6
View File
@@ -123,12 +123,12 @@ const (
storageBindings = 32 storageBindings = 32
) )
// NewBackend returns a new Backend. func init() {
// backend.NewOpenGLDevice = newOpenGLDevice
// Pass a WebGL context if GOOS is "js", otherwise pass nil for the current }
// context.
func NewBackend(ctx Context) (*Backend, error) { func newOpenGLDevice(api backend.OpenGL) (backend.Device, error) {
f, err := glimpl.NewFunctions(ctx) f, err := glimpl.NewFunctions(api.Context)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+11 -4
View File
@@ -28,6 +28,9 @@ import (
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
"gioui.org/op/clip" "gioui.org/op/clip"
// Register backend.
_ "gioui.org/gpu/gl"
) )
type GPU interface { type GPU interface {
@@ -380,14 +383,18 @@ const (
materialTexture materialTexture
) )
func New(ctx backend.Device) (GPU, error) { func New(api API) (GPU, error) {
d, err := backend.NewDevice(api)
if err != nil {
return nil, err
}
forceCompute := os.Getenv("GIORENDERER") == "forcecompute" forceCompute := os.Getenv("GIORENDERER") == "forcecompute"
feats := ctx.Caps().Features feats := d.Caps().Features
switch { switch {
case !forceCompute && feats.Has(backend.FeatureFloatRenderTargets): case !forceCompute && feats.Has(backend.FeatureFloatRenderTargets):
return newGPU(ctx) return newGPU(d)
case feats.Has(backend.FeatureCompute): case feats.Has(backend.FeatureCompute):
return newCompute(ctx) return newCompute(d)
default: default:
return nil, errors.New("gpu: no support for float render targets nor compute") return nil, errors.New("gpu: no support for float render targets nor compute")
} }