mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 15:45:38 +00:00
ea38195e2e
This change adds a CPU fallback for devices that don't support the old renderer nor have GPU support for compute programs. Most of the hard work is implemented in the gioui.org/cpu module. It uses the SwiftShader project with light modification to output statically compiled CPU .o files for each compute program. The CPU fallback only covers Linux and Android on arm, arm64, amd64 architectures. There is no fundamental reason support can't be extended to other platforms: - macOS and iOS are probably easy, but it's likely that virtually every device has GPU support for compute shaders. - Windows needs a Cgo-less port, or a build constraint to require a C compiler (Gio core doesn't). - FreeBSD and OpenBSD are probably also easy to do because they're so similar to Linux. - The 386 binaries didn't work properly in my tests, so fixes to SwiftShader is probably needed. However, I expect virtually every Intel device can run amd64 binaries. Updates gio#49 Fixes gio#228 Signed-off-by: Elias Naur <mail@eliasnaur.com>
133 lines
2.7 KiB
Go
133 lines
2.7 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package gpu
|
|
|
|
import (
|
|
"runtime"
|
|
"unsafe"
|
|
|
|
"gioui.org/cpu"
|
|
)
|
|
|
|
const supportsCPUCompute = runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" || runtime.GOARCH == "arm"
|
|
|
|
// This file contains code specific to running compute shaders on the CPU.
|
|
|
|
// dispatcher dispatches CPU compute programs across multiple goroutines.
|
|
type dispatcher struct {
|
|
// done is notified when a worker completes its work slice.
|
|
done chan struct{}
|
|
// work receives work slice indices. It is closed when the dispatcher is released.
|
|
work chan work
|
|
// dispatch receives compute jobs, which is then split among workers.
|
|
dispatch chan dispatch
|
|
// sync receives notification when a Sync completes.
|
|
sync chan struct{}
|
|
}
|
|
|
|
type work struct {
|
|
ctx *cpu.DispatchContext
|
|
index int
|
|
}
|
|
|
|
type dispatch struct {
|
|
_type jobType
|
|
program *cpu.ProgramInfo
|
|
descSet unsafe.Pointer
|
|
x, y, z int
|
|
}
|
|
|
|
type jobType uint8
|
|
|
|
const (
|
|
jobDispatch jobType = iota
|
|
jobBarrier
|
|
jobSync
|
|
)
|
|
|
|
func newDispatcher(workers int) *dispatcher {
|
|
d := &dispatcher{
|
|
work: make(chan work, workers),
|
|
done: make(chan struct{}, workers),
|
|
// Leave some room to avoid blocking calls to Dispatch.
|
|
dispatch: make(chan dispatch, 20),
|
|
sync: make(chan struct{}),
|
|
}
|
|
for i := 0; i < workers; i++ {
|
|
go d.worker()
|
|
}
|
|
go d.dispatcher()
|
|
return d
|
|
}
|
|
|
|
func (d *dispatcher) dispatcher() {
|
|
defer close(d.work)
|
|
var free []*cpu.DispatchContext
|
|
defer func() {
|
|
for _, ctx := range free {
|
|
ctx.Free()
|
|
}
|
|
}()
|
|
var used []*cpu.DispatchContext
|
|
for job := range d.dispatch {
|
|
switch job._type {
|
|
case jobDispatch:
|
|
if len(free) == 0 {
|
|
free = append(free, cpu.NewDispatchContext())
|
|
}
|
|
ctx := free[len(free)-1]
|
|
free = free[:len(free)-1]
|
|
used = append(used, ctx)
|
|
ctx.Prepare(cap(d.work), job.program, job.descSet, job.x, job.y, job.z)
|
|
for i := 0; i < cap(d.work); i++ {
|
|
d.work <- work{
|
|
ctx: ctx,
|
|
index: i,
|
|
}
|
|
}
|
|
case jobBarrier:
|
|
// Wait for all outstanding dispatches to complete.
|
|
for i := 0; i < len(used)*cap(d.work); i++ {
|
|
<-d.done
|
|
}
|
|
free = append(free, used...)
|
|
used = used[:0]
|
|
case jobSync:
|
|
d.sync <- struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *dispatcher) worker() {
|
|
thread := cpu.NewThreadContext()
|
|
defer thread.Free()
|
|
for w := range d.work {
|
|
w.ctx.Dispatch(w.index, thread)
|
|
d.done <- struct{}{}
|
|
}
|
|
}
|
|
|
|
func (d *dispatcher) Barrier() {
|
|
d.dispatch <- dispatch{_type: jobBarrier}
|
|
}
|
|
|
|
func (d *dispatcher) Sync() {
|
|
d.dispatch <- dispatch{_type: jobSync}
|
|
<-d.sync
|
|
}
|
|
|
|
func (d *dispatcher) Dispatch(program *cpu.ProgramInfo, descSet unsafe.Pointer, x, y, z int) {
|
|
d.dispatch <- dispatch{
|
|
_type: jobDispatch,
|
|
program: program,
|
|
descSet: descSet,
|
|
x: x,
|
|
y: y,
|
|
z: z,
|
|
}
|
|
}
|
|
|
|
func (d *dispatcher) Stop() {
|
|
close(d.dispatch)
|
|
}
|