Capture: Capture Render Pass
Bug: 413053623
Change-Id: I6a6a6964acb6cf83a5b62bc65fa0d45eda683c15
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/266277
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Shrek Shao <shrekshao@google.com>
Commit-Queue: Gregg Tavares <gman@chromium.org>
diff --git a/src/dawn/native/webgpu/BindGroupWGPU.cpp b/src/dawn/native/webgpu/BindGroupWGPU.cpp
index 139aa6d..a5ceaa0 100644
--- a/src/dawn/native/webgpu/BindGroupWGPU.cpp
+++ b/src/dawn/native/webgpu/BindGroupWGPU.cpp
@@ -37,6 +37,7 @@
#include "dawn/native/webgpu/CaptureContext.h"
#include "dawn/native/webgpu/ComputePipelineWGPU.h"
#include "dawn/native/webgpu/DeviceWGPU.h"
+#include "dawn/native/webgpu/RenderPipelineWGPU.h"
#include "dawn/native/webgpu/SamplerWGPU.h"
#include "dawn/native/webgpu/TextureWGPU.h"
@@ -116,9 +117,8 @@
}
MaybeError BindGroup::AddReferenced(CaptureContext& captureContext) {
- // We shouldn't need to include any referenced bound objects as we only serialize from
- // setBindGroup in a command buffer and the command buffer itself should already reference all.
- // We do need to reference the layout though.
+ // We have to include any referenced bound textures views as the front end does
+ // not track texture views.
//
// Unfortunately we can't just call `AddResource(layout)` because that would add a call to
// createBindGroupLayout which we only want if the bindGroupLayout was not implicit. If
@@ -132,6 +132,22 @@
DAWN_CHECK(false);
}
+ {
+ BindGroupLayoutInternalBase* layout = GetLayout();
+ const auto& bindingMap = layout->GetBindingMap();
+ for (const auto& [bindingNumbers, apiBindingIndex] : bindingMap) {
+ BindingIndex bindingIndex = layout->AsBindingIndex(apiBindingIndex);
+ const auto& bindingInfo = layout->GetAPIBindingInfo(apiBindingIndex);
+
+ DAWN_TRY(MatchVariant(
+ bindingInfo.bindingLayout,
+ [&](const TextureBindingInfo& info) -> MaybeError {
+ return captureContext.AddResource(GetBindingAsTextureView(bindingIndex));
+ },
+ [&](const auto& info) -> MaybeError { return {}; }));
+ }
+ }
+
return {};
}
diff --git a/src/dawn/native/webgpu/CaptureContext.cpp b/src/dawn/native/webgpu/CaptureContext.cpp
index fac7831..df07d2d 100644
--- a/src/dawn/native/webgpu/CaptureContext.cpp
+++ b/src/dawn/native/webgpu/CaptureContext.cpp
@@ -179,6 +179,15 @@
}};
}
+schema::Color ToSchema(const Color& color) {
+ return {{
+ .r = color.r,
+ .g = color.g,
+ .b = color.b,
+ .a = color.a,
+ }};
+}
+
schema::TexelCopyBufferLayout ToSchema(const BufferCopy& bufferCopy) {
return {{
.offset = bufferCopy.offset,
diff --git a/src/dawn/native/webgpu/CaptureContext.h b/src/dawn/native/webgpu/CaptureContext.h
index e92e16e..ab3cae2 100644
--- a/src/dawn/native/webgpu/CaptureContext.h
+++ b/src/dawn/native/webgpu/CaptureContext.h
@@ -42,6 +42,7 @@
namespace dawn::native {
class BufferBase;
+struct Color;
struct Origin3D;
struct Extent3D;
struct BufferCopy;
@@ -186,6 +187,7 @@
wgpu::TextureAspect ToDawn(const Aspect aspect);
schema::Origin3D ToSchema(const Origin3D& origin);
schema::Extent3D ToSchema(const Extent3D& extent);
+schema::Color ToSchema(const Color& color);
schema::ProgrammableStage ToSchema(CaptureContext& captureContext, const ProgrammableStage& stage);
schema::TexelCopyBufferLayout ToSchema(const BufferCopy& bufferCopy);
schema::TexelCopyBufferInfo ToSchema(CaptureContext& captureContext, const BufferCopy& bufferCopy);
diff --git a/src/dawn/native/webgpu/CommandBufferWGPU.cpp b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
index c3a9a54..5a3c61d 100644
--- a/src/dawn/native/webgpu/CommandBufferWGPU.cpp
+++ b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
@@ -67,6 +67,7 @@
// does not outlast a CommandBuffer which itself uses Refs.
struct CommandBufferResourceUsages {
std::vector<ComputePipelineBase*> computePipelines;
+ std::vector<RenderPipelineBase*> renderPipelines;
std::vector<BindGroupBase*> bindGroups;
};
@@ -625,6 +626,47 @@
return {};
}
+MaybeError GatherReferencedResourcesFromRenderPass(CaptureContext& captureContext,
+ CommandIterator& commands,
+ CommandBufferResourceUsages& usedResources) {
+ Command type;
+ while (commands.NextCommandId(&type)) {
+ switch (type) {
+ case Command::EndRenderPass: {
+ commands.NextCommand<EndRenderPassCmd>();
+ return {};
+ }
+ case Command::SetRenderPipeline: {
+ auto cmd = commands.NextCommand<SetRenderPipelineCmd>();
+ usedResources.renderPipelines.push_back(cmd->pipeline.Get());
+ break;
+ }
+ case Command::SetBindGroup: {
+ auto cmd = commands.NextCommand<SetBindGroupCmd>();
+ usedResources.bindGroups.push_back(cmd->group.Get());
+ break;
+ }
+ DAWN_SKIP_COMMAND(Draw)
+ DAWN_SKIP_COMMAND(DrawIndexed)
+ DAWN_SKIP_COMMAND(DrawIndirect)
+ DAWN_SKIP_COMMAND(DrawIndexedIndirect)
+ DAWN_SKIP_COMMAND(InsertDebugMarker)
+ DAWN_SKIP_COMMAND(PopDebugGroup)
+ DAWN_SKIP_COMMAND(PushDebugGroup)
+ DAWN_SKIP_COMMAND(WriteTimestamp)
+ DAWN_SKIP_COMMAND(SetImmediateData)
+ default: {
+ DAWN_CHECK(false);
+ break;
+ }
+ }
+ }
+
+ // EndComputePass should have been called
+ DAWN_UNREACHABLE();
+ return {};
+}
+
MaybeError CaptureComputePass(CaptureContext& captureContext, CommandIterator& commands) {
Command type;
while (commands.NextCommandId(&type)) {
@@ -682,6 +724,63 @@
return {};
}
+MaybeError CaptureRenderPass(CaptureContext& captureContext, CommandIterator& commands) {
+ Command type;
+ while (commands.NextCommandId(&type)) {
+ switch (type) {
+ case Command::EndRenderPass: {
+ commands.NextCommand<EndRenderPassCmd>();
+ Serialize(captureContext, schema::RenderPassCommand::End);
+ return {};
+ }
+ case Command::SetRenderPipeline: {
+ const auto& cmd = *commands.NextCommand<SetRenderPipelineCmd>();
+ schema::RenderPassCommandSetPipelineCmd data{{
+ .data = {{
+ .pipelineId = captureContext.GetId(cmd.pipeline.Get()),
+ }},
+ }};
+ Serialize(captureContext, data);
+ break;
+ }
+ case Command::SetBindGroup: {
+ const auto& cmd = *commands.NextCommand<SetBindGroupCmd>();
+ const uint32_t* dynamicOffsetsData =
+ cmd.dynamicOffsetCount > 0 ? commands.NextData<uint32_t>(cmd.dynamicOffsetCount)
+ : nullptr;
+ if (ShouldCaptureBindGroup(captureContext, cmd.group.Get())) {
+ schema::RenderPassCommandSetBindGroupCmd data{{
+ .data = {{
+ .index = uint32_t(cmd.index),
+ .bindGroupId = captureContext.GetId(cmd.group),
+ .dynamicOffsets = std::vector<uint32_t>(
+ dynamicOffsetsData, dynamicOffsetsData + cmd.dynamicOffsetCount),
+ }},
+ }};
+ Serialize(captureContext, data);
+ }
+ break;
+ }
+ case Command::Draw: {
+ const auto& cmd = *commands.NextCommand<DrawCmd>();
+ schema::RenderPassCommandDrawCmd data{{
+ .data = {{
+ .vertexCount = cmd.vertexCount,
+ .instanceCount = cmd.instanceCount,
+ .firstVertex = cmd.firstVertex,
+ .firstInstance = cmd.firstInstance,
+ }},
+ }};
+ Serialize(captureContext, data);
+ break;
+ }
+ default:
+ DAWN_CHECK(false);
+ }
+ }
+ return {};
+}
+
template <typename T>
MaybeError AddReferencedPassResourceUsages(CaptureContext& captureContext,
const std::vector<T>& syncScopeResourceUsages) {
@@ -748,10 +847,35 @@
switch (type) {
case Command::BeginComputePass: {
commands.NextCommand<BeginComputePassCmd>();
+ // TODO(451389800): Handle QuerySet
+ // if (cmd.timestampWrites.querySet != nullptr) {
+ // DAWN_TRY(captureContext.AddResource(cmd.timestampWrites.querySet.Get()));
+ // }
DAWN_TRY(GatherReferencedResourcesFromComputePass(captureContext, commands,
usedResources));
break;
}
+ case Command::BeginRenderPass: {
+ const auto& cmd = *commands.NextCommand<BeginRenderPassCmd>();
+ for (const auto& attachment : cmd.colorAttachments) {
+ if (attachment.view != nullptr) {
+ DAWN_TRY(captureContext.AddResource(attachment.view.Get()));
+ }
+ if (attachment.resolveTarget != nullptr) {
+ DAWN_TRY(captureContext.AddResource(attachment.resolveTarget.Get()));
+ }
+ }
+ if (cmd.depthStencilAttachment.view != nullptr) {
+ DAWN_TRY(captureContext.AddResource(cmd.depthStencilAttachment.view.Get()));
+ }
+ // TODO(451389800): Handle QuerySet
+ // if (cmd.timestampWrites.querySet != nullptr) {
+ // DAWN_TRY(captureContext.AddResource(cmd.timestampWrites.querySet.Get()));
+ // }
+ DAWN_TRY(GatherReferencedResourcesFromRenderPass(captureContext, commands,
+ usedResources));
+ break;
+ }
DAWN_SKIP_COMMAND(CopyBufferToBuffer)
DAWN_SKIP_COMMAND(CopyBufferToTexture)
DAWN_SKIP_COMMAND(CopyTextureToBuffer)
@@ -768,6 +892,9 @@
for (auto pipeline : usedResources.computePipelines) {
DAWN_TRY(captureContext.AddResource(pipeline));
}
+ for (auto pipeline : usedResources.renderPipelines) {
+ DAWN_TRY(captureContext.AddResource(pipeline));
+ }
for (auto bindGroup : usedResources.bindGroups) {
if (ShouldCaptureBindGroup(captureContext, bindGroup)) {
DAWN_TRY(captureContext.AddResource(bindGroup));
@@ -777,6 +904,36 @@
return {};
}
+schema::ColorAttachment ToSchema(CaptureContext& captureContext,
+ const RenderPassColorAttachmentInfo& info) {
+ return {{
+ .viewId = captureContext.GetId(info.view),
+ .depthSlice = info.view->GetDimension() == wgpu::TextureViewDimension::e3D
+ ? info.depthSlice
+ : wgpu::kDepthSliceUndefined,
+ .resolveTargetId = captureContext.GetId(info.resolveTarget),
+ .loadOp = info.loadOp,
+ .storeOp = info.storeOp,
+ .clearValue = ToSchema(info.clearColor),
+ }};
+}
+
+schema::RenderPassDepthStencilAttachment ToSchema(
+ CaptureContext& captureContext,
+ const RenderPassDepthStencilAttachmentInfo& info) {
+ return {{
+ .viewId = captureContext.GetId(info.view),
+ .depthLoadOp = info.depthLoadOp,
+ .depthStoreOp = info.depthStoreOp,
+ .depthClearValue = info.clearDepth,
+ .depthReadOnly = info.depthReadOnly,
+ .stencilLoadOp = info.stencilLoadOp,
+ .stencilStoreOp = info.stencilStoreOp,
+ .stencilClearValue = info.clearStencil,
+ .stencilReadOnly = info.stencilReadOnly,
+ }};
+}
+
MaybeError CommandBuffer::CaptureCreationParameters(CaptureContext& captureContext) {
return UseCommands([&](CommandIterator& commands) -> MaybeError {
Command type;
@@ -845,6 +1002,31 @@
DAWN_TRY(CaptureComputePass(captureContext, commands));
break;
}
+ case Command::BeginRenderPass: {
+ const auto& cmd = *commands.NextCommand<BeginRenderPassCmd>();
+ std::vector<schema::ColorAttachment> colorAttachments;
+ for (ColorAttachmentIndex i : cmd.attachmentState->GetColorAttachmentsMask()) {
+ colorAttachments.push_back(
+ ToSchema(captureContext, cmd.colorAttachments[i]));
+ }
+ schema::EncoderCommandBeginRenderPassCmd data{{
+ .data = {{
+ .label = cmd.label,
+ .colorAttachments = colorAttachments,
+ .depthStencilAttachment =
+ ToSchema(captureContext, cmd.depthStencilAttachment),
+ // TODO(451389800): Handle QuerySet
+ // .occlusionQuerySetId =
+ // captureContext.GetId(cmd.occlusionQuerySet.Get()),
+ .occlusionQuerySetId = 0,
+ .timestampWrites = ToSchema(captureContext, cmd.timestampWrites),
+ }},
+ }};
+ Serialize(captureContext, data);
+ // Capture commands inside the compute pass
+ DAWN_TRY(CaptureRenderPass(captureContext, commands));
+ break;
+ }
default:
DAWN_CHECK(false);
}
diff --git a/src/dawn/native/webgpu/RenderPipelineWGPU.cpp b/src/dawn/native/webgpu/RenderPipelineWGPU.cpp
index 87e1fa6..1307fea 100644
--- a/src/dawn/native/webgpu/RenderPipelineWGPU.cpp
+++ b/src/dawn/native/webgpu/RenderPipelineWGPU.cpp
@@ -30,6 +30,8 @@
#include <string>
#include <vector>
#include "dawn/common/StringViewUtils.h"
+#include "dawn/native/webgpu/BindGroupLayoutWGPU.h"
+#include "dawn/native/webgpu/CaptureContext.h"
#include "dawn/native/webgpu/DeviceWGPU.h"
#include "dawn/native/webgpu/PipelineLayoutWGPU.h"
#include "dawn/native/webgpu/ShaderModuleWGPU.h"
@@ -46,7 +48,9 @@
RenderPipeline::RenderPipeline(Device* device,
const UnpackedPtr<RenderPipelineDescriptor>& descriptor)
- : RenderPipelineBase(device, descriptor), ObjectWGPU(device->wgpu.renderPipelineRelease) {}
+ : RenderPipelineBase(device, descriptor),
+ RecordableObject(schema::ObjectType::RenderPipeline),
+ ObjectWGPU(device->wgpu.renderPipelineRelease) {}
MaybeError RenderPipeline::InitializeImpl() {
auto device = ToBackend(GetDevice());
@@ -185,4 +189,133 @@
return {};
}
+MaybeError RenderPipeline::AddReferenced(CaptureContext& captureContext) {
+ DAWN_TRY(captureContext.AddResource(GetStage(SingleShaderStage::Vertex).module.Get()));
+ if (HasStage(SingleShaderStage::Fragment)) {
+ DAWN_TRY(captureContext.AddResource(GetStage(SingleShaderStage::Fragment).module.Get()));
+ }
+ PipelineLayoutBase* pipelineLayout = GetLayout();
+ if (!pipelineLayout->IsImplicit()) {
+ // TODO(452983510): add support for explicit pipeline layout
+ // DAWN_TRY(captureContext.AddResource(pipelineLayout));
+ return DAWN_INTERNAL_ERROR("explicit pipeline layout unsupported");
+ }
+ return {};
+}
+
+schema::BlendComponent ToSchema(const BlendComponent* component) {
+ const BlendComponent& c = component ? *component : BlendComponent();
+ return {{
+ .operation = c.operation,
+ .srcFactor = c.srcFactor,
+ .dstFactor = c.dstFactor,
+ }};
+}
+
+schema::StencilFaceState ToSchema(const StencilFaceState& state) {
+ return {{
+ .compare = state.compare,
+ .failOp = state.failOp,
+ .depthFailOp = state.depthFailOp,
+ .passOp = state.passOp,
+ }};
+}
+
+MaybeError RenderPipeline::CaptureCreationParameters(CaptureContext& captureContext) {
+ schema::ObjectId layoutId = 0;
+ std::vector<schema::BindGroupLayoutIndexIdPair> groupIndexIds;
+
+ PipelineLayoutBase* pipelineLayout = GetLayout();
+ if (pipelineLayout->IsImplicit()) {
+ // If it's an implicit layout then, on playback, we need to add the bind group layouts
+ // to the id to resource map as there is no other connection.
+ for (BindGroupIndex groupIndex : pipelineLayout->GetBindGroupLayoutsMask()) {
+ BindGroupLayoutBase* bgl = pipelineLayout->GetFrontendBindGroupLayout(groupIndex);
+
+ groupIndexIds.push_back(schema::BindGroupLayoutIndexIdPair{{
+ .groupIndex = uint32_t(groupIndex),
+ .bindGroupLayoutId = captureContext.AddAndGetIdForImplicitResource(bgl),
+ }});
+ }
+ } else {
+ layoutId = captureContext.GetId(pipelineLayout);
+ }
+
+ std::vector<schema::VertexBufferLayout> buffers;
+ for (VertexBufferSlot slot : GetVertexBuffersUsed()) {
+ const auto& info = GetVertexBuffer(slot);
+
+ std::vector<schema::VertexAttribute> attributes;
+ // TODO(454365240): handle attributes
+
+ buffers.push_back({{
+ .arrayStride = info.arrayStride,
+ .stepMode = info.stepMode,
+ .attributes = attributes,
+ }});
+ }
+
+ const DepthStencilState defaultDepthStencilState;
+ const DepthStencilState* depthStencilState = GetDepthStencilState();
+ if (!depthStencilState) {
+ depthStencilState = &defaultDepthStencilState;
+ }
+ ProgrammableStage empty;
+ const ProgrammableStage& fragment =
+ HasStage(SingleShaderStage::Fragment) ? GetStage(SingleShaderStage::Fragment) : empty;
+ std::vector<schema::ColorTargetState> targets;
+ if (fragment.module != nullptr) {
+ for (auto slot : GetColorAttachmentsMask()) {
+ const auto& target = *GetColorTargetState(slot);
+ targets.push_back({{
+ .format = target.format,
+ .blend{{
+ .color = ToSchema(target.blend ? &target.blend->color : nullptr),
+ .alpha = ToSchema(target.blend ? &target.blend->alpha : nullptr),
+ }},
+ .writeMask = target.writeMask,
+ }});
+ }
+ }
+
+ schema::RenderPipeline data{{
+ .layoutId = layoutId,
+ .vertex{{
+ .program = ToSchema(captureContext, GetStage(SingleShaderStage::Vertex)),
+ .buffers = buffers,
+ }},
+ .primitive{{
+ .topology = GetPrimitiveTopology(),
+ .stripIndexFormat = GetStripIndexFormat(),
+ .frontFace = GetFrontFace(),
+ .cullMode = GetCullMode(),
+ .unclippedDepth = HasUnclippedDepth(),
+ }},
+ .depthStencil{{
+ .format = depthStencilState->format,
+ .depthWriteEnabled = depthStencilState->depthWriteEnabled == wgpu::OptionalBool(true),
+ .depthCompare = depthStencilState->depthCompare,
+ .stencilFront = ToSchema(depthStencilState->stencilFront),
+ .stencilBack = ToSchema(depthStencilState->stencilBack),
+ .stencilReadMask = depthStencilState->stencilReadMask,
+ .stencilWriteMask = depthStencilState->stencilWriteMask,
+ .depthBias = depthStencilState->depthBias,
+ .depthBiasSlopeScale = depthStencilState->depthBiasSlopeScale,
+ .depthBiasClamp = depthStencilState->depthBiasClamp,
+ }},
+ .multisample{{
+ .count = GetSampleCount(),
+ .mask = GetSampleMask(),
+ .alphaToCoverageEnabled = IsAlphaToCoverageEnabled(),
+ }},
+ .fragment{{
+ .program = ToSchema(captureContext, fragment),
+ .targets = targets,
+ }},
+ .groupIndexIds = groupIndexIds,
+ }};
+ Serialize(captureContext, data);
+ return {};
+}
+
} // namespace dawn::native::webgpu
diff --git a/src/dawn/native/webgpu/RenderPipelineWGPU.h b/src/dawn/native/webgpu/RenderPipelineWGPU.h
index 4ab966c..081e679 100644
--- a/src/dawn/native/webgpu/RenderPipelineWGPU.h
+++ b/src/dawn/native/webgpu/RenderPipelineWGPU.h
@@ -32,18 +32,24 @@
#include "dawn/common/Constants.h"
#include "dawn/native/RenderPipeline.h"
#include "dawn/native/webgpu/ObjectWGPU.h"
+#include "dawn/native/webgpu/RecordableObject.h"
namespace dawn::native::webgpu {
class Device;
-class RenderPipeline final : public RenderPipelineBase, public ObjectWGPU<WGPURenderPipeline> {
+class RenderPipeline final : public RenderPipelineBase,
+ public RecordableObject,
+ public ObjectWGPU<WGPURenderPipeline> {
public:
static Ref<RenderPipeline> CreateUninitialized(
Device* device,
const UnpackedPtr<RenderPipelineDescriptor>& descriptor);
MaybeError InitializeImpl() override;
+ MaybeError AddReferenced(CaptureContext& captureContext) override;
+ MaybeError CaptureCreationParameters(CaptureContext& context) override;
+
protected:
RenderPipeline(Device* device, const UnpackedPtr<RenderPipelineDescriptor>& descriptor);
};
diff --git a/src/dawn/native/webgpu/Serialization.cpp b/src/dawn/native/webgpu/Serialization.cpp
index 04c0b44..4fe0118 100644
--- a/src/dawn/native/webgpu/Serialization.cpp
+++ b/src/dawn/native/webgpu/Serialization.cpp
@@ -49,10 +49,18 @@
WriteBytes(context, reinterpret_cast<const char*>(&v), sizeof(v));
}
+void Serialize(CaptureContext& context, float v) {
+ WriteBytes(context, reinterpret_cast<const char*>(&v), sizeof(v));
+}
+
void Serialize(CaptureContext& context, double v) {
WriteBytes(context, reinterpret_cast<const char*>(&v), sizeof(v));
}
+void Serialize(CaptureContext& context, bool v) {
+ WriteBytes(context, reinterpret_cast<const char*>(&v), sizeof(v));
+}
+
void Serialize(CaptureContext& context, const std::string& v) {
Serialize(context, v.size());
WriteBytes(context, v.data(), v.size());
diff --git a/src/dawn/native/webgpu/Serialization.h b/src/dawn/native/webgpu/Serialization.h
index 14c2d32..27c8d9e 100644
--- a/src/dawn/native/webgpu/Serialization.h
+++ b/src/dawn/native/webgpu/Serialization.h
@@ -33,7 +33,9 @@
void Serialize(CaptureContext& context, int32_t v);
void Serialize(CaptureContext& context, uint32_t v);
void Serialize(CaptureContext& context, uint64_t v);
+void Serialize(CaptureContext& context, float v);
void Serialize(CaptureContext& context, double v);
+void Serialize(CaptureContext& context, bool v);
void Serialize(CaptureContext& context, const std::string& v);
template <typename T>
@@ -150,6 +152,10 @@
struct CmdType##CmdName##Cmd : CmdType##CmdName##Cmd##__Contents, \
public Serializable<CmdType##CmdName##Cmd>
+// Makes both a CmdData and a Cmd struct for a given render pass command name.
+#define DAWN_REPLAY_MAKE_RENDER_PASS_CMD_AND_CMD_DATA(CmdName, CMD_MEMBERS) \
+ DAWN_REPLAY_MAKE_CMD_AND_CMD_DATA(RenderPassCommand, CmdName, CMD_MEMBERS)
+
// Makes both a CmdData and a Cmd struct for a given compute pass command name.
#define DAWN_REPLAY_MAKE_COMPUTE_PASS_CMD_AND_CMD_DATA(CmdName, CMD_MEMBERS) \
DAWN_REPLAY_MAKE_CMD_AND_CMD_DATA(ComputePassCommand, CmdName, CMD_MEMBERS)
diff --git a/src/dawn/replay/Deserialization.cpp b/src/dawn/replay/Deserialization.cpp
index 04a54ab..694268e 100644
--- a/src/dawn/replay/Deserialization.cpp
+++ b/src/dawn/replay/Deserialization.cpp
@@ -51,10 +51,18 @@
return ReadBytes(s, reinterpret_cast<char*>(v), sizeof(*v));
}
+MaybeError Deserialize(ReadHead& s, float* v) {
+ return ReadBytes(s, reinterpret_cast<char*>(v), sizeof(*v));
+}
+
MaybeError Deserialize(ReadHead& s, double* v) {
return ReadBytes(s, reinterpret_cast<char*>(v), sizeof(*v));
}
+MaybeError Deserialize(ReadHead& s, bool* v) {
+ return ReadBytes(s, reinterpret_cast<char*>(v), sizeof(*v));
+}
+
MaybeError Deserialize(ReadHead& s, std::string* v) {
size_t size = 0;
DAWN_TRY(Deserialize(s, &size));
diff --git a/src/dawn/replay/Deserialization.h b/src/dawn/replay/Deserialization.h
index ec33a92..5579fba 100644
--- a/src/dawn/replay/Deserialization.h
+++ b/src/dawn/replay/Deserialization.h
@@ -34,7 +34,9 @@
MaybeError Deserialize(ReadHead& s, int32_t* v);
MaybeError Deserialize(ReadHead& s, uint32_t* v);
MaybeError Deserialize(ReadHead& s, uint64_t* v);
+MaybeError Deserialize(ReadHead& s, float* v);
MaybeError Deserialize(ReadHead& s, double* v);
+MaybeError Deserialize(ReadHead& s, bool* v);
MaybeError Deserialize(ReadHead& s, std::string* v);
template <typename T>
@@ -159,6 +161,10 @@
struct CmdType##CmdName##Cmd : CmdType##CmdName##Cmd##__Contents, \
public ::dawn::replay::Deserializable<CmdType##CmdName##Cmd>
+// Makes both a CmdData and a Cmd struct for a given render pass command name.
+#define DAWN_REPLAY_MAKE_RENDER_PASS_CMD_AND_CMD_DATA(CmdName, CMD_MEMBERS) \
+ DAWN_REPLAY_MAKE_CMD_AND_CMD_DATA(RenderPassCommand, CmdName, CMD_MEMBERS)
+
// Makes both a CmdData and a Cmd struct for a given compute pass command name.
#define DAWN_REPLAY_MAKE_COMPUTE_PASS_CMD_AND_CMD_DATA(CmdName, CMD_MEMBERS) \
DAWN_REPLAY_MAKE_CMD_AND_CMD_DATA(ComputePassCommand, CmdName, CMD_MEMBERS)
diff --git a/src/dawn/replay/Replay.cpp b/src/dawn/replay/Replay.cpp
index ac59327..41c52f1 100644
--- a/src/dawn/replay/Replay.cpp
+++ b/src/dawn/replay/Replay.cpp
@@ -51,6 +51,15 @@
};
}
+wgpu::Color ToWGPU(const schema::Color& color) {
+ return wgpu::Color{
+ .r = color.r,
+ .g = color.g,
+ .b = color.b,
+ .a = color.a,
+ };
+}
+
wgpu::PassTimestampWrites ToWGPU(const Replay& replay, const schema::TimestampWrites& writes) {
return wgpu::PassTimestampWrites{
.nextInChain = nullptr,
@@ -324,6 +333,47 @@
return DAWN_INTERNAL_ERROR("Missing ComputePass End command");
}
+MaybeError ProcessRenderPassCommands(const Replay& replay,
+ ReadHead& readHead,
+ wgpu::Device device,
+ wgpu::RenderPassEncoder pass) {
+ schema::RenderPassCommand cmd;
+
+ while (!readHead.IsDone()) {
+ DAWN_TRY(Deserialize(readHead, &cmd));
+ switch (cmd) {
+ case schema::RenderPassCommand::End: {
+ pass.End();
+ return {};
+ }
+ case schema::RenderPassCommand::SetPipeline: {
+ schema::RenderPassCommandSetPipelineCmdData data;
+ DAWN_TRY(Deserialize(readHead, &data));
+ pass.SetPipeline(replay.GetObjectById<wgpu::RenderPipeline>(data.pipelineId));
+ break;
+ }
+ case schema::RenderPassCommand::SetBindGroup: {
+ schema::RenderPassCommandSetBindGroupCmdData data;
+ DAWN_TRY(Deserialize(readHead, &data));
+ pass.SetBindGroup(data.index,
+ replay.GetObjectById<wgpu::BindGroup>(data.bindGroupId),
+ data.dynamicOffsets.size(), data.dynamicOffsets.data());
+ break;
+ }
+ case schema::RenderPassCommand::Draw: {
+ schema::RenderPassCommandDrawCmdData data;
+ DAWN_TRY(Deserialize(readHead, &data));
+ pass.Draw(data.vertexCount, data.instanceCount, data.firstVertex,
+ data.firstInstance);
+ break;
+ }
+ default:
+ return DAWN_INTERNAL_ERROR("Render Pass Command not implemented");
+ }
+ }
+ return DAWN_INTERNAL_ERROR("Missing RenderPass End command");
+}
+
MaybeError ProcessEncoderCommands(const Replay& replay,
ReadHead& readHead,
wgpu::Device device,
@@ -345,6 +395,52 @@
DAWN_TRY(ProcessComputePassCommands(replay, readHead, device, pass));
break;
}
+ case schema::EncoderCommand::BeginRenderPass: {
+ schema::EncoderCommandBeginRenderPassCmdData data;
+ DAWN_TRY(Deserialize(readHead, &data));
+ wgpu::PassTimestampWrites timestampWrites = ToWGPU(replay, data.timestampWrites);
+ std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
+
+ for (const auto& attachment : data.colorAttachments) {
+ colorAttachments.push_back(wgpu::RenderPassColorAttachment{
+ .nextInChain = nullptr,
+ .view = replay.GetObjectById<wgpu::TextureView>(attachment.viewId),
+ .depthSlice = attachment.depthSlice,
+ .resolveTarget =
+ replay.GetObjectById<wgpu::TextureView>(attachment.resolveTargetId),
+ .loadOp = attachment.loadOp,
+ .storeOp = attachment.storeOp,
+ .clearValue = ToWGPU(attachment.clearValue),
+ });
+ }
+
+ wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{
+ .view =
+ replay.GetObjectById<wgpu::TextureView>(data.depthStencilAttachment.viewId),
+ .depthLoadOp = data.depthStencilAttachment.depthLoadOp,
+ .depthStoreOp = data.depthStencilAttachment.depthStoreOp,
+ .depthClearValue = data.depthStencilAttachment.depthClearValue,
+ .depthReadOnly = data.depthStencilAttachment.depthReadOnly,
+ .stencilLoadOp = data.depthStencilAttachment.stencilLoadOp,
+ .stencilStoreOp = data.depthStencilAttachment.stencilStoreOp,
+ .stencilClearValue = data.depthStencilAttachment.stencilClearValue,
+ .stencilReadOnly = data.depthStencilAttachment.stencilReadOnly,
+ };
+
+ wgpu::RenderPassDescriptor desc{
+ .label = wgpu::StringView(data.label),
+ .colorAttachmentCount = colorAttachments.size(),
+ .colorAttachments = colorAttachments.data(),
+ .depthStencilAttachment =
+ depthStencilAttachment.view != nullptr ? &depthStencilAttachment : nullptr,
+ .occlusionQuerySet =
+ replay.GetObjectById<wgpu::QuerySet>(data.occlusionQuerySetId),
+ .timestampWrites = timestampWrites.querySet ? ×tampWrites : nullptr,
+ };
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&desc);
+ DAWN_TRY(ProcessRenderPassCommands(replay, readHead, device, pass));
+ break;
+ }
case schema::EncoderCommand::CopyBufferToBuffer: {
schema::EncoderCommandCopyBufferToBufferCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
diff --git a/src/dawn/replay/Replay.h b/src/dawn/replay/Replay.h
index b3d15ca..15a6aae 100644
--- a/src/dawn/replay/Replay.h
+++ b/src/dawn/replay/Replay.h
@@ -49,6 +49,7 @@
wgpu::ComputePipeline,
wgpu::PipelineLayout,
wgpu::QuerySet,
+ wgpu::RenderPipeline,
wgpu::Sampler,
wgpu::ShaderModule,
wgpu::Texture,
diff --git a/src/dawn/serialization/Schema.h b/src/dawn/serialization/Schema.h
index 9ace6e0..80fbfef 100644
--- a/src/dawn/serialization/Schema.h
+++ b/src/dawn/serialization/Schema.h
@@ -61,6 +61,30 @@
End,
};
+enum class RenderPassCommand : uint32_t {
+ Invalid = 0, // 0 is invalid at it's more likely to catch bugs.
+ Draw,
+ DrawIndexed,
+ DrawIndirect,
+ DrawIndexedIndirect,
+ SetPipeline,
+ SetBindGroup,
+ SetIndexBuffer,
+ SetVertexBuffer,
+ SetViewport,
+ SetScissorRect,
+ SetBlendConstant,
+ SetStencilReference,
+ EndRenderPass,
+ ExecuteBundles,
+ WriteTimestamp,
+ InsertDebugMarker,
+ PopDebugGroup,
+ PushDebugGroup,
+ SetImmediateData,
+ End,
+};
+
enum class EncoderCommand : uint32_t {
Invalid = 0, // 0 is invalid at it's more likely to catch bugs.
BeginComputePass,
@@ -104,6 +128,14 @@
DAWN_REPLAY_SERIALIZABLE(struct, Extent3D, EXTENT3D_MEMBER){};
+#define COLOR_MEMBER(X) \
+ X(double, r) \
+ X(double, g) \
+ X(double, b) \
+ X(double, a)
+
+DAWN_REPLAY_SERIALIZABLE(struct, Color, COLOR_MEMBER){};
+
#define PIPELINE_CONSTANT_MEMBER(X) \
X(std::string, name) \
X(double, value)
@@ -117,6 +149,90 @@
DAWN_REPLAY_SERIALIZABLE(struct, ProgrammableStage, PROGRAMMABLE_STAGE_MEMBER){};
+#define VERTEX_ATTRIBUTE_MEMBER(X) \
+ X(wgpu::VertexFormat, format) \
+ X(uint64_t, offset) \
+ X(uint32_t, shaderLocation)
+
+DAWN_REPLAY_SERIALIZABLE(struct, VertexAttribute, VERTEX_ATTRIBUTE_MEMBER){};
+
+#define VERTEX_BUFFER_LAYOUT_MEMBER(X) \
+ X(uint64_t, arrayStride) \
+ X(wgpu::VertexStepMode, stepMode) \
+ X(std::vector<VertexAttribute>, attributes)
+
+DAWN_REPLAY_SERIALIZABLE(struct, VertexBufferLayout, VERTEX_BUFFER_LAYOUT_MEMBER){};
+
+#define VERTEX_STATE_MEMBER(X) \
+ X(ProgrammableStage, program) \
+ X(std::vector<VertexBufferLayout>, buffers)
+
+DAWN_REPLAY_SERIALIZABLE(struct, VertexState, VERTEX_STATE_MEMBER){};
+
+#define PRIMITIVE_STATE_MEMBER(X) \
+ X(wgpu::PrimitiveTopology, topology) \
+ X(wgpu::IndexFormat, stripIndexFormat) \
+ X(wgpu::FrontFace, frontFace) \
+ X(wgpu::CullMode, cullMode) \
+ X(bool, unclippedDepth)
+
+DAWN_REPLAY_SERIALIZABLE(struct, PrimitiveState, PRIMITIVE_STATE_MEMBER){};
+
+#define STENCIL_FACE_STATE_MEMBER(X) \
+ X(wgpu::CompareFunction, compare) \
+ X(wgpu::StencilOperation, failOp) \
+ X(wgpu::StencilOperation, depthFailOp) \
+ X(wgpu::StencilOperation, passOp)
+
+DAWN_REPLAY_SERIALIZABLE(struct, StencilFaceState, STENCIL_FACE_STATE_MEMBER){};
+
+#define DEPTH_STENCIL_STATE_MEMBER(X) \
+ X(wgpu::TextureFormat, format) \
+ X(bool, depthWriteEnabled) \
+ X(wgpu::CompareFunction, depthCompare) \
+ X(StencilFaceState, stencilFront) \
+ X(StencilFaceState, stencilBack) \
+ X(uint32_t, stencilReadMask) \
+ X(uint32_t, stencilWriteMask) \
+ X(int32_t, depthBias) \
+ X(float, depthBiasSlopeScale) \
+ X(float, depthBiasClamp)
+
+DAWN_REPLAY_SERIALIZABLE(struct, DepthStencilState, DEPTH_STENCIL_STATE_MEMBER){};
+
+#define MULTISAMPLE_STATE_MEMBER(X) \
+ X(uint32_t, count) \
+ X(uint32_t, mask) \
+ X(bool, alphaToCoverageEnabled)
+
+DAWN_REPLAY_SERIALIZABLE(struct, MultisampleState, MULTISAMPLE_STATE_MEMBER){};
+
+#define BLEND_COMPONENT_MEMBER(X) \
+ X(wgpu::BlendOperation, operation) \
+ X(wgpu::BlendFactor, srcFactor) \
+ X(wgpu::BlendFactor, dstFactor)
+
+DAWN_REPLAY_SERIALIZABLE(struct, BlendComponent, BLEND_COMPONENT_MEMBER){};
+
+#define BLEND_STATE_MEMBER(X) \
+ X(BlendComponent, color) \
+ X(BlendComponent, alpha)
+
+DAWN_REPLAY_SERIALIZABLE(struct, BlendState, BLEND_STATE_MEMBER){};
+
+#define COLOR_TARGET_STATE_MEMBER(X) \
+ X(wgpu::TextureFormat, format) \
+ X(BlendState, blend) \
+ X(wgpu::ColorWriteMask, writeMask)
+
+DAWN_REPLAY_SERIALIZABLE(struct, ColorTargetState, COLOR_TARGET_STATE_MEMBER){};
+
+#define FRAGMENT_STATE_MEMBER(X) \
+ X(ProgrammableStage, program) \
+ X(std::vector<ColorTargetState>, targets)
+
+DAWN_REPLAY_SERIALIZABLE(struct, FragmentState, FRAGMENT_STATE_MEMBER){};
+
#define BUFFER_CREATION_MEMBER(X) \
X(uint64_t, size) \
X(wgpu::BufferUsage, usage)
@@ -172,6 +288,17 @@
DAWN_REPLAY_SERIALIZABLE(struct, ComputePipeline, COMPUTE_PIPELINE_CREATION_MEMBER){};
+#define RENDER_PIPELINE_CREATION_MEMBER(X) \
+ X(ObjectId, layoutId) \
+ X(VertexState, vertex) \
+ X(PrimitiveState, primitive) \
+ X(DepthStencilState, depthStencil) \
+ X(MultisampleState, multisample) \
+ X(FragmentState, fragment) \
+ X(std::vector<BindGroupLayoutIndexIdPair>, groupIndexIds)
+
+DAWN_REPLAY_SERIALIZABLE(struct, RenderPipeline, RENDER_PIPELINE_CREATION_MEMBER){};
+
#define BIND_GROUP_ENTRY_MEMBER(X) \
X(uint32_t, binding) \
X(ObjectId, bufferId) \
@@ -223,6 +350,31 @@
DAWN_REPLAY_SERIALIZABLE(struct, TimestampWrites, TIMESTAMP_WRITES_MEMBER){};
+#define COLOR_ATTACHMENT_MEMBER(X) \
+ X(ObjectId, viewId) \
+ X(uint32_t, depthSlice) \
+ X(ObjectId, resolveTargetId) \
+ X(wgpu::LoadOp, loadOp) \
+ X(wgpu::StoreOp, storeOp) \
+ X(Color, clearValue)
+
+DAWN_REPLAY_SERIALIZABLE(struct, ColorAttachment, COLOR_ATTACHMENT_MEMBER){};
+
+#define RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_MEMBER(X) \
+ X(ObjectId, viewId) \
+ X(wgpu::LoadOp, depthLoadOp) \
+ X(wgpu::StoreOp, depthStoreOp) \
+ X(float, depthClearValue) \
+ X(bool, depthReadOnly) \
+ X(wgpu::LoadOp, stencilLoadOp) \
+ X(wgpu::StoreOp, stencilStoreOp) \
+ X(uint32_t, stencilClearValue) \
+ X(bool, stencilReadOnly)
+
+DAWN_REPLAY_SERIALIZABLE(struct,
+ RenderPassDepthStencilAttachment,
+ RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_MEMBER){};
+
#define CREATE_RESOURCE_CMD_DATA_MEMBER(X) X(LabeledResource, resource)
DAWN_REPLAY_MAKE_ROOT_CMD_AND_CMD_DATA(CreateResource, CREATE_RESOURCE_CMD_DATA_MEMBER){};
@@ -293,6 +445,16 @@
DAWN_REPLAY_MAKE_ENCODER_CMD_AND_CMD_DATA(BeginComputePass, BEGIN_COMPUTE_PASS_CMD_DATA_MEMBER){};
+#define BEGIN_RENDER_PASS_CMD_DATA_MEMBER(X) \
+ X(std::string, label) \
+ X(std::vector<ColorAttachment>, colorAttachments) \
+ X(RenderPassDepthStencilAttachment, depthStencilAttachment) \
+ X(ObjectId, occlusionQuerySetId) \
+ X(TimestampWrites, timestampWrites) \
+ X(uint64_t, maxDrawCount)
+
+DAWN_REPLAY_MAKE_ENCODER_CMD_AND_CMD_DATA(BeginRenderPass, BEGIN_RENDER_PASS_CMD_DATA_MEMBER){};
+
#define SET_COMPUTE_PIPELINE_CMD_DATA_MEMBER(X) X(ObjectId, pipelineId)
DAWN_REPLAY_MAKE_COMPUTE_PASS_CMD_AND_CMD_DATA(SetComputePipeline,
@@ -312,6 +474,20 @@
DAWN_REPLAY_MAKE_COMPUTE_PASS_CMD_AND_CMD_DATA(Dispatch, DISPATCH_CMD_DATA_MEMBER){};
+#define SET_PIPELINE_CMD_DATA_MEMBER(X) X(ObjectId, pipelineId)
+
+DAWN_REPLAY_MAKE_RENDER_PASS_CMD_AND_CMD_DATA(SetPipeline, SET_PIPELINE_CMD_DATA_MEMBER){};
+
+DAWN_REPLAY_MAKE_RENDER_PASS_CMD_AND_CMD_DATA(SetBindGroup, SET_BIND_GROUP_CMD_DATA_MEMBER){};
+
+#define DRAW_CMD_DATA_MEMBER(X) \
+ X(uint32_t, vertexCount) \
+ X(uint32_t, instanceCount) \
+ X(uint32_t, firstVertex) \
+ X(uint32_t, firstInstance)
+
+DAWN_REPLAY_MAKE_RENDER_PASS_CMD_AND_CMD_DATA(Draw, DRAW_CMD_DATA_MEMBER){};
+
} // namespace schema
#endif // SRC_DAWN_SERIALIZATION_SCHEMA_H_
diff --git a/src/dawn/tests/white_box/CaptureAndReplayTests.cpp b/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
index 7bd1e33..b13007c 100644
--- a/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
+++ b/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
@@ -1182,6 +1182,47 @@
}
}
+// Capture and replay the simplest render pass.
+// It just starts and ends a render pass and uses the clearValue to set
+// a texture.
+TEST_P(CaptureAndReplayTests, CaptureRenderPassBasic) {
+ wgpu::TextureDescriptor textureDesc;
+ textureDesc.label = "myTexture";
+ textureDesc.size = {1, 1, 1};
+ textureDesc.format = wgpu::TextureFormat::RGBA8Uint;
+ textureDesc.usage = wgpu::TextureUsage::RenderAttachment;
+ wgpu::Texture texture = device.CreateTexture(&textureDesc);
+
+ wgpu::CommandBuffer commands;
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+
+ utils::ComboRenderPassDescriptor passDescriptor({texture.CreateView()});
+ passDescriptor.cColorAttachments[0].clearValue = {0x11, 0x22, 0x33, 0x44};
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDescriptor);
+ pass.End();
+
+ commands = encoder.Finish();
+ }
+
+ // --- capture ---
+ auto recorder = Recorder::CreateAndStart(device);
+
+ queue.Submit(1, &commands);
+
+ // --- replay ---
+ auto capture = recorder.Finish();
+ auto replay = capture.Replay(device);
+
+ {
+ wgpu::Texture texture = replay->GetObjectByLabel<wgpu::Texture>("myTexture");
+ ASSERT_NE(texture, nullptr);
+
+ uint8_t expected[] = {0x11, 0x22, 0x33, 0x44};
+ EXPECT_TEXTURE_EQ(&expected[0], texture, {0, 0}, {1, 1}, 0, wgpu::TextureAspect::All);
+ }
+}
+
DAWN_INSTANTIATE_TEST(CaptureAndReplayTests, WebGPUBackend());
} // anonymous namespace