RenderPassEncoder.cpp (16274B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "RenderPassEncoder.h" 7 8 #include "BindGroup.h" 9 #include "CommandEncoder.h" 10 #include "ExternalTexture.h" 11 #include "RenderBundle.h" 12 #include "RenderPipeline.h" 13 #include "TextureView.h" 14 #include "Utility.h" 15 #include "mozilla/dom/WebGPUBinding.h" 16 #include "mozilla/webgpu/ffi/wgpu.h" 17 18 namespace mozilla::webgpu { 19 20 GPU_IMPL_CYCLE_COLLECTION(RenderPassEncoder, mParent, mUsedBindGroups, 21 mUsedBuffers, mUsedPipelines, mUsedTextureViews, 22 mUsedRenderBundles) 23 GPU_IMPL_JS_WRAP(RenderPassEncoder) 24 25 void ffiWGPURenderPassDeleter::operator()(ffi::WGPURecordedRenderPass* raw) { 26 if (raw) { 27 ffi::wgpu_render_pass_destroy(raw); 28 } 29 } 30 31 static ffi::WGPUStoreOp ConvertStoreOp(const dom::GPUStoreOp& aOp) { 32 switch (aOp) { 33 case dom::GPUStoreOp::Store: 34 return ffi::WGPUStoreOp_Store; 35 case dom::GPUStoreOp::Discard: 36 return ffi::WGPUStoreOp_Discard; 37 } 38 MOZ_CRASH("bad GPUStoreOp"); 39 } 40 41 static ffi::WGPUColor ConvertColor(const dom::Sequence<double>& aSeq) { 42 ffi::WGPUColor color{ 43 .r = aSeq.SafeElementAt(0, 0.0), 44 .g = aSeq.SafeElementAt(1, 0.0), 45 .b = aSeq.SafeElementAt(2, 0.0), 46 .a = aSeq.SafeElementAt(3, 1.0), 47 }; 48 return color; 49 } 50 51 static ffi::WGPUColor ConvertColor(const dom::GPUColorDict& aColor) { 52 ffi::WGPUColor color = {aColor.mR, aColor.mG, aColor.mB, aColor.mA}; 53 return color; 54 } 55 56 static ffi::WGPUColor ConvertColor( 57 const dom::DoubleSequenceOrGPUColorDict& aColor) { 58 if (aColor.IsDoubleSequence()) { 59 return ConvertColor(aColor.GetAsDoubleSequence()); 60 } 61 if (aColor.IsGPUColorDict()) { 62 return ConvertColor(aColor.GetAsGPUColorDict()); 63 } 64 MOZ_ASSERT_UNREACHABLE( 65 "Unexpected dom::DoubleSequenceOrGPUColorDict variant"); 66 return ffi::WGPUColor(); 67 } 68 static ffi::WGPUColor ConvertColor( 69 const dom::OwningDoubleSequenceOrGPUColorDict& aColor) { 70 if (aColor.IsDoubleSequence()) { 71 return ConvertColor(aColor.GetAsDoubleSequence()); 72 } 73 if (aColor.IsGPUColorDict()) { 74 return ConvertColor(aColor.GetAsGPUColorDict()); 75 } 76 MOZ_ASSERT_UNREACHABLE( 77 "Unexpected dom::OwningDoubleSequenceOrGPUColorDict variant"); 78 return ffi::WGPUColor(); 79 } 80 81 ffi::WGPURecordedRenderPass* BeginRenderPass( 82 CommandEncoder* const aParent, const dom::GPURenderPassDescriptor& aDesc) { 83 ffi::WGPURenderPassDescriptor desc = {}; 84 85 webgpu::StringHelper label(aDesc.mLabel); 86 desc.label = label.Get(); 87 88 ffi::WGPURenderPassDepthStencilAttachment dsDesc = {}; 89 if (aDesc.mDepthStencilAttachment.WasPassed()) { 90 const auto& dsa = aDesc.mDepthStencilAttachment.Value(); 91 // NOTE: We're assuming callers reified this to be a view. 92 dsDesc.view = dsa.mView.GetAsGPUTextureView()->GetId(); 93 94 // - 95 96 if (dsa.mDepthLoadOp.WasPassed()) { 97 dsDesc.depth.load_op.tag = 98 ffi::WGPUFfiOption_LoadOp_FfiOption_f32_Some_LoadOp_FfiOption_f32; 99 switch (dsa.mDepthLoadOp.Value()) { 100 case dom::GPULoadOp::Load: 101 dsDesc.depth.load_op.some.tag = 102 ffi::WGPULoadOp_FfiOption_f32_Load_FfiOption_f32; 103 break; 104 case dom::GPULoadOp::Clear: 105 dsDesc.depth.load_op.some.clear_tag = 106 ffi::WGPULoadOp_FfiOption_f32_Clear_FfiOption_f32; 107 if (dsa.mDepthClearValue.WasPassed()) { 108 dsDesc.depth.load_op.some.clear.tag = 109 ffi::WGPUFfiOption_f32_Some_f32; 110 dsDesc.depth.load_op.some.clear.some = dsa.mDepthClearValue.Value(); 111 } else { 112 dsDesc.depth.load_op.some.clear.tag = 113 ffi::WGPUFfiOption_f32_None_f32; 114 } 115 break; 116 } 117 } else { 118 dsDesc.depth.load_op.tag = 119 ffi::WGPUFfiOption_LoadOp_FfiOption_f32_None_LoadOp_FfiOption_f32; 120 } 121 122 if (dsa.mDepthStoreOp.WasPassed()) { 123 dsDesc.depth.store_op.tag = ffi::WGPUFfiOption_StoreOp_Some_StoreOp; 124 dsDesc.depth.store_op.some = ConvertStoreOp(dsa.mDepthStoreOp.Value()); 125 } else { 126 dsDesc.depth.store_op.tag = ffi::WGPUFfiOption_StoreOp_None_StoreOp; 127 } 128 129 dsDesc.depth.read_only = dsa.mDepthReadOnly; 130 131 // - 132 133 if (dsa.mStencilLoadOp.WasPassed()) { 134 dsDesc.stencil.load_op.tag = 135 ffi::WGPUFfiOption_LoadOp_FfiOption_u32_Some_LoadOp_FfiOption_u32; 136 switch (dsa.mStencilLoadOp.Value()) { 137 case dom::GPULoadOp::Load: 138 dsDesc.stencil.load_op.some.tag = 139 ffi::WGPULoadOp_FfiOption_u32_Load_FfiOption_u32; 140 break; 141 case dom::GPULoadOp::Clear: 142 dsDesc.stencil.load_op.some.clear_tag = 143 ffi::WGPULoadOp_FfiOption_u32_Clear_FfiOption_u32; 144 dsDesc.stencil.load_op.some.clear.tag = 145 ffi::WGPUFfiOption_u32_Some_u32; 146 dsDesc.stencil.load_op.some.clear.some = dsa.mStencilClearValue; 147 break; 148 } 149 } else { 150 dsDesc.stencil.load_op.tag = 151 ffi::WGPUFfiOption_LoadOp_FfiOption_u32_None_LoadOp_FfiOption_u32; 152 } 153 154 if (dsa.mStencilStoreOp.WasPassed()) { 155 dsDesc.stencil.store_op.tag = ffi::WGPUFfiOption_StoreOp_Some_StoreOp; 156 dsDesc.stencil.store_op.some = 157 ConvertStoreOp(dsa.mStencilStoreOp.Value()); 158 } else { 159 dsDesc.stencil.store_op.tag = ffi::WGPUFfiOption_StoreOp_None_StoreOp; 160 } 161 162 dsDesc.stencil.read_only = dsa.mStencilReadOnly; 163 164 // - 165 166 desc.depth_stencil_attachment = &dsDesc; 167 } 168 169 AutoTArray<ffi::WGPUFfiRenderPassColorAttachment, WGPUMAX_COLOR_ATTACHMENTS> 170 colorDescs; 171 172 for (const auto& ca : aDesc.mColorAttachments) { 173 ffi::WGPUFfiRenderPassColorAttachment cd = {}; 174 // NOTE: We're assuming callers reified this to be a view. 175 cd.view = ca.mView.GetAsGPUTextureView()->GetId(); 176 cd.store_op = ConvertStoreOp(ca.mStoreOp); 177 178 if (ca.mDepthSlice.WasPassed()) { 179 cd.depth_slice.tag = ffi::WGPUFfiOption_u32_Some_u32; 180 cd.depth_slice.some = ca.mDepthSlice.Value(); 181 } else { 182 cd.depth_slice.tag = ffi::WGPUFfiOption_u32_None_u32; 183 } 184 if (ca.mResolveTarget.WasPassed()) { 185 // NOTE: We're assuming callers reified this to be a view. 186 cd.resolve_target = 187 ca.mResolveTarget.Value().GetAsGPUTextureView()->GetId(); 188 } 189 190 switch (ca.mLoadOp) { 191 case dom::GPULoadOp::Load: 192 cd.load_op.tag = ffi::WGPULoadOp_Color_Load_Color; 193 break; 194 case dom::GPULoadOp::Clear: 195 cd.load_op.clear_tag = ffi::WGPULoadOp_Color_Clear_Color; 196 if (ca.mClearValue.WasPassed()) { 197 cd.load_op.clear = ConvertColor(ca.mClearValue.Value()); 198 } else { 199 cd.load_op.clear = ffi::WGPUColor{0}; 200 } 201 break; 202 } 203 colorDescs.AppendElement(cd); 204 } 205 206 desc.color_attachments = {colorDescs.Elements(), colorDescs.Length()}; 207 208 if (aDesc.mOcclusionQuerySet.WasPassed()) { 209 desc.occlusion_query_set = aDesc.mOcclusionQuerySet.Value().GetId(); 210 } 211 212 ffi::WGPUPassTimestampWrites passTimestampWrites = {}; 213 if (aDesc.mTimestampWrites.WasPassed()) { 214 AssignPassTimestampWrites(aDesc.mTimestampWrites.Value(), 215 passTimestampWrites); 216 desc.timestamp_writes = &passTimestampWrites; 217 } 218 219 return ffi::wgpu_command_encoder_begin_render_pass(&desc); 220 } 221 222 RenderPassEncoder::RenderPassEncoder(CommandEncoder* const aParent, RawId aId, 223 const dom::GPURenderPassDescriptor& aDesc) 224 : ObjectBase(aParent->GetChild(), aId, 225 ffi::wgpu_client_drop_render_pass_encoder), 226 ChildOf(aParent), 227 mPass(BeginRenderPass(aParent, aDesc)) { 228 mValid = !!mPass; 229 if (!mValid) { 230 return; 231 } 232 233 // NOTE: We depend on callers ensuring that texture-or-view fields are reified 234 // to views. 235 236 for (const auto& at : aDesc.mColorAttachments) { 237 mUsedTextureViews.AppendElement(at.mView.GetAsGPUTextureView()); 238 } 239 if (aDesc.mDepthStencilAttachment.WasPassed()) { 240 mUsedTextureViews.AppendElement( 241 aDesc.mDepthStencilAttachment.Value().mView.GetAsGPUTextureView()); 242 } 243 } 244 245 RenderPassEncoder::~RenderPassEncoder() = default; 246 247 void RenderPassEncoder::SetBindGroup(uint32_t aSlot, 248 BindGroup* const aBindGroup, 249 const uint32_t* aDynamicOffsets, 250 size_t aDynamicOffsetsLength) { 251 RawId bindGroup = 0; 252 if (aBindGroup) { 253 mUsedBindGroups.AppendElement(aBindGroup); 254 mUsedCanvasContexts.AppendElements(aBindGroup->GetCanvasContexts()); 255 bindGroup = aBindGroup->GetId(); 256 } 257 ffi::wgpu_recorded_render_pass_set_bind_group( 258 mPass.get(), aSlot, bindGroup, {aDynamicOffsets, aDynamicOffsetsLength}); 259 } 260 261 void RenderPassEncoder::SetBindGroup( 262 uint32_t aSlot, BindGroup* const aBindGroup, 263 const dom::Sequence<uint32_t>& aDynamicOffsets, ErrorResult& aRv) { 264 if (!mValid) { 265 return; 266 } 267 this->SetBindGroup(aSlot, aBindGroup, aDynamicOffsets.Elements(), 268 aDynamicOffsets.Length()); 269 } 270 271 void RenderPassEncoder::SetBindGroup( 272 uint32_t aSlot, BindGroup* const aBindGroup, 273 const dom::Uint32Array& aDynamicOffsetsData, 274 uint64_t aDynamicOffsetsDataStart, uint64_t aDynamicOffsetsDataLength, 275 ErrorResult& aRv) { 276 if (!mValid) { 277 return; 278 } 279 280 auto dynamicOffsets = 281 GetDynamicOffsetsFromArray(aDynamicOffsetsData, aDynamicOffsetsDataStart, 282 aDynamicOffsetsDataLength, aRv); 283 284 if (dynamicOffsets.isSome()) { 285 this->SetBindGroup(aSlot, aBindGroup, dynamicOffsets->Elements(), 286 dynamicOffsets->Length()); 287 } 288 } 289 290 void RenderPassEncoder::SetPipeline(const RenderPipeline& aPipeline) { 291 if (!mValid) { 292 return; 293 } 294 mUsedPipelines.AppendElement(&aPipeline); 295 ffi::wgpu_recorded_render_pass_set_pipeline(mPass.get(), aPipeline.GetId()); 296 } 297 298 void RenderPassEncoder::SetIndexBuffer(const Buffer& aBuffer, 299 const dom::GPUIndexFormat& aIndexFormat, 300 uint64_t aOffset, 301 const dom::Optional<uint64_t>& aSize) { 302 if (!mValid) { 303 return; 304 } 305 mUsedBuffers.AppendElement(&aBuffer); 306 const auto iformat = aIndexFormat == dom::GPUIndexFormat::Uint32 307 ? ffi::WGPUIndexFormat_Uint32 308 : ffi::WGPUIndexFormat_Uint16; 309 const uint64_t* sizeRef = aSize.WasPassed() ? &aSize.Value() : nullptr; 310 ffi::wgpu_recorded_render_pass_set_index_buffer(mPass.get(), aBuffer.GetId(), 311 iformat, aOffset, sizeRef); 312 } 313 314 void RenderPassEncoder::SetVertexBuffer(uint32_t aSlot, const Buffer& aBuffer, 315 uint64_t aOffset, 316 const dom::Optional<uint64_t>& aSize) { 317 if (!mValid) { 318 return; 319 } 320 mUsedBuffers.AppendElement(&aBuffer); 321 322 const uint64_t* sizeRef = aSize.WasPassed() ? &aSize.Value() : nullptr; 323 ffi::wgpu_recorded_render_pass_set_vertex_buffer( 324 mPass.get(), aSlot, aBuffer.GetId(), aOffset, sizeRef); 325 } 326 327 void RenderPassEncoder::Draw(uint32_t aVertexCount, uint32_t aInstanceCount, 328 uint32_t aFirstVertex, uint32_t aFirstInstance) { 329 if (!mValid) { 330 return; 331 } 332 ffi::wgpu_recorded_render_pass_draw(mPass.get(), aVertexCount, aInstanceCount, 333 aFirstVertex, aFirstInstance); 334 } 335 336 void RenderPassEncoder::DrawIndexed(uint32_t aIndexCount, 337 uint32_t aInstanceCount, 338 uint32_t aFirstIndex, int32_t aBaseVertex, 339 uint32_t aFirstInstance) { 340 if (!mValid) { 341 return; 342 } 343 ffi::wgpu_recorded_render_pass_draw_indexed(mPass.get(), aIndexCount, 344 aInstanceCount, aFirstIndex, 345 aBaseVertex, aFirstInstance); 346 } 347 348 void RenderPassEncoder::DrawIndirect(const Buffer& aIndirectBuffer, 349 uint64_t aIndirectOffset) { 350 if (!mValid) { 351 return; 352 } 353 mUsedBuffers.AppendElement(&aIndirectBuffer); 354 ffi::wgpu_recorded_render_pass_draw_indirect( 355 mPass.get(), aIndirectBuffer.GetId(), aIndirectOffset); 356 } 357 358 void RenderPassEncoder::DrawIndexedIndirect(const Buffer& aIndirectBuffer, 359 uint64_t aIndirectOffset) { 360 if (!mValid) { 361 return; 362 } 363 mUsedBuffers.AppendElement(&aIndirectBuffer); 364 ffi::wgpu_recorded_render_pass_draw_indexed_indirect( 365 mPass.get(), aIndirectBuffer.GetId(), aIndirectOffset); 366 } 367 368 void RenderPassEncoder::SetViewport(float x, float y, float width, float height, 369 float minDepth, float maxDepth) { 370 if (!mValid) { 371 return; 372 } 373 ffi::wgpu_recorded_render_pass_set_viewport(mPass.get(), x, y, width, height, 374 minDepth, maxDepth); 375 } 376 377 void RenderPassEncoder::SetScissorRect(uint32_t x, uint32_t y, uint32_t width, 378 uint32_t height) { 379 if (!mValid) { 380 return; 381 } 382 ffi::wgpu_recorded_render_pass_set_scissor_rect(mPass.get(), x, y, width, 383 height); 384 } 385 386 void RenderPassEncoder::SetBlendConstant( 387 const dom::DoubleSequenceOrGPUColorDict& color) { 388 if (!mValid) { 389 return; 390 } 391 ffi::WGPUColor aColor = ConvertColor(color); 392 ffi::wgpu_recorded_render_pass_set_blend_constant(mPass.get(), &aColor); 393 } 394 395 void RenderPassEncoder::SetStencilReference(uint32_t reference) { 396 if (!mValid) { 397 return; 398 } 399 ffi::wgpu_recorded_render_pass_set_stencil_reference(mPass.get(), reference); 400 } 401 402 void RenderPassEncoder::BeginOcclusionQuery(uint32_t aQueryIndex) { 403 if (!mValid) { 404 return; 405 } 406 ffi::wgpu_recorded_render_pass_begin_occlusion_query(mPass.get(), 407 aQueryIndex); 408 } 409 410 void RenderPassEncoder::EndOcclusionQuery() { 411 if (!mValid) { 412 return; 413 } 414 ffi::wgpu_recorded_render_pass_end_occlusion_query(mPass.get()); 415 } 416 417 void RenderPassEncoder::ExecuteBundles( 418 const dom::Sequence<OwningNonNull<RenderBundle>>& aBundles) { 419 if (!mValid) { 420 return; 421 } 422 nsTArray<ffi::WGPURenderBundleId> renderBundles(aBundles.Length()); 423 for (const auto& bundle : aBundles) { 424 mUsedRenderBundles.AppendElement(bundle); 425 mUsedCanvasContexts.AppendElements(bundle->GetCanvasContexts()); 426 renderBundles.AppendElement(bundle->GetId()); 427 } 428 ffi::wgpu_recorded_render_pass_execute_bundles( 429 mPass.get(), {renderBundles.Elements(), renderBundles.Length()}); 430 } 431 432 void RenderPassEncoder::PushDebugGroup(const nsAString& aString) { 433 if (!mValid) { 434 return; 435 } 436 const NS_ConvertUTF16toUTF8 utf8(aString); 437 ffi::wgpu_recorded_render_pass_push_debug_group(mPass.get(), utf8.get(), 0); 438 } 439 void RenderPassEncoder::PopDebugGroup() { 440 if (!mValid) { 441 return; 442 } 443 ffi::wgpu_recorded_render_pass_pop_debug_group(mPass.get()); 444 } 445 void RenderPassEncoder::InsertDebugMarker(const nsAString& aString) { 446 if (!mValid) { 447 return; 448 } 449 const NS_ConvertUTF16toUTF8 utf8(aString); 450 ffi::wgpu_recorded_render_pass_insert_debug_marker(mPass.get(), utf8.get(), 451 0); 452 } 453 454 void RenderPassEncoder::End() { 455 if (mParent->GetState() != CommandEncoderState::Locked) { 456 const auto* message = "Encoding must not have ended"; 457 ffi::wgpu_report_validation_error(GetClient(), 458 mParent->GetDevice()->GetId(), message); 459 } 460 if (!mValid) { 461 return; 462 } 463 nsTArray<RefPtr<ExternalTexture>> externalTextures; 464 for (const auto& bindGroup : mUsedBindGroups) { 465 externalTextures.AppendElements(bindGroup->GetExternalTextures()); 466 } 467 MOZ_ASSERT(!!mPass); 468 mParent->EndRenderPass(*mPass, mUsedCanvasContexts, externalTextures); 469 470 mValid = false; 471 mPass.release(); 472 mUsedBindGroups.Clear(); 473 mUsedBuffers.Clear(); 474 mUsedPipelines.Clear(); 475 mUsedTextureViews.Clear(); 476 mUsedRenderBundles.Clear(); 477 } 478 479 } // namespace mozilla::webgpu