| // 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>(); | |
| ityp::span<BindingIndex, const uint32_t> dynamicOffsets; | |
| if (cmd.dynamicOffsetCount != BindingIndex{0u}) { | |
| dynamicOffsets = commands.NextData<uint32_t>(cmd.dynamicOffsetCount); | |
| } | |
| schema::CommandBufferCommandSetBindGroupCmd data{{ | |
| .data = {{ | |
| .index = uint32_t{cmd.index}, | |
| .bindGroupId = captureContext.GetId(cmd.group), | |
| .dynamicOffsets = | |
| std::vector<uint32_t>(dynamicOffsets.begin(), dynamicOffsets.end()), | |
| }}, | |
| }}; | |
| Serialize(captureContext, data); | |
| break; | |
| } | |
| case Command::SetImmediates: { | |
| const auto& cmd = *commands.NextCommand<SetImmediatesCmd>(); | |
| Span<const uint8_t> immediateData = commands.NextData<uint8_t>(cmd.size); | |
| schema::CommandBufferCommandSetImmediatesCmd data{{ | |
| .data = {{ | |
| .offset = cmd.offset, | |
| .data = std::vector<uint8_t>(immediateData.begin(), immediateData.end()), | |
| }}, | |
| }}; | |
| 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>(); | |
| std::string_view label = NextNullTerminatedString(&commands, cmd.length); | |
| schema::CommandBufferCommandPushDebugGroupCmd data{{ | |
| .data = {{ | |
| .groupLabel = {label.begin(), label.end()}, | |
| }}, | |
| }}; | |
| 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>(); | |
| std::string_view label = NextNullTerminatedString(&commands, cmd.length); | |
| schema::CommandBufferCommandInsertDebugMarkerCmd data{{ | |
| .data = {{ | |
| .markerLabel = {label.begin(), label.end()}, | |
| }}, | |
| }}; | |
| 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 > BindingIndex{0u}) { | |
| 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 |