Capture: Render Pass with pipeline/bindGroup
This is the next step up from a pass with no pipeline
Bug: 451389801
Change-Id: I6a6a696417e832fb0a478bece207c55af3a340b8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/268314
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Gregg Tavares <gman@chromium.org>
diff --git a/src/dawn/native/webgpu/BindGroupWGPU.cpp b/src/dawn/native/webgpu/BindGroupWGPU.cpp
index a5ceaa0..f48d7e7 100644
--- a/src/dawn/native/webgpu/BindGroupWGPU.cpp
+++ b/src/dawn/native/webgpu/BindGroupWGPU.cpp
@@ -135,7 +135,7 @@
{
BindGroupLayoutInternalBase* layout = GetLayout();
const auto& bindingMap = layout->GetBindingMap();
- for (const auto& [bindingNumbers, apiBindingIndex] : bindingMap) {
+ for (const auto& [bindingNumber, apiBindingIndex] : bindingMap) {
BindingIndex bindingIndex = layout->AsBindingIndex(apiBindingIndex);
const auto& bindingInfo = layout->GetAPIBindingInfo(apiBindingIndex);
@@ -160,13 +160,14 @@
for (const auto& [bindingNumber, apiBindingIndex] : bindingMap) {
BindingIndex bindingIndex = layout->AsBindingIndex(apiBindingIndex);
const auto& bindingInfo = layout->GetAPIBindingInfo(apiBindingIndex);
+ uint32_t binding = uint32_t(bindingNumber);
MatchVariant(
bindingInfo.bindingLayout,
[&](const BufferBindingInfo& info) {
const auto& entry = GetBindingAsBufferBinding(bindingIndex);
entries.push_back(schema::BindGroupEntry{{
- .binding = uint32_t(bindingNumber),
+ .binding = binding,
.bufferId = captureContext.GetId(entry.buffer),
.offset = entry.offset,
.size = entry.size,
@@ -174,6 +175,17 @@
.textureViewId = 0,
}});
},
+ [&](const TextureBindingInfo& info) {
+ const auto& entry = GetBindingAsTextureView(bindingIndex);
+ entries.push_back(schema::BindGroupEntry{{
+ .binding = binding,
+ .bufferId = 0,
+ .offset = 0,
+ .size = 0,
+ .samplerId = 0,
+ .textureViewId = captureContext.GetId(entry),
+ }});
+ },
[&](const auto& info) { DAWN_CHECK(false); });
}
diff --git a/src/dawn/replay/Replay.cpp b/src/dawn/replay/Replay.cpp
index 41c52f1..7cd05b1 100644
--- a/src/dawn/replay/Replay.cpp
+++ b/src/dawn/replay/Replay.cpp
@@ -117,6 +117,40 @@
};
}
+wgpu::StencilFaceState ToWGPU(const schema::StencilFaceState& state) {
+ return wgpu::StencilFaceState{
+ .compare = state.compare,
+ .failOp = state.failOp,
+ .depthFailOp = state.depthFailOp,
+ .passOp = state.passOp,
+ };
+}
+
+wgpu::BlendComponent ToWGPU(const schema::BlendComponent& component) {
+ return wgpu::BlendComponent{
+ .operation = component.operation,
+ .srcFactor = component.srcFactor,
+ .dstFactor = component.dstFactor,
+ };
+}
+
+wgpu::BlendState ToWGPU(const schema::BlendState& state) {
+ return wgpu::BlendState{
+ .color = ToWGPU(state.color),
+ .alpha = ToWGPU(state.alpha),
+ };
+}
+
+bool IsBlendComponentEnabled(const wgpu::BlendComponent& component) {
+ return component.operation != wgpu::BlendOperation::Add ||
+ component.srcFactor != wgpu::BlendFactor::One ||
+ component.dstFactor != wgpu::BlendFactor::Zero;
+}
+
+bool IsBlendEnabled(const wgpu::BlendState& blend) {
+ return IsBlendComponentEnabled(blend.color) || IsBlendComponentEnabled(blend.alpha);
+}
+
MaybeError ReadContentIntoBuffer(ReadHead& readHead,
wgpu::Device device,
wgpu::Buffer buffer,
@@ -230,6 +264,92 @@
return {computePipeline};
}
+template <typename F>
+ResultOrError<wgpu::RenderPipeline> CreateRenderPipeline(const Replay& replay,
+ wgpu::Device device,
+ ReadHead& readHead,
+ const std::string& label,
+ F func) {
+ schema::RenderPipeline pipeline;
+ DAWN_TRY(Deserialize(readHead, &pipeline));
+
+ std::vector<wgpu::ConstantEntry> vertexConstants = ToWGPU(pipeline.vertex.program.constants);
+ std::vector<wgpu::ConstantEntry> fragmentConstants =
+ ToWGPU(pipeline.fragment.program.constants);
+ std::vector<wgpu::ColorTargetState> colorTargets;
+ std::vector<wgpu::BlendState> blendStates(pipeline.fragment.targets.size());
+
+ wgpu::FragmentState* fragment = nullptr;
+ wgpu::FragmentState fragmentState;
+ if (pipeline.fragment.program.moduleId) {
+ fragment = &fragmentState;
+ fragmentState.module =
+ replay.GetObjectById<wgpu::ShaderModule>(pipeline.fragment.program.moduleId);
+ fragmentState.entryPoint = wgpu::StringView(pipeline.fragment.program.entryPoint);
+ fragmentState.constantCount = fragmentConstants.size();
+ fragmentState.constants = fragmentConstants.data();
+ for (const auto& target : pipeline.fragment.targets) {
+ wgpu::BlendState& blend = blendStates[colorTargets.size()];
+ blend = ToWGPU(target.blend);
+ colorTargets.push_back({
+ .format = target.format,
+ .blend = IsBlendEnabled(blend) ? &blend : nullptr,
+ .writeMask = target.writeMask,
+ });
+ }
+ fragmentState.targetCount = colorTargets.size();
+ fragmentState.targets = colorTargets.data();
+ }
+
+ wgpu::DepthStencilState* depthStencil = nullptr;
+ wgpu::DepthStencilState depthStencilState;
+ if (pipeline.depthStencil.format != wgpu::TextureFormat::Undefined) {
+ depthStencil = &depthStencilState;
+ depthStencilState.format = pipeline.depthStencil.format;
+ depthStencilState.depthWriteEnabled = pipeline.depthStencil.depthWriteEnabled;
+ depthStencilState.depthCompare = pipeline.depthStencil.depthCompare;
+ depthStencilState.stencilFront = ToWGPU(pipeline.depthStencil.stencilFront);
+ depthStencilState.stencilBack = ToWGPU(pipeline.depthStencil.stencilBack);
+ depthStencilState.stencilReadMask = pipeline.depthStencil.stencilReadMask;
+ depthStencilState.stencilWriteMask = pipeline.depthStencil.stencilWriteMask;
+ depthStencilState.depthBias = pipeline.depthStencil.depthBias;
+ depthStencilState.depthBiasSlopeScale = pipeline.depthStencil.depthBiasSlopeScale;
+ depthStencilState.depthBiasClamp = pipeline.depthStencil.depthBiasClamp;
+ }
+
+ wgpu::RenderPipelineDescriptor desc{
+ .label = wgpu::StringView(label),
+ .layout = replay.GetObjectById<wgpu::PipelineLayout>(pipeline.layoutId),
+ .vertex =
+ {
+ .module =
+ replay.GetObjectById<wgpu::ShaderModule>(pipeline.vertex.program.moduleId),
+ .entryPoint = wgpu::StringView(pipeline.vertex.program.entryPoint),
+ .constantCount = vertexConstants.size(),
+ .constants = vertexConstants.data(),
+ },
+ .primitive =
+ {
+ .topology = pipeline.primitive.topology,
+ .stripIndexFormat = pipeline.primitive.stripIndexFormat,
+ .frontFace = pipeline.primitive.frontFace,
+ .cullMode = pipeline.primitive.cullMode,
+ .unclippedDepth = pipeline.primitive.unclippedDepth,
+ },
+ .depthStencil = depthStencil,
+ .multisample =
+ {
+ .count = pipeline.multisample.count,
+ .mask = pipeline.multisample.mask,
+ .alphaToCoverageEnabled = pipeline.multisample.alphaToCoverageEnabled,
+ },
+ .fragment = fragment,
+ };
+ wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&desc);
+ func(renderPipeline, pipeline.groupIndexIds);
+ return {renderPipeline};
+}
+
ResultOrError<wgpu::ShaderModule> CreateShaderModule(wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
@@ -554,6 +674,25 @@
return {};
}
+ case schema::ObjectType::RenderPipeline: {
+ wgpu::RenderPipeline renderPipeline;
+ DAWN_TRY_ASSIGN(
+ renderPipeline,
+ CreateRenderPipeline(
+ *this, device, readHead, resource.label,
+ [this](wgpu::RenderPipeline& renderPipeline,
+ const std::vector<schema::BindGroupLayoutIndexIdPair>& groupIndexIds) {
+ // Register any implicit bindgroups.
+ for (const auto& groupIndexId : groupIndexIds) {
+ wgpu::BindGroupLayout bgl =
+ renderPipeline.GetBindGroupLayout(groupIndexId.groupIndex);
+ mResources.insert({groupIndexId.bindGroupLayoutId, {"", bgl}});
+ }
+ }));
+ mResources.insert({resource.id, {resource.label, renderPipeline}});
+ return {};
+ }
+
case schema::ObjectType::ShaderModule: {
wgpu::ShaderModule shaderModule;
DAWN_TRY_ASSIGN(shaderModule, CreateShaderModule(device, readHead, resource.label));
@@ -629,8 +768,7 @@
break;
}
default: {
- // UNIMPLEMENTED();
- break;
+ return DAWN_INTERNAL_ERROR("unimplemented root command");
}
}
}
diff --git a/src/dawn/tests/white_box/CaptureAndReplayTests.cpp b/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
index b13007c..18dd68f 100644
--- a/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
+++ b/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
@@ -35,6 +35,7 @@
#include "dawn/replay/Replay.h"
#include "dawn/tests/DawnTest.h"
#include "dawn/tests/MockCallback.h"
+#include "dawn/utils/ComboRenderPipelineDescriptor.h"
#include "dawn/utils/TestUtils.h"
#include "dawn/utils/WGPUHelpers.h"
@@ -1223,6 +1224,88 @@
}
}
+// Capture and replay the a render pass where a texture is rendered into another.
+TEST_P(CaptureAndReplayTests, CaptureRenderPassBasicWithBindGroup) {
+ const uint8_t myData[] = {0x11, 0x22, 0x33, 0x44};
+
+ wgpu::TextureDescriptor textureDesc;
+ textureDesc.label = "srcTexture";
+ textureDesc.size = {1, 1, 1};
+ textureDesc.format = wgpu::TextureFormat::RGBA8Uint;
+ textureDesc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst;
+ wgpu::Texture srcTexture = device.CreateTexture(&textureDesc);
+
+ {
+ wgpu::TexelCopyBufferLayout texelCopyBufferLayout =
+ utils::CreateTexelCopyBufferLayout(0, 4);
+ wgpu::TexelCopyTextureInfo texelCopyTextureInfo =
+ utils::CreateTexelCopyTextureInfo(srcTexture, 0, {0, 0, 0}, wgpu::TextureAspect::All);
+ wgpu::Extent3D extent = {1, 1, 1};
+ queue.WriteTexture(&texelCopyTextureInfo, myData, sizeof(myData), &texelCopyBufferLayout,
+ &extent);
+ }
+
+ textureDesc.usage = wgpu::TextureUsage::RenderAttachment;
+ textureDesc.label = "dstTexture";
+ wgpu::Texture dstTexture = device.CreateTexture(&textureDesc);
+
+ const char* shader = R"(
+ @group(0) @binding(0) var tex: texture_2d<u32>;
+
+ @vertex fn vs() -> @builtin(position) vec4f {
+ return vec4f(0.0, 0.0, 0.0, 1.0);
+ }
+
+ @fragment fn fs() -> @location(0) vec4u {
+ return textureLoad(tex, vec2u(0), 0);
+ }
+ )";
+ auto module = utils::CreateShaderModule(device, shader);
+
+ utils::ComboRenderPipelineDescriptor desc;
+ desc.vertex.module = module;
+ desc.cFragment.module = module;
+ desc.cFragment.targetCount = 1;
+ desc.cTargets[0].format = wgpu::TextureFormat::RGBA8Uint;
+ desc.primitive.topology = wgpu::PrimitiveTopology::PointList;
+ wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc);
+
+ wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
+ {
+ {0, srcTexture.CreateView()},
+ });
+ wgpu::CommandBuffer commands;
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+
+ utils::ComboRenderPassDescriptor passDescriptor({dstTexture.CreateView()});
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDescriptor);
+ pass.SetPipeline(pipeline);
+ pass.SetBindGroup(0, bindGroup);
+ pass.Draw(1);
+ 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>("dstTexture");
+ 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