-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathTextureBindGroup.ts
188 lines (170 loc) · 5.5 KB
/
TextureBindGroup.ts
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
import { BindGroup } from './BindGroup'
import { isRenderer, Renderer } from '../renderers/utils'
import { GPUCurtains } from '../../curtains/GPUCurtains'
import { Texture } from '../textures/Texture'
import { Sampler } from '../samplers/Sampler'
import { BindGroupParams } from '../../types/BindGroups'
import { MaterialTexture } from '../../types/Materials'
/**
* An object defining all possible {@link TextureBindGroup} class instancing parameters
*/
export interface TextureBindGroupParams extends BindGroupParams {
/** array of {@link MaterialTexture | textures} to add to a {@link TextureBindGroup} */
textures?: MaterialTexture[]
/** array of {@link Sampler} to add to a {@link TextureBindGroup} */
samplers?: Sampler[]
}
/**
* Used to regroup all {@link types/BindGroups.BindGroupBindingElement | bindings} related to textures (texture, texture matrices buffers and samplers) into one single specific {@link BindGroup}.
*
* Also responsible for uploading video textures if needed.
*
* @example
* ```javascript
* // set our main GPUCurtains instance
* const gpuCurtains = new GPUCurtains({
* container: '#canvas' // selector of our WebGPU canvas container
* })
*
* // set the GPU device
* // note this is asynchronous
* await gpuCurtains.setDevice()
*
* // create a render texture
* const renderTexture = new RenderTexture(gpuCurtains, {
* label: 'Input texture',
* name: 'inputTexture',
* })
*
* // create a texture bind group using that render texture
* const textureBindGroup = new TextureBindGroup(gpuCurtains, {
* label: 'My texture bind group',
* textures: [renderTexture],
* uniforms: {
* params: {
* struct: {
* opacity: {
* type: 'f32',
* value: 1,
* },
* mousePosition: {
* type: 'vec2f',
* value: new Vec2(),
* },
* },
* },
* },
* })
*
* // create the GPU buffer, bindGroupLayout and bindGroup
* textureBindGroup.createBindGroup()
* ```
*/
export class TextureBindGroup extends BindGroup {
/**
* TextureBindGroup constructor
* @param renderer - a {@link Renderer} class object or a {@link GPUCurtains} class object
* @param parameters - {@link TextureBindGroupParams | parameters} used to create our {@link TextureBindGroup}
*/
constructor(
renderer: Renderer | GPUCurtains,
{ label, index = 0, bindings = [], uniforms, storages, textures = [], samplers = [] }: TextureBindGroupParams = {}
) {
const type = 'TextureBindGroup'
// we could pass our curtains object OR our curtains renderer object
renderer = (renderer && (renderer as GPUCurtains).renderer) || (renderer as Renderer)
isRenderer(renderer, type)
super(renderer, { label, index, bindings, uniforms, storages })
this.options = {
...this.options,
// will be filled after
textures: [],
samplers: [],
}
// add initial textures if any
if (textures.length) {
textures.forEach((texture) => this.addTexture(texture))
}
// add initial samplers if any
if (samplers.length) {
samplers.forEach((sampler) => this.addSampler(sampler))
}
this.type = type
}
/**
* Adds a texture to the textures array and the struct
* @param texture - texture to add
*/
addTexture(texture: MaterialTexture) {
this.textures.push(texture)
this.addBindings([...texture.bindings])
}
/**
* Get the current textures array
* @readonly
*/
get textures(): MaterialTexture[] {
return this.options.textures
}
/**
* Adds a sampler to the samplers array and the struct
* @param sampler
*/
addSampler(sampler: Sampler) {
this.samplers.push(sampler)
this.addBindings([sampler.binding])
}
/**
* Get the current samplers array
* @readonly
*/
get samplers(): Sampler[] {
return this.options.samplers
}
/**
* Get whether the GPU bind group is ready to be created
* It can be created if it has {@link BindGroup#bindings} and has not been created yet and all GPU textures and samplers are created
* @readonly
*/
get shouldCreateBindGroup(): boolean {
return (
!this.bindGroup &&
!!this.bindings.length &&
!this.textures.find((texture) => !(texture.texture || (texture as Texture).externalTexture)) &&
!this.samplers.find((sampler) => !sampler.sampler)
)
}
/**
* Update the {@link TextureBindGroup#textures | bind group textures}:
* - Check if they need to copy their source texture
* - Upload video texture if needed
*/
updateTextures() {
this.textures.forEach((texture) => {
// copy textures that need it on first init, but only when original texture is ready
if (texture instanceof Texture) {
if (texture.options.fromTexture && texture.options.fromTexture.sourceUploaded && !texture.sourceUploaded) {
texture.copy(texture.options.fromTexture)
}
if (texture.shouldUpdate && texture.options.sourceType && texture.options.sourceType === 'externalVideo') {
texture.uploadVideoTexture()
}
}
})
}
/**
* Update the {@link TextureBindGroup}, which means update its {@link TextureBindGroup#textures | textures}, then update its {@link TextureBindGroup#bufferBindings | buffer bindings} and finally {@link TextureBindGroup#resetBindGroup | reset it} if needed
*/
update() {
this.updateTextures()
super.update()
}
/**
* Destroy our {@link TextureBindGroup}
*/
destroy() {
super.destroy()
this.options.textures = []
this.options.samplers = []
}
}