-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathsystem.go
509 lines (444 loc) · 16.8 KB
/
system.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
// Copyright (c) 2022, Cogent Core. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vgpu
import (
"fmt"
"log"
vk "github.com/goki/vulkan"
)
// System manages a system of Pipelines that all share
// a common collection of Vars, Values, and a Memory manager.
// For example, this could be a collection of different
// pipelines for different material types, or different
// compute operations performed on a common set of data.
// It maintains its own logical device and associated queue.
type System struct {
// optional name of this System
Name string
// gpu device
GPU *GPU
// logical device for this System, which is a non-owned copy of either Surface or RenderFrame device
Device Device
// cmd pool specific to this system
CmdPool CmdPool
// if true, this is a compute system -- otherwise is graphics
Compute bool
// if true, variables are statically bound to specific offsets in memory buffers, vs. dynamically bound offsets. Typically a compute shader operating on fixed data variables can use static binding, while graphics (e.g., vphong) requires dynamic binding to efficiently use the same shader code for multiple different values of the same variable type
StaticVars bool
// all pipelines
Pipelines []*Pipeline
// map of all pipelines -- names must be unique
PipelineMap map[string]*Pipeline
// map of events for synchronizing processing within a single command stream -- this is the best method for compute shaders to coordinate within a given sequence of shader runs in a single command stream
Events map[string]vk.Event
// map of semaphores for GPU-side sync between different submitted commands -- names must be unique -- note: better to use Events within one command if possible.
Semaphores map[string]vk.Semaphore
// map of fences for CPU-GPU sync -- names must be unique. WaitIdle implictly uses a fence so it is not essential to use this for simple wait case
Fences map[string]vk.Fence
// map of command buffers, for persistent recorded commands -- names must be unique
CmdBuffs map[string]vk.CommandBuffer
// manages all the memory for all the Values
Mem Memory
// renderpass with depth buffer for this system
Render Render
// number of textures at point of last system Config.
// pipelines are not rebuilt if this is the same.
configNTextures int
}
// InitGraphics initializes the System for graphics use, using
// the graphics device from the Surface associated with this system
// or another device can be initialized by calling
// sy.Device.Init(gp, vk.QueueGraphicsBit)
func (sy *System) InitGraphics(gp *GPU, name string, dev *Device) error {
sy.GPU = gp
sy.Render.Sys = sy
sy.Name = name
sy.Compute = false
sy.Device = *dev
sy.InitCmd()
sy.Mem.Init(gp, &sy.Device)
return nil
}
// InitCompute initializes the System for compute functionality,
// which creates its own Compute device.
func (sy *System) InitCompute(gp *GPU, name string) error {
sy.GPU = gp
sy.Render.Sys = sy
sy.Name = name
sy.Compute = true
sy.Device.Init(gp, vk.QueueComputeBit)
sy.InitCmd()
sy.Mem.Init(gp, &sy.Device)
sy.NewFence("ComputeWait") // always have this named fence avail for wait
return nil
}
// InitCmd initializes the command pool and buffer
func (sy *System) InitCmd() {
sy.CmdPool.ConfigResettable(&sy.Device)
sy.CmdPool.NewBuffer(&sy.Device)
}
// Vars returns a pointer to the vars for this pipeline, which has vals within it
func (sy *System) Vars() *Vars {
return &sy.Mem.Vars
}
func (sy *System) Destroy() {
for _, ev := range sy.Events {
vk.DestroyEvent(sy.Device.Device, ev, nil)
}
sy.Events = nil
for _, sp := range sy.Semaphores {
vk.DestroySemaphore(sy.Device.Device, sp, nil)
}
sy.Semaphores = nil
for _, fc := range sy.Fences {
vk.DestroyFence(sy.Device.Device, fc, nil)
}
sy.Fences = nil
sy.CmdBuffs = nil
for _, pl := range sy.Pipelines {
pl.Destroy()
}
sy.Pipelines = nil
sy.CmdPool.Destroy(sy.Device.Device)
sy.Mem.Destroy(sy.Device.Device)
if sy.Compute {
sy.Device.Destroy()
} else {
sy.Render.Destroy()
}
sy.GPU = nil
}
// AddPipeline adds given pipeline
func (sy *System) AddPipeline(pl *Pipeline) {
if sy.PipelineMap == nil {
sy.PipelineMap = make(map[string]*Pipeline)
}
sy.Pipelines = append(sy.Pipelines, pl)
sy.PipelineMap[pl.Name] = pl
}
// NewPipeline returns a new pipeline added to this System,
// initialized for use in this system.
func (sy *System) NewPipeline(name string) *Pipeline {
pl := &Pipeline{Name: name}
pl.Init(sy)
sy.AddPipeline(pl)
return pl
}
// PipelineByNameTry returns pipeline by name with error for not found
func (sy *System) PipelineByNameTry(name string) (*Pipeline, error) {
pl, ok := sy.PipelineMap[name]
if !ok {
err := fmt.Errorf("Pipeline named: %s not found", name)
log.Println(err)
return nil, err
}
return pl, nil
}
// NewSemaphore returns a new semaphore using system device
func (sy *System) NewSemaphore(name string) vk.Semaphore {
sp := NewSemaphore(sy.Device.Device)
if sy.Semaphores == nil {
sy.Semaphores = make(map[string]vk.Semaphore)
}
sy.Semaphores[name] = sp
return sp
}
// SemaphoreByNameTry returns semaphore by name with error for not found
func (sy *System) SemaphoreByNameTry(name string) (vk.Semaphore, error) {
sp, ok := sy.Semaphores[name]
if !ok {
err := fmt.Errorf("Semaphore named: %s not found", name)
log.Println(err)
return vk.NullSemaphore, err
}
return sp, nil
}
// NewEvent returns a new event using system device
func (sy *System) NewEvent(name string) vk.Event {
sp := NewEvent(sy.Device.Device)
if sy.Events == nil {
sy.Events = make(map[string]vk.Event)
}
sy.Events[name] = sp
return sp
}
// EventByNameTry returns event by name with error for not found
func (sy *System) EventByNameTry(name string) (vk.Event, error) {
sp, ok := sy.Events[name]
if !ok {
err := fmt.Errorf("Event named: %s not found", name)
log.Println(err)
return vk.NullEvent, err
}
return sp, nil
}
// NewFence returns a new fence using system device
func (sy *System) NewFence(name string) vk.Fence {
sp := NewFence(sy.Device.Device)
if sy.Fences == nil {
sy.Fences = make(map[string]vk.Fence)
}
sy.Fences[name] = sp
return sp
}
// FenceByNameTry returns fence by name with error for not found
func (sy *System) FenceByNameTry(name string) (vk.Fence, error) {
sp, ok := sy.Fences[name]
if !ok {
err := fmt.Errorf("Fence named: %s not found", name)
log.Println(err)
return vk.NullFence, err
}
return sp, nil
}
// NewCmdBuff returns a new fence using system device
func (sy *System) NewCmdBuff(name string) vk.CommandBuffer {
cb := sy.CmdPool.NewBuffer(&sy.Device)
if sy.CmdBuffs == nil {
sy.CmdBuffs = make(map[string]vk.CommandBuffer)
}
sy.CmdBuffs[name] = cb
return cb
}
// CmdBuffByNameTry returns fence by name with error for not found
func (sy *System) CmdBuffByNameTry(name string) (vk.CommandBuffer, error) {
cb, ok := sy.CmdBuffs[name]
if !ok {
err := fmt.Errorf("CmdBuff named: %s not found", name)
// log.Println(err)
return nil, err
}
return cb, nil
}
// ConfigRender configures the renderpass, including the image
// format that we're rendering to, for a surface render target,
// and the depth buffer format (pass UndefinedType for no depth buffer).
func (sy *System) ConfigRender(imgFmt *ImageFormat, depthFmt Types) {
sy.Render.Config(sy.Device.Device, imgFmt, depthFmt, false)
}
// ConfigRenderNonSurface configures the renderpass, including the image
// format that we're rendering to, for a RenderFrame non-surface target,
// and the depth buffer format (pass UndefinedType for no depth buffer).
func (sy *System) ConfigRenderNonSurface(imgFmt *ImageFormat, depthFmt Types) {
sy.Render.Config(sy.Device.Device, imgFmt, depthFmt, true)
}
// Config configures the entire system, after everything has been
// setup (Pipelines, Vars, etc). Memory / Values do not yet need to
// be initialized but are allocated from the Vars on this call,
// so the total number of Values per Var, and number of VarSets,
// all must be configured.
func (sy *System) Config() {
sy.Mem.Vars.StaticVars = sy.StaticVars
sy.Mem.Config(sy.Device.Device)
if sy.StaticVars {
sy.Mem.Vars.BindStatVarsAll()
} else {
sy.Mem.Vars.BindDynVarsAll()
}
ntextures := sy.Mem.Vars.NTextures
rebuild := ntextures != sy.configNTextures
if Debug {
fmt.Printf("%s\n", sy.Vars().StringDoc())
}
for _, pl := range sy.Pipelines {
pl.Config(rebuild)
}
sy.configNTextures = ntextures
}
//////////////////////////////////////////////////////////////
// Set graphics options
// SetGraphicsDefaults configures all the default settings for all
// graphics rendering pipelines (not for a compute pipeline)
func (sy *System) SetGraphicsDefaults() {
for _, pl := range sy.Pipelines {
pl.SetGraphicsDefaults()
}
sy.SetClearColor(0, 0, 0, 1)
sy.SetClearDepthStencil(1, 0)
}
// SetTopology sets the topology of vertex position data.
// TriangleList is the default.
// Also for Strip modes, restartEnable allows restarting a new
// strip by inserting a ??
// For all pipelines, to keep graphics settings consistent.
func (sy *System) SetTopology(topo Topologies, restartEnable bool) {
for _, pl := range sy.Pipelines {
pl.SetTopology(topo, restartEnable)
}
}
// SetRasterization sets various options for how to rasterize shapes:
// Defaults are: vk.PolygonModeFill, vk.CullModeBackBit, vk.FrontFaceCounterClockwise, 1.0
// For all pipelines, to keep graphics settings consistent.
func (sy *System) SetRasterization(polygonMode vk.PolygonMode, cullMode vk.CullModeFlagBits, frontFace vk.FrontFace, lineWidth float32) {
for _, pl := range sy.Pipelines {
pl.SetRasterization(polygonMode, cullMode, frontFace, lineWidth)
}
}
// SetCullFace sets the face culling mode: true = back, false = front
// use CullBack, CullFront constants
func (sy *System) SetCullFace(back bool) {
for _, pl := range sy.Pipelines {
pl.SetCullFace(back)
}
}
// SetFrontFace sets the winding order for what counts as a front face
// true = CCW, false = CW
func (sy *System) SetFrontFace(ccw bool) {
for _, pl := range sy.Pipelines {
pl.SetFrontFace(ccw)
}
}
// SetLineWidth sets the rendering line width -- 1 is default.
func (sy *System) SetLineWidth(lineWidth float32) {
for _, pl := range sy.Pipelines {
pl.SetLineWidth(lineWidth)
}
}
// SetColorBlend determines the color blending function:
// either 1-source alpha (alphaBlend) or no blending:
// new color overwrites old. Default is alphaBlend = true
// For all pipelines, to keep graphics settings consistent.
func (sy *System) SetColorBlend(alphaBlend bool) {
for _, pl := range sy.Pipelines {
pl.SetColorBlend(alphaBlend)
}
}
// SetClearColor sets the RGBA colors to set when starting new render
// For all pipelines, to keep graphics settings consistent.
func (sy *System) SetClearColor(r, g, b, a float32) {
sy.Render.SetClearColor(r, g, b, a)
}
// SetClearDepthStencil sets the depth and stencil values when starting new render
// For all pipelines, to keep graphics settings consistent.
func (sy *System) SetClearDepthStencil(depth float32, stencil uint32) {
sy.Render.SetClearDepthStencil(depth, stencil)
}
//////////////////////////////////////////////////////////////////////////
// Rendering
// CmdBindVars adds command to the given command buffer
// to bind the Vars descriptors, for given collection of descriptors descIndex
// (see Vars NDescs for info).
func (sy *System) CmdBindVars(cmd vk.CommandBuffer, descIndex int) {
vars := sy.Vars()
if len(vars.SetMap) == 0 {
return
}
vars.BindDescIndex = descIndex
dset := vars.VkDescSets[descIndex]
doff := vars.DynOffs[descIndex]
if sy.Compute {
if sy.StaticVars {
vk.CmdBindDescriptorSets(cmd, vk.PipelineBindPointCompute, vars.VkDescLayout,
0, uint32(len(dset)), dset, 0, nil)
} else {
vk.CmdBindDescriptorSets(cmd, vk.PipelineBindPointCompute, vars.VkDescLayout,
0, uint32(len(dset)), dset, uint32(len(doff)), doff)
}
} else {
vk.CmdBindDescriptorSets(cmd, vk.PipelineBindPointGraphics, vars.VkDescLayout,
0, uint32(len(dset)), dset, uint32(len(doff)), doff)
}
}
// CmdBindTextureVarIndex returns the txIndex needed to select the given Texture value
// at valIndex in given variable in given set index, for use in a shader (i.e., pass
// txIndex as a push constant to the shader to select this texture). If there are
// more than MaxTexturesPerSet textures, then it may need to select a different
// descIndex where that val has been allocated -- the descIndex is returned, and
// switched is true if it had to issue a CmdBindVars to given command buffer
// to bind to that desc set, updating BindDescIndex. Typically other vars are
// bound to the same vals across sets, so this should not affect them, but
// that is not necessarily the case, so other steps might need to be taken.
// If the texture is not valid, a -1 is returned for txIndex, and an error is logged.
func (sy *System) CmdBindTextureVarIndex(cmd vk.CommandBuffer, setIndex int, varNm string, valIndex int) (txIndex, descIndex int, switched bool, err error) {
vars := sy.Vars()
txv, _, _ := vars.ValueByIndexTry(setIndex, varNm, valIndex)
descIndex = valIndex / MaxTexturesPerSet
if descIndex != vars.BindDescIndex {
sy.CmdBindVars(cmd, descIndex)
vars.BindDescIndex = descIndex
switched = true
}
stIndex := descIndex * MaxTexturesPerSet
txIndex = txv.TextureValidIndex(stIndex, valIndex)
if txIndex < 0 {
err = fmt.Errorf("vgpu.CmdBindTextureVarIndex: Texture var %s image val at index %d (starting at idx: %d) is not valid", varNm, valIndex, stIndex)
log.Println(err) // this is always bad
}
return
}
// CmdResetBindVars adds command to the given command buffer
// to bind the Vars descriptors, for given collection of descriptors descIndex
// (see Vars NDescs for info).
func (sy *System) CmdResetBindVars(cmd vk.CommandBuffer, descIndex int) {
CmdResetBegin(cmd)
sy.CmdBindVars(cmd, descIndex)
}
// BeginRenderPass adds commands to the given command buffer
// to start the render pass on given framebuffer.
// Clears the frame first, according to the ClearValues.
// Also Binds descriptor sets to command buffer for given collection
// of descriptors descIndex (see Vars NDescs for info).
func (sy *System) BeginRenderPass(cmd vk.CommandBuffer, fr *Framebuffer, descIndex int) {
sy.CmdBindVars(cmd, descIndex)
sy.Render.BeginRenderPass(cmd, fr)
}
// ResetBeginRenderPass adds commands to the given command buffer
// to reset command buffer and call begin on it, then starts
// the render pass on given framebuffer (BeginRenderPass)
// Clears the frame first, according to the ClearValues.
// Also Binds descriptor sets to command buffer for given collection
// of descriptors descIndex (see Vars NDescs for info).
func (sy *System) ResetBeginRenderPass(cmd vk.CommandBuffer, fr *Framebuffer, descIndex int) {
CmdResetBegin(cmd)
sy.BeginRenderPass(cmd, fr, descIndex)
}
// BeginRenderPassNoClear adds commands to the given command buffer
// to start the render pass on given framebuffer.
// does NOT clear the frame first -- loads prior state.
// Also Binds descriptor sets to command buffer for given collection
// of descriptors descIndex (see Vars NDescs for info).
func (sy *System) BeginRenderPassNoClear(cmd vk.CommandBuffer, fr *Framebuffer, descIndex int) {
sy.CmdBindVars(cmd, descIndex)
sy.Render.BeginRenderPassNoClear(cmd, fr)
}
// ResetBeginRenderPassNoClear adds commands to the given command buffer
// to reset command buffer and call begin on it, then starts
// the render pass on given framebuffer (BeginRenderPass)
// does NOT clear the frame first -- loads prior state.
// Also Binds descriptor sets to command buffer for given collection
// of descriptors descIndex (see Vars NDescs for info).
func (sy *System) ResetBeginRenderPassNoClear(cmd vk.CommandBuffer, fr *Framebuffer, descIndex int) {
CmdResetBegin(cmd)
sy.BeginRenderPassNoClear(cmd, fr, descIndex)
}
// EndRenderPass adds commands to the given command buffer
// to end the render pass. It does not call EndCommandBuffer,
// in case any further commands are to be added.
func (sy *System) EndRenderPass(cmd vk.CommandBuffer) {
// Note that ending the renderpass changes the image's layout from
// vk.ImageLayoutColorAttachmentOptimal to vk.ImageLayoutPresentSrc
vk.CmdEndRenderPass(cmd)
}
/////////////////////////////////////////////
// Memory utils
// MemCmdStart starts a one-time memory command using the
// Memory CmdPool and Device associated with this System
// Use this for other random memory transfer commands.
func (sy *System) MemCmdStart() vk.CommandBuffer {
cmd := sy.Mem.CmdPool.NewBuffer(&sy.Device)
CmdBeginOneTime(cmd)
return cmd
}
// MemCmdEndSubmitWaitFree submits current one-time memory command
// using the Memory CmdPool and Device associated with this System
// Use this for other random memory transfer commands.
func (sy *System) MemCmdEndSubmitWaitFree() {
sy.Mem.CmdPool.EndSubmitWaitFree(&sy.Device)
}
/////////////////////////////////////////////
// Compute utils
// CmdSubmitWait does SubmitWait on CmdPool
func (sy *System) CmdSubmitWait() {
sy.CmdPool.SubmitWait(&sy.Device)
}