1 //
2 // Copyright 2024 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "libANGLE/renderer/wgpu/wgpu_pipeline_state.h"
8
9 #include "common/aligned_memory.h"
10 #include "common/hash_utils.h"
11 #include "libANGLE/Error.h"
12 #include "libANGLE/renderer/wgpu/ContextWgpu.h"
13 #include "libANGLE/renderer/wgpu/wgpu_utils.h"
14
15 namespace rx
16 {
17 namespace webgpu
18 {
19 // Can pack the index format into 1 bit since it has 2 values and Undefined is not used.
20 static_assert(static_cast<uint32_t>(wgpu::IndexFormat::Uint32) == 2U,
21 "Max wgpu::IndexFormat is not 2");
22 static_assert(static_cast<uint32_t>(wgpu::IndexFormat::Undefined) == 0,
23 "wgpu::IndexFormat::Undefined unexpected value");
PackIndexFormat(wgpu::IndexFormat unpackedFormat)24 constexpr uint32_t PackIndexFormat(wgpu::IndexFormat unpackedFormat)
25 {
26 ASSERT(static_cast<uint32_t>(unpackedFormat) > 0);
27 return static_cast<uint32_t>(unpackedFormat) - 1;
28 }
29
UnpackIndexFormat(uint32_t packedIndexFormat)30 constexpr wgpu::IndexFormat UnpackIndexFormat(uint32_t packedIndexFormat)
31 {
32 return static_cast<wgpu::IndexFormat>(packedIndexFormat + 1);
33 }
34
35 // Can pack the front face into 1 bit since it has 2 values and Undefined is not used.
36 static_assert(static_cast<uint32_t>(wgpu::FrontFace::CW) == 2U, "Max wgpu::FrontFace is not 2");
37 static_assert(static_cast<uint32_t>(wgpu::FrontFace::Undefined) == 0,
38 "wgpu::FrontFace::Undefined unexpected value");
PackFrontFace(wgpu::FrontFace unpackedFrontFace)39 constexpr uint32_t PackFrontFace(wgpu::FrontFace unpackedFrontFace)
40 {
41 ASSERT(static_cast<uint32_t>(unpackedFrontFace) > 0);
42 return static_cast<uint32_t>(unpackedFrontFace) - 1;
43 }
44
UnpackFrontFace(uint32_t packedFrontFace)45 constexpr wgpu::FrontFace UnpackFrontFace(uint32_t packedFrontFace)
46 {
47 return static_cast<wgpu::FrontFace>(packedFrontFace + 1);
48 }
49
PackedVertexAttribute()50 PackedVertexAttribute::PackedVertexAttribute()
51 {
52 memset(this, 0, sizeof(PackedVertexAttribute));
53 }
54
55 // GraphicsPipelineDesc implementation.
RenderPipelineDesc()56 RenderPipelineDesc::RenderPipelineDesc()
57 {
58 (void)mPad0;
59 memset(this, 0, sizeof(RenderPipelineDesc));
60 }
61
62 RenderPipelineDesc::~RenderPipelineDesc() = default;
63
RenderPipelineDesc(const RenderPipelineDesc & other)64 RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &other)
65 {
66 *this = other;
67 }
68
operator =(const RenderPipelineDesc & other)69 RenderPipelineDesc &RenderPipelineDesc::operator=(const RenderPipelineDesc &other)
70 {
71 memcpy(this, &other, sizeof(*this));
72 return *this;
73 }
74
setPrimitiveMode(gl::PrimitiveMode primitiveMode,gl::DrawElementsType indexTypeOrInvalid)75 bool RenderPipelineDesc::setPrimitiveMode(gl::PrimitiveMode primitiveMode,
76 gl::DrawElementsType indexTypeOrInvalid)
77 {
78 bool changed = false;
79
80 wgpu::PrimitiveTopology topology = gl_wgpu::GetPrimitiveTopology(primitiveMode);
81 if (mPrimitiveState.topology != static_cast<uint8_t>(topology))
82 {
83 SetBitField(mPrimitiveState.topology, topology);
84 changed = true;
85 }
86
87 uint32_t indexFormat = webgpu::IsStripPrimitiveTopology(topology) &&
88 indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum
89 ? PackIndexFormat(gl_wgpu::GetIndexFormat(indexTypeOrInvalid))
90 : 0;
91 if (mPrimitiveState.stripIndexFormat != static_cast<uint8_t>(indexFormat))
92 {
93 SetBitField(mPrimitiveState.stripIndexFormat, indexFormat);
94 changed = true;
95 }
96
97 return changed;
98 }
99
setFrontFace(GLenum frontFace)100 void RenderPipelineDesc::setFrontFace(GLenum frontFace)
101 {
102 SetBitField(mPrimitiveState.frontFace, PackFrontFace(gl_wgpu::GetFrontFace(frontFace)));
103 }
104
setCullMode(gl::CullFaceMode cullMode,bool cullFaceEnabled)105 void RenderPipelineDesc::setCullMode(gl::CullFaceMode cullMode, bool cullFaceEnabled)
106 {
107 SetBitField(mPrimitiveState.cullMode, gl_wgpu::GetCullMode(cullMode, cullFaceEnabled));
108 }
109
setColorWriteMask(size_t colorIndex,bool r,bool g,bool b,bool a)110 void RenderPipelineDesc::setColorWriteMask(size_t colorIndex, bool r, bool g, bool b, bool a)
111 {
112 PackedColorTargetState &colorTarget = mColorTargetStates[colorIndex];
113 SetBitField(colorTarget.writeMask, gl_wgpu::GetColorWriteMask(r, g, b, a));
114 }
115
setVertexAttribute(size_t attribIndex,PackedVertexAttribute & newAttrib)116 bool RenderPipelineDesc::setVertexAttribute(size_t attribIndex, PackedVertexAttribute &newAttrib)
117 {
118 PackedVertexAttribute ¤tAttrib = mVertexAttributes[attribIndex];
119 if (memcmp(¤tAttrib, &newAttrib, sizeof(PackedVertexAttribute)) == 0)
120 {
121 return false;
122 }
123
124 memcpy(¤tAttrib, &newAttrib, sizeof(PackedVertexAttribute));
125 return true;
126 }
127
setColorAttachmentFormat(size_t colorIndex,wgpu::TextureFormat format)128 bool RenderPipelineDesc::setColorAttachmentFormat(size_t colorIndex, wgpu::TextureFormat format)
129 {
130 if (mColorTargetStates[colorIndex].format == static_cast<uint8_t>(format))
131 {
132 return false;
133 }
134
135 SetBitField(mColorTargetStates[colorIndex].format, format);
136 return true;
137 }
138
setDepthStencilAttachmentFormat(wgpu::TextureFormat format)139 bool RenderPipelineDesc::setDepthStencilAttachmentFormat(wgpu::TextureFormat format)
140 {
141 if (mDepthStencilState.format == static_cast<uint8_t>(format))
142 {
143 return false;
144 }
145
146 SetBitField(mDepthStencilState.format, format);
147 return true;
148 }
149
setDepthFunc(wgpu::CompareFunction compareFunc)150 bool RenderPipelineDesc::setDepthFunc(wgpu::CompareFunction compareFunc)
151 {
152 if (mDepthStencilState.depthCompare == static_cast<uint8_t>(compareFunc))
153 {
154 return false;
155 }
156 SetBitField(mDepthStencilState.depthCompare, compareFunc);
157 return true;
158 }
159
setStencilFrontFunc(wgpu::CompareFunction compareFunc)160 bool RenderPipelineDesc::setStencilFrontFunc(wgpu::CompareFunction compareFunc)
161 {
162 if (mDepthStencilState.stencilFrontCompare == static_cast<uint8_t>(compareFunc))
163 {
164 return false;
165 }
166 SetBitField(mDepthStencilState.stencilFrontCompare, compareFunc);
167 return true;
168 }
169
setStencilFrontOps(wgpu::StencilOperation failOp,wgpu::StencilOperation depthFailOp,wgpu::StencilOperation passOp)170 bool RenderPipelineDesc::setStencilFrontOps(wgpu::StencilOperation failOp,
171 wgpu::StencilOperation depthFailOp,
172 wgpu::StencilOperation passOp)
173 {
174 if (mDepthStencilState.stencilFrontFailOp == static_cast<uint8_t>(failOp) &&
175 mDepthStencilState.stencilFrontDepthFailOp == static_cast<uint8_t>(depthFailOp) &&
176 mDepthStencilState.stencilFrontPassOp == static_cast<uint8_t>(passOp))
177 {
178 return false;
179 }
180 SetBitField(mDepthStencilState.stencilFrontFailOp, failOp);
181 SetBitField(mDepthStencilState.stencilFrontDepthFailOp, depthFailOp);
182 SetBitField(mDepthStencilState.stencilFrontPassOp, passOp);
183 return true;
184 }
185
setStencilBackFunc(wgpu::CompareFunction compareFunc)186 bool RenderPipelineDesc::setStencilBackFunc(wgpu::CompareFunction compareFunc)
187 {
188 if (mDepthStencilState.stencilBackCompare == static_cast<uint8_t>(compareFunc))
189 {
190 return false;
191 }
192 SetBitField(mDepthStencilState.stencilBackCompare, compareFunc);
193 return true;
194 }
195
setStencilBackOps(wgpu::StencilOperation failOp,wgpu::StencilOperation depthFailOp,wgpu::StencilOperation passOp)196 bool RenderPipelineDesc::setStencilBackOps(wgpu::StencilOperation failOp,
197 wgpu::StencilOperation depthFailOp,
198 wgpu::StencilOperation passOp)
199 {
200 if (mDepthStencilState.stencilBackFailOp == static_cast<uint8_t>(failOp) &&
201 mDepthStencilState.stencilBackDepthFailOp == static_cast<uint8_t>(depthFailOp) &&
202 mDepthStencilState.stencilBackPassOp == static_cast<uint8_t>(passOp))
203 {
204 return false;
205 }
206 SetBitField(mDepthStencilState.stencilBackFailOp, failOp);
207 SetBitField(mDepthStencilState.stencilBackDepthFailOp, depthFailOp);
208 SetBitField(mDepthStencilState.stencilBackPassOp, passOp);
209 return true;
210 }
211
setStencilReadMask(uint8_t readMask)212 bool RenderPipelineDesc::setStencilReadMask(uint8_t readMask)
213 {
214
215 if (mDepthStencilState.stencilReadMask == readMask)
216 {
217 return false;
218 }
219 mDepthStencilState.stencilReadMask = readMask;
220 return true;
221 }
222
setStencilWriteMask(uint8_t writeMask)223 bool RenderPipelineDesc::setStencilWriteMask(uint8_t writeMask)
224 {
225 if (mDepthStencilState.stencilWriteMask == writeMask)
226 {
227 return false;
228 }
229 mDepthStencilState.stencilWriteMask = writeMask;
230 return true;
231 }
232
hash() const233 size_t RenderPipelineDesc::hash() const
234 {
235 return angle::ComputeGenericHash(this, sizeof(*this));
236 }
237
createPipeline(ContextWgpu * context,const wgpu::PipelineLayout & pipelineLayout,const gl::ShaderMap<wgpu::ShaderModule> & shaders,wgpu::RenderPipeline * pipelineOut) const238 angle::Result RenderPipelineDesc::createPipeline(ContextWgpu *context,
239 const wgpu::PipelineLayout &pipelineLayout,
240 const gl::ShaderMap<wgpu::ShaderModule> &shaders,
241 wgpu::RenderPipeline *pipelineOut) const
242 {
243 wgpu::RenderPipelineDescriptor pipelineDesc;
244 pipelineDesc.layout = pipelineLayout;
245
246 pipelineDesc.vertex.module = shaders[gl::ShaderType::Vertex];
247 pipelineDesc.vertex.entryPoint = "wgslMain";
248 pipelineDesc.vertex.constantCount = 0;
249 pipelineDesc.vertex.constants = nullptr;
250 pipelineDesc.vertex.bufferCount = 0;
251 pipelineDesc.vertex.buffers = nullptr;
252
253 pipelineDesc.primitive.topology =
254 static_cast<wgpu::PrimitiveTopology>(mPrimitiveState.topology);
255 if (webgpu::IsStripPrimitiveTopology(pipelineDesc.primitive.topology))
256 {
257 pipelineDesc.primitive.stripIndexFormat =
258 UnpackIndexFormat(mPrimitiveState.stripIndexFormat);
259 }
260 else
261 {
262 pipelineDesc.primitive.stripIndexFormat = wgpu::IndexFormat::Undefined;
263 }
264 pipelineDesc.primitive.frontFace = UnpackFrontFace(mPrimitiveState.frontFace);
265 pipelineDesc.primitive.cullMode = static_cast<wgpu::CullMode>(mPrimitiveState.cullMode);
266
267 size_t attribCount = 0;
268 gl::AttribArray<wgpu::VertexBufferLayout> vertexBuffers;
269 gl::AttribArray<wgpu::VertexAttribute> vertexAttribs;
270
271 for (PackedVertexAttribute packedAttrib : mVertexAttributes)
272 {
273 if (!packedAttrib.enabled)
274 {
275 continue;
276 }
277
278 wgpu::VertexAttribute &newAttribute = vertexAttribs[attribCount];
279 newAttribute.format = static_cast<wgpu::VertexFormat>(packedAttrib.format);
280 newAttribute.offset = packedAttrib.offset;
281 newAttribute.shaderLocation = packedAttrib.shaderLocation;
282
283 wgpu::VertexBufferLayout &newBufferLayout = vertexBuffers[attribCount];
284 newBufferLayout.arrayStride = packedAttrib.stride;
285 newBufferLayout.attributeCount = 1;
286 newBufferLayout.attributes = &newAttribute;
287
288 attribCount++;
289 }
290
291 pipelineDesc.vertex.bufferCount = attribCount;
292 pipelineDesc.vertex.buffers = vertexBuffers.data();
293
294 wgpu::FragmentState fragmentState;
295 std::array<wgpu::ColorTargetState, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS> colorTargets;
296 std::array<wgpu::BlendState, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS> blendStates;
297 if (shaders[gl::ShaderType::Fragment])
298 {
299 fragmentState.module = shaders[gl::ShaderType::Fragment];
300 fragmentState.entryPoint = "wgslMain";
301 fragmentState.constantCount = 0;
302 fragmentState.constants = nullptr;
303
304 size_t colorTargetCount = 0;
305 for (size_t colorTargetIndex = 0; colorTargetIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
306 ++colorTargetIndex)
307 {
308 const webgpu::PackedColorTargetState &packedColorTarget =
309 mColorTargetStates[colorTargetIndex];
310 wgpu::ColorTargetState &outputColorTarget = colorTargets[colorTargetIndex];
311
312 outputColorTarget.format = static_cast<wgpu::TextureFormat>(packedColorTarget.format);
313 if (packedColorTarget.blendEnabled)
314 {
315 blendStates[colorTargetIndex].color.srcFactor =
316 static_cast<wgpu::BlendFactor>(packedColorTarget.colorBlendSrcFactor);
317 blendStates[colorTargetIndex].color.dstFactor =
318 static_cast<wgpu::BlendFactor>(packedColorTarget.colorBlendDstFactor);
319 blendStates[colorTargetIndex].color.operation =
320 static_cast<wgpu::BlendOperation>(packedColorTarget.colorBlendOp);
321
322 blendStates[colorTargetIndex].alpha.srcFactor =
323 static_cast<wgpu::BlendFactor>(packedColorTarget.alphaBlendSrcFactor);
324 blendStates[colorTargetIndex].alpha.dstFactor =
325 static_cast<wgpu::BlendFactor>(packedColorTarget.alphaBlendDstFactor);
326 blendStates[colorTargetIndex].alpha.operation =
327 static_cast<wgpu::BlendOperation>(packedColorTarget.alphaBlendOp);
328 }
329
330 outputColorTarget.writeMask =
331 static_cast<wgpu::ColorWriteMask>(packedColorTarget.writeMask);
332
333 if (outputColorTarget.format != wgpu::TextureFormat::Undefined)
334 {
335 colorTargetCount = colorTargetIndex + 1;
336 }
337 }
338 fragmentState.targetCount = colorTargetCount;
339 fragmentState.targets = colorTargets.data();
340
341 pipelineDesc.fragment = &fragmentState;
342 }
343
344 wgpu::DepthStencilState depthStencilState;
345 if (static_cast<wgpu::TextureFormat>(mDepthStencilState.format) !=
346 wgpu::TextureFormat::Undefined)
347 {
348 const webgpu::PackedDepthStencilState &packedDepthStencilState = mDepthStencilState;
349
350 depthStencilState.format = static_cast<wgpu::TextureFormat>(packedDepthStencilState.format);
351 depthStencilState.depthWriteEnabled =
352 static_cast<bool>(packedDepthStencilState.depthWriteEnabled);
353 depthStencilState.depthCompare =
354 static_cast<wgpu::CompareFunction>(packedDepthStencilState.depthCompare);
355
356 depthStencilState.stencilFront.compare =
357 static_cast<wgpu::CompareFunction>(packedDepthStencilState.stencilFrontCompare);
358 depthStencilState.stencilFront.failOp =
359 static_cast<wgpu::StencilOperation>(packedDepthStencilState.stencilFrontFailOp);
360 depthStencilState.stencilFront.depthFailOp =
361 static_cast<wgpu::StencilOperation>(packedDepthStencilState.stencilFrontDepthFailOp);
362 depthStencilState.stencilFront.passOp =
363 static_cast<wgpu::StencilOperation>(packedDepthStencilState.stencilFrontPassOp);
364
365 depthStencilState.stencilBack.compare =
366 static_cast<wgpu::CompareFunction>(packedDepthStencilState.stencilBackCompare);
367 depthStencilState.stencilBack.failOp =
368 static_cast<wgpu::StencilOperation>(packedDepthStencilState.stencilBackFailOp);
369 depthStencilState.stencilBack.depthFailOp =
370 static_cast<wgpu::StencilOperation>(packedDepthStencilState.stencilBackDepthFailOp);
371 depthStencilState.stencilBack.passOp =
372 static_cast<wgpu::StencilOperation>(packedDepthStencilState.stencilBackPassOp);
373
374 depthStencilState.stencilReadMask = packedDepthStencilState.stencilReadMask;
375 depthStencilState.stencilWriteMask = packedDepthStencilState.stencilWriteMask;
376
377 depthStencilState.depthBias = packedDepthStencilState.depthBias;
378 depthStencilState.depthBiasSlopeScale = packedDepthStencilState.depthBiasSlopeScalef;
379 depthStencilState.depthBiasClamp = packedDepthStencilState.depthBiasClamp;
380
381 pipelineDesc.depthStencil = &depthStencilState;
382 }
383
384 wgpu::Device device = context->getDevice();
385 ANGLE_WGPU_SCOPED_DEBUG_TRY(context, *pipelineOut = device.CreateRenderPipeline(&pipelineDesc));
386
387 return angle::Result::Continue;
388 }
389
operator ==(const RenderPipelineDesc & lhs,const RenderPipelineDesc & rhs)390 bool operator==(const RenderPipelineDesc &lhs, const RenderPipelineDesc &rhs)
391 {
392 return memcmp(&lhs, &rhs, sizeof(RenderPipelineDesc)) == 0;
393 }
394
395 // PipelineCache implementation.
396 PipelineCache::PipelineCache() = default;
397 PipelineCache::~PipelineCache() = default;
398
getRenderPipeline(ContextWgpu * context,const RenderPipelineDesc & desc,const wgpu::PipelineLayout & pipelineLayout,const gl::ShaderMap<wgpu::ShaderModule> & shaders,wgpu::RenderPipeline * pipelineOut)399 angle::Result PipelineCache::getRenderPipeline(ContextWgpu *context,
400 const RenderPipelineDesc &desc,
401 const wgpu::PipelineLayout &pipelineLayout,
402 const gl::ShaderMap<wgpu::ShaderModule> &shaders,
403 wgpu::RenderPipeline *pipelineOut)
404 {
405 auto iter = mRenderPipelines.find(desc);
406 if (iter != mRenderPipelines.end())
407 {
408 *pipelineOut = iter->second;
409 return angle::Result::Continue;
410 }
411
412 ANGLE_TRY(desc.createPipeline(context, pipelineLayout, shaders, pipelineOut));
413 mRenderPipelines.insert(std::make_pair(desc, *pipelineOut));
414
415 return angle::Result::Continue;
416 }
417
418 } // namespace webgpu
419
420 } // namespace rx
421