| // Copyright 2025 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #include "src/dawn/native/webgpu/CommandBufferHelpers.h" | |
| #include <vector> | |
| #include "src/dawn/native/CommandAllocator.h" | |
| #include "src/dawn/native/Commands.h" | |
| #include "src/dawn/native/webgpu/BindGroupWGPU.h" | |
| #include "src/dawn/native/webgpu/BufferWGPU.h" | |
| #include "src/dawn/native/webgpu/CaptureContext.h" | |
| #include "src/dawn/native/webgpu/ComputePipelineWGPU.h" | |
| #include "src/dawn/native/webgpu/RenderBundleWGPU.h" | |
| #include "src/dawn/native/webgpu/RenderPipelineWGPU.h" | |
| #include "src/utils/compiler.h" | |
| namespace dawn::native::webgpu { | |
| void CaptureSharedCommand(CaptureContext& captureContext, CommandIterator& commands, Command type) { | |
| switch (type) { | |
| case Command::SetBindGroup: { | |
| const auto& cmd = *commands.NextCommand<SetBindGroupCmd>(); | |
| const uint32_t* dynamicOffsetsData = | |
| cmd.dynamicOffsetCount > 0 ? commands.NextData<uint32_t>(cmd.dynamicOffsetCount) | |
| : nullptr; | |
| schema::CommandBufferCommandSetBindGroupCmd data{{ | |
| .data = {{ | |
| .index = uint32_t(cmd.index), | |
| .bindGroupId = captureContext.GetId(cmd.group), | |
| .dynamicOffsets = std::vector<uint32_t>( | |
| dynamicOffsetsData, | |
| DAWN_UNSAFE_TODO(dynamicOffsetsData + cmd.dynamicOffsetCount)), | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::SetImmediates: { | |
| const auto& cmd = *commands.NextCommand<SetImmediatesCmd>(); | |
| const uint8_t* values = commands.NextData<uint8_t>(cmd.size); | |
| schema::CommandBufferCommandSetImmediatesCmd data{{ | |
| .data = {{ | |
| .offset = cmd.offset, | |
| .data = std::vector<uint8_t>(values, DAWN_UNSAFE_TODO(values + cmd.size)), | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| default: | |
| DAWN_UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| // Returns true if command was handled | |
| void CaptureDebugCommand(CaptureContext& captureContext, CommandIterator& commands, Command type) { | |
| switch (type) { | |
| case Command::PushDebugGroup: { | |
| const auto& cmd = *commands.NextCommand<PushDebugGroupCmd>(); | |
| const char* label = commands.NextData<char>(cmd.length + 1); | |
| schema::CommandBufferCommandPushDebugGroupCmd data{{ | |
| .data = {{ | |
| .groupLabel = label, | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::PopDebugGroup: { | |
| commands.NextCommand<PopDebugGroupCmd>(); | |
| Serialize(captureContext, schema::CommandBufferCommand::PopDebugGroup); | |
| break; | |
| } | |
| case Command::InsertDebugMarker: { | |
| const auto& cmd = *commands.NextCommand<InsertDebugMarkerCmd>(); | |
| const char* label = commands.NextData<char>(cmd.length + 1); | |
| schema::CommandBufferCommandInsertDebugMarkerCmd data{{ | |
| .data = {{ | |
| .markerLabel = label, | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| default: | |
| DAWN_UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| // Captures commands common to a render pass and a render bundle | |
| MaybeError CaptureRenderCommand(CaptureContext& captureContext, | |
| CommandIterator& commands, | |
| Command type) { | |
| switch (type) { | |
| case Command::SetRenderPipeline: { | |
| const auto& cmd = *commands.NextCommand<SetRenderPipelineCmd>(); | |
| schema::CommandBufferCommandSetRenderPipelineCmd data{{ | |
| .data = {{ | |
| .pipelineId = captureContext.GetId(cmd.pipeline.Get()), | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::SetVertexBuffer: { | |
| const auto& cmd = *commands.NextCommand<SetVertexBufferCmd>(); | |
| schema::CommandBufferCommandSetVertexBufferCmd data{{ | |
| .data = {{ | |
| .slot = uint32_t(cmd.slot), | |
| .bufferId = captureContext.GetId(cmd.buffer), | |
| .offset = cmd.offset, | |
| .size = cmd.size, | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::SetIndexBuffer: { | |
| const auto& cmd = *commands.NextCommand<SetIndexBufferCmd>(); | |
| schema::CommandBufferCommandSetIndexBufferCmd data{{ | |
| .data = {{ | |
| .bufferId = captureContext.GetId(cmd.buffer), | |
| .format = cmd.format, | |
| .offset = cmd.offset, | |
| .size = cmd.size, | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::Draw: { | |
| const auto& cmd = *commands.NextCommand<DrawCmd>(); | |
| schema::CommandBufferCommandDrawCmd data{{ | |
| .data = {{ | |
| .vertexCount = cmd.vertexCount, | |
| .instanceCount = cmd.instanceCount, | |
| .firstVertex = cmd.firstVertex, | |
| .firstInstance = cmd.firstInstance, | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::DrawIndexed: { | |
| const auto& cmd = *commands.NextCommand<DrawIndexedCmd>(); | |
| schema::CommandBufferCommandDrawIndexedCmd data{{ | |
| .data = {{ | |
| .indexCount = cmd.indexCount, | |
| .instanceCount = cmd.instanceCount, | |
| .firstIndex = cmd.firstIndex, | |
| .baseVertex = cmd.baseVertex, | |
| .firstInstance = cmd.firstInstance, | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::DrawIndirect: { | |
| const auto& cmd = *commands.NextCommand<DrawIndirectCmd>(); | |
| schema::CommandBufferCommandDrawIndirectCmd data{{ | |
| .data = {{ | |
| .indirectBufferId = captureContext.GetId(cmd.indirectBuffer), | |
| .indirectOffset = cmd.indirectOffset, | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::DrawIndexedIndirect: { | |
| const auto& cmd = *commands.NextCommand<DrawIndexedIndirectCmd>(); | |
| schema::CommandBufferCommandDrawIndexedIndirectCmd data{{ | |
| .data = {{ | |
| .indirectBufferId = captureContext.GetId(cmd.indirectBuffer), | |
| .indirectOffset = cmd.indirectOffset, | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::SetBindGroup: | |
| case Command::SetImmediates: | |
| CaptureSharedCommand(captureContext, commands, type); | |
| break; | |
| case Command::PushDebugGroup: | |
| case Command::PopDebugGroup: | |
| case Command::InsertDebugMarker: | |
| CaptureDebugCommand(captureContext, commands, type); | |
| break; | |
| default: | |
| return DAWN_UNIMPLEMENTED_ERROR("Unimplemented command"); | |
| } | |
| return {}; | |
| } | |
| // Gathers resources used by commands from both render passes and render bundles. | |
| MaybeError GatherReferencedResourcesFromRenderCommand(CaptureContext& captureContext, | |
| CommandIterator& commands, | |
| CommandBufferResourceUsages& usedResources, | |
| Command type) { | |
| switch (type) { | |
| case Command::SetRenderPipeline: { | |
| auto cmd = commands.NextCommand<SetRenderPipelineCmd>(); | |
| usedResources.renderPipelines.push_back(cmd->pipeline.Get()); | |
| break; | |
| } | |
| case Command::SetBindGroup: { | |
| auto cmd = commands.NextCommand<SetBindGroupCmd>(); | |
| if (cmd->dynamicOffsetCount > 0) { | |
| commands.NextData<uint32_t>(cmd->dynamicOffsetCount); | |
| } | |
| usedResources.bindGroups.push_back(cmd->group.Get()); | |
| break; | |
| } | |
| case Command::BeginOcclusionQuery: | |
| case Command::EndOcclusionQuery: | |
| case Command::SetBlendConstant: | |
| case Command::SetScissorRect: | |
| case Command::SetStencilReference: | |
| case Command::SetViewport: | |
| case Command::Draw: | |
| case Command::DrawIndexed: | |
| case Command::DrawIndirect: | |
| case Command::DrawIndexedIndirect: | |
| case Command::WriteTimestamp: | |
| case Command::SetImmediates: | |
| case Command::SetIndexBuffer: | |
| case Command::SetVertexBuffer: | |
| case Command::PushDebugGroup: | |
| case Command::InsertDebugMarker: | |
| case Command::PopDebugGroup: | |
| SkipCommand(&commands, type); | |
| break; | |
| default: | |
| return DAWN_UNIMPLEMENTED_ERROR("Unimplemented command"); | |
| } | |
| return {}; | |
| } | |
| MaybeError AddUsedResources(CaptureContext& captureContext, | |
| const CommandBufferResourceUsages& usedResources) { | |
| for (auto pipeline : usedResources.computePipelines) { | |
| DAWN_TRY(captureContext.AddResource(ToBackend(pipeline))); | |
| } | |
| for (auto pipeline : usedResources.renderPipelines) { | |
| DAWN_TRY(captureContext.AddResource(ToBackend(pipeline))); | |
| } | |
| for (auto bindGroup : usedResources.bindGroups) { | |
| DAWN_TRY(captureContext.AddResource(ToBackend(bindGroup))); | |
| } | |
| for (auto bundle : usedResources.renderBundles) { | |
| DAWN_TRY(captureContext.AddResource(ToBackend(bundle))); | |
| } | |
| return {}; | |
| } | |
| } // namespace dawn::native::webgpu |