-
-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathencoder_pool.go
More file actions
88 lines (75 loc) · 2.51 KB
/
Copy pathencoder_pool.go
File metadata and controls
88 lines (75 loc) · 2.51 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
//go:build !rust && !(js && wasm)
package wgpu
import (
"fmt"
"sync"
"github.com/gogpu/wgpu/hal"
)
// encoderPool pools hal.CommandEncoder instances to avoid creating expensive
// GPU resources (DX12 command allocators, Vulkan command pools) every frame.
// Matches Rust wgpu-core's CommandAllocator pattern (allocator.rs).
//
// Each encoder in the pool is in "closed" state — ready for BeginEncoding.
// After EndEncoding returns a command buffer, the encoder retains its internal
// resources. After GPU completion, ResetAll prepares the encoder for reuse.
//
// The pool is backend-agnostic: DX12 encoders keep their ID3D12CommandAllocator,
// Vulkan encoders keep their VkCommandPool, and lightweight backends (GLES,
// Software, Metal, Noop) pass through with negligible overhead.
type encoderPool struct {
mu sync.Mutex
free []hal.CommandEncoder
halDevice hal.Device
}
// newEncoderPool creates a new encoder pool for the given HAL device.
func newEncoderPool(halDevice hal.Device) *encoderPool {
return &encoderPool{
halDevice: halDevice,
}
}
// poolManagedSetter is implemented by HAL encoders that need special setup
// when managed by the encoder pool (e.g., Vulkan CommandEncoder).
type poolManagedSetter interface {
SetPoolManaged(managed bool)
}
// acquire returns a command encoder from the pool, or creates a new one.
// The returned encoder is in "closed" state, ready for BeginEncoding.
func (p *encoderPool) acquire() (hal.CommandEncoder, error) {
p.mu.Lock()
if n := len(p.free); n > 0 {
enc := p.free[n-1]
p.free[n-1] = nil // avoid retaining reference
p.free = p.free[:n-1]
p.mu.Unlock()
return enc, nil
}
p.mu.Unlock()
// Create a new encoder from the HAL device.
enc, err := p.halDevice.CreateCommandEncoder(&hal.CommandEncoderDescriptor{
Label: "(wgpu internal) pending writes",
})
if err != nil {
return nil, fmt.Errorf("encoder pool: create encoder: %w", err)
}
// Mark as pool-managed if the backend supports it (e.g., Vulkan).
if setter, ok := enc.(poolManagedSetter); ok {
setter.SetPoolManaged(true)
}
return enc, nil
}
// release returns an encoder to the pool after ResetAll has been called.
// The encoder must be in "closed" state (not recording).
func (p *encoderPool) release(enc hal.CommandEncoder) {
p.mu.Lock()
p.free = append(p.free, enc)
p.mu.Unlock()
}
// destroy releases all pooled encoders. Called during device shutdown.
func (p *encoderPool) destroy() {
p.mu.Lock()
defer p.mu.Unlock()
for _, enc := range p.free {
enc.Destroy()
}
p.free = nil
}