Skip to content

Commit fdb620f

Browse files
committed
gpu: major update to arrayn variable creation: actual array size is determined when value buffer is set, and also added ReadOnly flag, so it auto-creates read buffer for all !ReadOnly Storage variables.
1 parent 3e33b22 commit fdb620f

File tree

12 files changed

+197
-75
lines changed

12 files changed

+197
-75
lines changed

goal/gosl/examples/basic/compute.go

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import "cogentcore.org/core/math32"
1212
//gosl:vars
1313
var (
1414
// Params are the parameters for the computation.
15+
//gosl:read-only
1516
Params []ParamStruct
1617

1718
// Data is the data on which the computation operates.

goal/gosl/examples/basic/gosl.go

+137
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

goal/gosl/examples/basic/main.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"runtime"
1313

1414
"cogentcore.org/core/base/timer"
15+
"cogentcore.org/core/gpu"
1516
)
1617

1718
//go:generate gosl .
@@ -22,6 +23,9 @@ func init() {
2223
}
2324

2425
func main() {
26+
gpu.Debug = true
27+
GPUInit()
28+
2529
n := 2000000 // note: not necc to spec up-front, but easier if so
2630

2731
Params = make([]ParamStruct, 1)
@@ -37,8 +41,6 @@ func main() {
3741
sd[i].Raw = Data[i].Raw
3842
}
3943

40-
GPUInit()
41-
4244
cpuTmr := timer.Time{}
4345
cpuTmr.Start()
4446

goal/gosl/examples/rand/main.go

-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ func main() {
7070
gpu.SetValueFrom(cvl, []uint64{seed})
7171
gpu.SetValueFrom(dvl, dataG)
7272

73-
sgp.CreateReadBuffers()
74-
7573
gpuTmr := timer.Time{}
7674
gpuTmr.Start()
7775

goal/gosl/gotosl/gengpu.go

+9-10
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import (
4242
//go:embed %s/*.wgsl
4343
var shaders embed.FS
4444
45-
// GPU is the compute gpu device
45+
// ComputeGPU is the compute gpu device
4646
var ComputeGPU *gpu.GPU
4747
4848
// UseGPU indicates whether to use GPU vs. CPU.
@@ -53,6 +53,7 @@ var UseGPU bool
5353
b.WriteString(fmt.Sprintf(header, st.Config.Output))
5454

5555
for _, sy := range st.Systems {
56+
b.WriteString(fmt.Sprintf("// %s is a GPU compute System with kernels operating on the\nsame set of data variables.\n", st.genSysVar(sy)))
5657
b.WriteString(fmt.Sprintf("var %s *gpu.ComputeSystem\n", st.genSysVar(sy)))
5758
}
5859

@@ -77,8 +78,8 @@ const (
7778
b.WriteString(")\n")
7879

7980
initf := `
80-
// GPUInit initializes the GPU compute system
81-
// Configuring Systems, variables and kernels.
81+
// GPUInit initializes the GPU compute system,
82+
// configuring system(s), variables and kernels.
8283
func GPUInit() {
8384
gp := gpu.NewComputeGPU()
8485
ComputeGPU = gp
@@ -92,7 +93,8 @@ func GPUInit() {
9293
b.WriteString("}\n\n")
9394

9495
release := `
95-
// GPURelease releases the GPU compute system.
96+
// GPURelease releases the GPU compute system resources.
97+
// Call this at program exit.
9698
func GPURelease() {
9799
`
98100

@@ -138,9 +140,6 @@ func (st *State) GenGPUSystemInit(sy *System) string {
138140
b.WriteString(fmt.Sprintf("\t\t\tsgp.AddStruct(%q, int(unsafe.Sizeof(%s{})), len(%s), gpu.ComputeShader)\n", vr.Name, vr.Type[2:], vr.Name))
139141
}
140142
b.WriteString("\t\t\tsgp.SetNValues(1)\n")
141-
if !gp.Uniform {
142-
b.WriteString("\t\t\tsgp.CreateReadBuffers()\n")
143-
}
144143
b.WriteString("\t\t}\n")
145144
}
146145
b.WriteString("\t\tsy.Config()\n")
@@ -157,9 +156,9 @@ func (st *State) GenGPUSystemOps(sy *System) string {
157156

158157
// 1 = kernel, 2 = system, 3 = sysname
159158
run := `
160-
// Run%[1]s runs the %[1]s kernel with given number of items,
161-
// on either the CPU or GPU depending on the UseGPU.
162-
// Pass *Var variable names to sync those variables back from the GPU
159+
// Run%[1]s runs the %[1]s kernel with given number of elements,
160+
// on either the CPU or GPU depending on the UseGPU variable.
161+
// Pass *Var variable enums to sync those variables back from the GPU
163162
// after running (irrelevant for CPU).
164163
func Run%[1]s(n int, syncVars ...GPUVars) {
165164
if UseGPU {

goal/gosl/gotosl/gotosl.go

+5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ type Var struct {
5454

5555
// Type of variable: either []Type or tensor.Float32, tensor.Int32
5656
Type string
57+
58+
// ReadOnly indicates that this variable is never read back from GPU,
59+
// specified by the gosl:read-only property in the variable comments.
60+
// It is important to optimize GPU memory usage to indicate this.
61+
ReadOnly bool
5762
}
5863

5964
// Group represents one variable group.

gpu/examples/compute/compute.go

-2
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@ func main() {
5959
}
6060
gpu.SetValueFrom(dvl, sd)
6161

62-
sgp.CreateReadBuffers()
63-
6462
ce, _ := sy.BeginComputePass()
6563
pl.Dispatch1D(ce, n, threads)
6664
ce.End()

gpu/value.go

+32-7
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,17 @@ type Value struct {
3030
// index of this value within the Var list of values
3131
Index int
3232

33-
// VarSize is the size of each Var element, which includes any fixed ArrayN
33+
// VarSize is the size of each Var element, which includes any fixed Var.ArrayN
3434
// array size specified on the Var.
35+
// The actual buffer size is VarSize * Value.ArrayN (or DynamicN for dynamic).
3536
VarSize int
3637

38+
// ArrayN is the actual number of array elements, for Uniform or Storage
39+
// variables without a fixed array size (i.e., the Var ArrayN = 1).
40+
// This is set when the buffer is actually created, based on the data,
41+
// or can be set directly prior to buffer creation.
42+
ArrayN int
43+
3744
// DynamicIndex is the current index into a DynamicOffset variable
3845
// to use for the SetBindGroup call. Note that this is an index,
3946
// not an offset, so it indexes the DynamicN Vars in the Value,
@@ -112,6 +119,7 @@ func (vl *Value) init(vr *Var, dev *Device, idx int) {
112119
vl.Index = idx
113120
vl.Name = fmt.Sprintf("%s_%d", vr.Name, vl.Index)
114121
vl.VarSize = vr.MemSize()
122+
vl.ArrayN = 1
115123
vl.alignBytes = vr.alignBytes
116124
vl.AlignVarSize = MemSizeAlign(vl.VarSize, vl.alignBytes)
117125
vl.isDynamic = vl.role == Vertex || vl.role == Index || vr.DynamicOffset
@@ -129,11 +137,12 @@ func (vl *Value) MemSize() int {
129137
if vl.isDynamic {
130138
return vl.AlignVarSize * vl.dynamicN
131139
}
132-
return vl.VarSize
140+
return vl.ArrayN * vl.VarSize
133141
}
134142

135143
// CreateBuffer creates the GPU buffer for this value if it does not
136144
// yet exist or is not the right size.
145+
// For !ReadOnly [Storage] buffers, calls [Value.CreateReadBuffer].
137146
func (vl *Value) CreateBuffer() error {
138147
if vl.role == SampledTexture {
139148
return nil
@@ -159,6 +168,9 @@ func (vl *Value) CreateBuffer() error {
159168
}
160169
vl.AllocSize = sz
161170
vl.buffer = buf
171+
if vl.role == Storage && !vl.vvar.ReadOnly {
172+
vl.CreateReadBuffer()
173+
}
162174
return nil
163175
}
164176

@@ -214,6 +226,9 @@ func (vl *Value) SetDynamicN(n int) {
214226

215227
// SetValueFrom copies given values into value buffer memory,
216228
// making the buffer if it has not yet been constructed.
229+
// The actual ArrayN size of Storage or Uniform variables will
230+
// be computed based on the size of the from bytes, relative to
231+
// the variable size.
217232
// IMPORTANT: do not use this for dynamic offset Uniform or
218233
// Storage variables, as the alignment will not be correct;
219234
// See [SetDynamicFromBytes].
@@ -223,6 +238,7 @@ func SetValueFrom[E any](vl *Value, from []E) error {
223238

224239
// SetFromBytes copies given bytes into value buffer memory,
225240
// making the buffer if it has not yet been constructed.
241+
// For !ReadOnly [Storage] buffers, calls [Value.CreateReadBuffer].
226242
// IMPORTANT: do not use this for dynamic offset Uniform or
227243
// Storage variables, as the alignment will not be correct;
228244
// See [SetDynamicFromBytes].
@@ -232,12 +248,19 @@ func (vl *Value) SetFromBytes(from []byte) error {
232248
return errors.Log(err)
233249
}
234250
nb := len(from)
251+
an := nb / vl.VarSize
252+
aover := nb % vl.VarSize
253+
if aover != 0 {
254+
err := fmt.Errorf("gpu.Value SetFromBytes %s, Size passed: %d is not an even multiple of the variable size: %d", vl.Name, nb, vl.VarSize)
255+
return errors.Log(err)
256+
}
235257
if vl.isDynamic { // Vertex, Index at this point
236-
dn := nb / vl.VarSize
237-
vl.SetDynamicN(dn)
258+
vl.SetDynamicN(an)
259+
} else {
260+
vl.ArrayN = an
238261
}
239262
tb := vl.MemSize()
240-
if nb != tb {
263+
if nb != tb { // this should never happen, but justin case
241264
err := fmt.Errorf("gpu.Value SetFromBytes %s, Size passed: %d != Size expected %d", vl.Name, nb, tb)
242265
return errors.Log(err)
243266
}
@@ -254,6 +277,9 @@ func (vl *Value) SetFromBytes(from []byte) error {
254277
}
255278
vl.buffer = buf
256279
vl.AllocSize = nb
280+
if vl.role == Storage && !vl.vvar.ReadOnly {
281+
vl.CreateReadBuffer()
282+
}
257283
} else {
258284
err := vl.device.Queue.WriteBuffer(vl.buffer, 0, from)
259285
if errors.Log(err) != nil {
@@ -406,9 +432,8 @@ func (vl *Value) SetFromTexture(tx *Texture) *Texture {
406432
}
407433

408434
// CreateReadBuffer creates a read buffer for this value,
409-
// if it does not yet exist or is not the right size.
435+
// for [Storage] values only. Automatically called for !ReadOnly.
410436
// Read buffer is needed for reading values back from the GPU.
411-
// Only for Storage role variables.
412437
func (vl *Value) CreateReadBuffer() error {
413438
if !(vl.role == Storage || vl.role == StorageTexture) {
414439
return nil

gpu/values.go

-12
Original file line numberDiff line numberDiff line change
@@ -147,18 +147,6 @@ func (vs *Values) MemSize() int {
147147
return tsz
148148
}
149149

150-
// CreateReadBuffers creates read buffers for all values.
151-
func (vs *Values) CreateReadBuffers() error {
152-
var errs []error
153-
for _, vl := range vs.Values {
154-
err := vl.CreateReadBuffer()
155-
if err != nil {
156-
errs = append(errs, err)
157-
}
158-
}
159-
return errors.Join(errs...)
160-
}
161-
162150
// bindGroupEntry returns the BindGroupEntry for Current
163151
// value for this variable.
164152
func (vs *Values) bindGroupEntry(vr *Var) []wgpu.BindGroupEntry {

0 commit comments

Comments
 (0)