gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/gpu/cpu.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package gpu 4 5 import ( 6 "unsafe" 7 8 "gioui.org/cpu" 9 ) 10 11 // This file contains code specific to running compute shaders on the CPU. 12 13 // dispatcher dispatches CPU compute programs across multiple goroutines. 14 type dispatcher struct { 15 // done is notified when a worker completes its work slice. 16 done chan struct{} 17 // work receives work slice indices. It is closed when the dispatcher is released. 18 work chan work 19 // dispatch receives compute jobs, which is then split among workers. 20 dispatch chan dispatch 21 // sync receives notification when a Sync completes. 22 sync chan struct{} 23 } 24 25 type work struct { 26 ctx *cpu.DispatchContext 27 index int 28 } 29 30 type dispatch struct { 31 _type jobType 32 program *cpu.ProgramInfo 33 descSet unsafe.Pointer 34 x, y, z int 35 } 36 37 type jobType uint8 38 39 const ( 40 jobDispatch jobType = iota 41 jobBarrier 42 jobSync 43 ) 44 45 func newDispatcher(workers int) *dispatcher { 46 d := &dispatcher{ 47 work: make(chan work, workers), 48 done: make(chan struct{}, workers), 49 // Leave some room to avoid blocking calls to Dispatch. 50 dispatch: make(chan dispatch, 20), 51 sync: make(chan struct{}), 52 } 53 for i := 0; i < workers; i++ { 54 go d.worker() 55 } 56 go d.dispatcher() 57 return d 58 } 59 60 func (d *dispatcher) dispatcher() { 61 defer close(d.work) 62 var free []*cpu.DispatchContext 63 defer func() { 64 for _, ctx := range free { 65 ctx.Free() 66 } 67 }() 68 var used []*cpu.DispatchContext 69 for job := range d.dispatch { 70 switch job._type { 71 case jobDispatch: 72 if len(free) == 0 { 73 free = append(free, cpu.NewDispatchContext()) 74 } 75 ctx := free[len(free)-1] 76 free = free[:len(free)-1] 77 used = append(used, ctx) 78 ctx.Prepare(cap(d.work), job.program, job.descSet, job.x, job.y, job.z) 79 for i := 0; i < cap(d.work); i++ { 80 d.work <- work{ 81 ctx: ctx, 82 index: i, 83 } 84 } 85 case jobBarrier: 86 // Wait for all outstanding dispatches to complete. 87 for i := 0; i < len(used)*cap(d.work); i++ { 88 <-d.done 89 } 90 free = append(free, used...) 91 used = used[:0] 92 case jobSync: 93 d.sync <- struct{}{} 94 } 95 } 96 } 97 98 func (d *dispatcher) worker() { 99 thread := cpu.NewThreadContext() 100 defer thread.Free() 101 for w := range d.work { 102 w.ctx.Dispatch(w.index, thread) 103 d.done <- struct{}{} 104 } 105 } 106 107 func (d *dispatcher) Barrier() { 108 d.dispatch <- dispatch{_type: jobBarrier} 109 } 110 111 func (d *dispatcher) Sync() { 112 d.dispatch <- dispatch{_type: jobSync} 113 <-d.sync 114 } 115 116 func (d *dispatcher) Dispatch(program *cpu.ProgramInfo, descSet unsafe.Pointer, x, y, z int) { 117 d.dispatch <- dispatch{ 118 _type: jobDispatch, 119 program: program, 120 descSet: descSet, 121 x: x, 122 y: y, 123 z: z, 124 } 125 } 126 127 func (d *dispatcher) Stop() { 128 close(d.dispatch) 129 }