-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathqueue.go
More file actions
152 lines (128 loc) · 4.09 KB
/
queue.go
File metadata and controls
152 lines (128 loc) · 4.09 KB
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
package wgpu
import (
"fmt"
"sync/atomic"
"time"
"github.com/gogpu/wgpu/hal"
)
// defaultSubmitTimeout is the maximum time to wait for GPU work to complete
// after submitting command buffers. 30 seconds accommodates heavy compute workloads.
const defaultSubmitTimeout = 30 * time.Second
// Queue handles command submission and data transfers.
type Queue struct {
hal hal.Queue
halDevice hal.Device
fence hal.Fence
fenceValue atomic.Uint64
device *Device
}
// Submit submits command buffers for execution.
// This is a synchronous operation - it blocks until the GPU has completed all submitted work.
func (q *Queue) Submit(commandBuffers ...*CommandBuffer) error {
if q.hal == nil {
return fmt.Errorf("wgpu: queue not available")
}
halBuffers := make([]hal.CommandBuffer, len(commandBuffers))
for i, cb := range commandBuffers {
if cb == nil {
return fmt.Errorf("wgpu: command buffer at index %d is nil", i)
}
halBuffers[i] = cb.halBuffer()
}
nextValue := q.fenceValue.Add(1)
err := q.hal.Submit(halBuffers, q.fence, nextValue)
if err != nil {
return fmt.Errorf("wgpu: submit failed: %w", err)
}
_, err = q.halDevice.Wait(q.fence, nextValue, defaultSubmitTimeout)
if err != nil {
return fmt.Errorf("wgpu: wait failed: %w", err)
}
for _, cb := range commandBuffers {
raw := cb.halBuffer()
if raw != nil {
q.halDevice.FreeCommandBuffer(raw)
}
}
return nil
}
// SubmitWithFence submits command buffers for execution with fence-based tracking.
// Unlike Submit, this method does NOT block until GPU completion. Instead, it
// signals the provided fence with submissionIndex when the work completes.
// The caller is responsible for polling or waiting on the fence.
//
// If fence is nil, the submission proceeds without fence signaling.
// Command buffers must be freed by the caller after the fence signals
// (use Device.FreeCommandBuffer).
func (q *Queue) SubmitWithFence(commandBuffers []*CommandBuffer, fence *Fence, submissionIndex uint64) error {
if q.hal == nil {
return fmt.Errorf("wgpu: queue not available")
}
halBuffers := make([]hal.CommandBuffer, len(commandBuffers))
for i, cb := range commandBuffers {
if cb == nil {
return fmt.Errorf("wgpu: command buffer at index %d is nil", i)
}
halBuffers[i] = cb.halBuffer()
}
var halFence hal.Fence
if fence != nil {
halFence = fence.hal
}
err := q.hal.Submit(halBuffers, halFence, submissionIndex)
if err != nil {
return fmt.Errorf("wgpu: submit failed: %w", err)
}
return nil
}
// WriteBuffer writes data to a buffer.
func (q *Queue) WriteBuffer(buffer *Buffer, offset uint64, data []byte) error {
if q.hal == nil || buffer == nil {
return fmt.Errorf("wgpu: WriteBuffer: queue or buffer is nil")
}
halBuffer := buffer.halBuffer()
if halBuffer == nil {
return fmt.Errorf("wgpu: WriteBuffer: no HAL buffer")
}
return q.hal.WriteBuffer(halBuffer, offset, data)
}
// ReadBuffer reads data from a GPU buffer.
func (q *Queue) ReadBuffer(buffer *Buffer, offset uint64, data []byte) error {
if q.hal == nil {
return fmt.Errorf("wgpu: queue not available")
}
if buffer == nil {
return fmt.Errorf("wgpu: buffer is nil")
}
halBuffer := buffer.halBuffer()
if halBuffer == nil {
return ErrReleased
}
return q.hal.ReadBuffer(halBuffer, offset, data)
}
// WriteTexture writes data to a texture.
func (q *Queue) WriteTexture(dst *ImageCopyTexture, data []byte, layout *ImageDataLayout, size *Extent3D) error {
if q.hal == nil || dst == nil {
return fmt.Errorf("wgpu: WriteTexture: queue or destination is nil")
}
if dst.Texture == nil || dst.Texture.hal == nil {
return fmt.Errorf("wgpu: WriteTexture: destination texture is invalid")
}
if layout == nil {
return fmt.Errorf("wgpu: WriteTexture: layout is nil")
}
if size == nil {
return fmt.Errorf("wgpu: WriteTexture: size is nil")
}
halDst := dst.toHAL()
halLayout := layout.toHAL()
halSize := size.toHAL()
return q.hal.WriteTexture(halDst, data, &halLayout, &halSize)
}
// release cleans up queue resources.
func (q *Queue) release() {
if q.fence != nil && q.halDevice != nil {
q.halDevice.DestroyFence(q.fence)
q.fence = nil
}
}