-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathPipelineEntry.ts
190 lines (162 loc) · 6.29 KB
/
PipelineEntry.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
189
190
import { isRenderer, Renderer } from '../renderers/utils'
import { PipelineEntryOptions, PipelineEntryParams, PipelineEntryStatus } from '../../types/PipelineEntries'
import { GPUCurtains } from '../../curtains/GPUCurtains'
import { AllowedBindGroups } from '../../types/BindGroups'
import { MaterialShadersType } from '../../types/Materials'
let pipelineId = 0
/**
* Used as a base class to create a pipeline entry.<br>
* {@link PipelineEntry} roles are:
* - Patch the given {@link core/materials/Material.Material | Material} shaders code and create the corresponding {@link GPUShaderModule}.
* - Create a {@link GPUPipelineLayout | pipeline layout} with the given {@link core/materials/Material.Material#bindGroups | bind groups}
* - Create a GPU pipeline
*/
export class PipelineEntry {
/** The type of the {@link PipelineEntry} */
type: string
/** The {@link Renderer} used to create this {@link PipelineEntry} */
renderer: Renderer
/** Index of this {@link PipelineEntry}, i.e. creation order */
readonly index: number
/** {@link GPUPipelineLayout | Pipeline layout} created based on the given {@link bindGroups | bind groups} */
layout: GPUPipelineLayout | null
/** The GPU pipeline */
pipeline: GPURenderPipeline | GPUComputePipeline | null
/** The pipeline {@link PipelineEntryStatus | compilation status} */
status: PipelineEntryStatus
/** Options used to create this {@link PipelineEntry} */
options: PipelineEntryOptions
/** {@link core/materials/Material.Material#bindGroups | bind groups} used to patch the shaders and create the {@link PipelineEntry#layout | pipeline layout} */
bindGroups: AllowedBindGroups[]
/**
* PipelineEntry constructor
* @param parameters - {@link PipelineEntryParams | parameters} used to create this {@link PipelineEntry}
*/
constructor(parameters: PipelineEntryParams) {
this.type = 'PipelineEntry'
let { renderer } = parameters
const { label, shaders, useAsync } = parameters
// we could pass our curtains object OR our curtains renderer object
renderer = (renderer && (renderer as GPUCurtains).renderer) || (renderer as Renderer)
isRenderer(renderer, label ? label + ' ' + this.type : this.type)
this.renderer = renderer
Object.defineProperty(this as PipelineEntry, 'index', { value: pipelineId++ })
this.layout = null
this.pipeline = null
this.status = {
compiling: false,
compiled: false,
error: null,
}
this.options = {
label,
shaders,
useAsync: useAsync !== undefined ? useAsync : true,
}
}
/**
* Get whether the {@link pipeline} is ready, i.e. successfully compiled
* @readonly
*/
get ready(): boolean {
return !this.status.compiling && this.status.compiled && !this.status.error
}
/**
* Get whether the {@link pipeline} is ready to be compiled, i.e. we have not already tried to compile it, and it's not currently compiling neither
* @readonly
*/
get canCompile(): boolean {
return !this.status.compiling && !this.status.compiled && !this.status.error
}
/**
* Set our {@link PipelineEntry#bindGroups | pipeline entry bind groups}
* @param bindGroups - {@link core/materials/Material.Material#bindGroups | bind groups} to use with this {@link PipelineEntry}
*/
setPipelineEntryBindGroups(bindGroups: AllowedBindGroups[]) {
this.bindGroups = bindGroups
}
/* SHADERS */
/**
* Create a {@link GPUShaderModule}
* @param parameters - Parameters used
* @param parameters.code - patched WGSL code string
* @param parameters.type - {@link MaterialShadersType | shader type}
* @returns - compiled {@link GPUShaderModule} if successful
*/
createShaderModule({ code = '', type = 'vertex' }: { code: string; type: MaterialShadersType }): GPUShaderModule {
const shaderModule = this.renderer.createShaderModule({
label: this.options.label + ': ' + type + ' shader module',
code,
})
if ('getCompilationInfo' in shaderModule && !this.renderer.production) {
shaderModule.getCompilationInfo().then((compilationInfo) => {
for (const message of compilationInfo.messages) {
let formattedMessage = ''
if (message.lineNum) {
formattedMessage += `Line ${message.lineNum}:${message.linePos} - ${code.substring(
message.offset,
message.offset + message.length
)}\n`
}
formattedMessage += message.message
switch (message.type) {
case 'error':
// TODO mesh onError?
console.error(`${this.options.label} compilation error:\n${formattedMessage}`)
break
case 'warning':
console.warn(`${this.options.label} compilation warning:\n${formattedMessage}`)
break
case 'info':
console.log(`${this.options.label} compilation information:\n${formattedMessage}`)
break
}
}
})
}
return shaderModule
}
/* SETUP */
/**
* Create the {@link PipelineEntry} shaders
*/
createShaders() {
/* will be overriden */
}
/**
* Create the pipeline entry {@link layout}
*/
createPipelineLayout() {
this.layout = this.renderer.createPipelineLayout({
label: this.options.label + ' layout',
bindGroupLayouts: this.bindGroups.map((bindGroup) => bindGroup.bindGroupLayout),
})
}
/**
* Create the {@link PipelineEntry} descriptor
*/
createPipelineDescriptor() {
/* will be overriden */
}
/**
* Flush a {@link PipelineEntry}, i.e. reset its {@link bindGroups | bind groups}, {@link layout} and descriptor and recompile the {@link pipeline}
* Used when one of the bind group or rendering property has changed
* @param newBindGroups - new {@link bindGroups | bind groups} in case they have changed
*/
flushPipelineEntry(newBindGroups: AllowedBindGroups[] = []) {
this.status.compiling = false
this.status.compiled = false
this.status.error = null
this.setPipelineEntryBindGroups(newBindGroups)
this.compilePipelineEntry()
}
/**
* Set up a {@link pipeline} by creating the shaders, the {@link layout} and the descriptor
*/
compilePipelineEntry() {
this.status.compiling = true
this.createShaders()
this.createPipelineLayout()
this.createPipelineDescriptor()
}
}