blob: 095d72874d92ef1fa652affc8fb1651010b558bc [file] [log] [blame] [edit]
// 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 "dawn/replay/Replay.h"
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <iostream>
#include <memory>
#include <ranges>
#include <string>
#include <utility>
#include <variant>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "dawn/common/Constants.h"
#include "dawn/replay/Capture.h"
#include "dawn/replay/Deserialization.h"
#include "src/dawn/replay/ReplayImpl.h"
namespace dawn::replay {
Replay::~Replay() = default;
std::unique_ptr<Replay> Replay::Create(wgpu::Device device, std::unique_ptr<Capture> capture) {
return ReplayImpl::Create(device, std::move(capture));
}
template <typename T>
T Replay::GetObjectByLabel(std::string_view label) const {
if (auto* impl = static_cast<const ReplayImpl*>(this)) {
return impl->GetObjectByLabel<T>(label);
}
return nullptr;
}
bool Replay::Play() {
if (auto* impl = static_cast<ReplayImpl*>(this)) {
auto result = impl->Play();
return result.IsSuccess();
}
return false;
}
template wgpu::BindGroup Replay::GetObjectByLabel<wgpu::BindGroup>(std::string_view label) const;
template wgpu::BindGroupLayout Replay::GetObjectByLabel<wgpu::BindGroupLayout>(
std::string_view label) const;
template wgpu::Buffer Replay::GetObjectByLabel<wgpu::Buffer>(std::string_view label) const;
template wgpu::CommandBuffer Replay::GetObjectByLabel<wgpu::CommandBuffer>(
std::string_view label) const;
template wgpu::ComputePipeline Replay::GetObjectByLabel<wgpu::ComputePipeline>(
std::string_view label) const;
template wgpu::Device Replay::GetObjectByLabel<wgpu::Device>(std::string_view label) const;
template wgpu::PipelineLayout Replay::GetObjectByLabel<wgpu::PipelineLayout>(
std::string_view label) const;
template wgpu::QuerySet Replay::GetObjectByLabel<wgpu::QuerySet>(std::string_view label) const;
template wgpu::RenderBundle Replay::GetObjectByLabel<wgpu::RenderBundle>(
std::string_view label) const;
template wgpu::RenderPipeline Replay::GetObjectByLabel<wgpu::RenderPipeline>(
std::string_view label) const;
template wgpu::Sampler Replay::GetObjectByLabel<wgpu::Sampler>(std::string_view label) const;
template wgpu::ShaderModule Replay::GetObjectByLabel<wgpu::ShaderModule>(
std::string_view label) const;
template wgpu::Texture Replay::GetObjectByLabel<wgpu::Texture>(std::string_view label) const;
template wgpu::TextureView Replay::GetObjectByLabel<wgpu::TextureView>(
std::string_view label) const;
namespace {
wgpu::Origin3D ToWGPU(const schema::Origin3D& origin) {
return wgpu::Origin3D{
.x = origin.x,
.y = origin.y,
.z = origin.z,
};
}
wgpu::Extent3D ToWGPU(const schema::Extent3D& extent) {
return wgpu::Extent3D{
.width = extent.width,
.height = extent.height,
.depthOrArrayLayers = extent.depthOrArrayLayers,
};
}
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 ReplayImpl& replay, const schema::TimestampWrites& writes) {
return wgpu::PassTimestampWrites{
.nextInChain = nullptr,
.querySet = replay.GetObjectById<wgpu::QuerySet>(writes.querySetId),
.beginningOfPassWriteIndex = writes.beginningOfPassWriteIndex,
.endOfPassWriteIndex = writes.endOfPassWriteIndex,
};
}
std::vector<wgpu::ConstantEntry> ToWGPU(const std::vector<schema::PipelineConstant>& constants) {
std::vector<wgpu::ConstantEntry> entries;
entries.reserve(constants.size());
std::transform(constants.begin(), constants.end(), std::back_inserter(entries),
[](const auto& constant) {
return wgpu::ConstantEntry{
.key = wgpu::StringView(constant.name),
.value = constant.value,
};
});
return entries;
}
wgpu::TexelCopyBufferLayout ToWGPU(const schema::TexelCopyBufferLayout& info) {
return wgpu::TexelCopyBufferLayout{
.offset = info.offset,
.bytesPerRow = info.bytesPerRow,
.rowsPerImage = info.rowsPerImage,
};
}
wgpu::TexelCopyBufferInfo ToWGPU(const ReplayImpl& replay,
const schema::TexelCopyBufferInfo& info) {
return wgpu::TexelCopyBufferInfo{
.layout = ToWGPU(info.layout),
.buffer = replay.GetObjectById<wgpu::Buffer>(info.bufferId),
};
}
wgpu::TexelCopyTextureInfo ToWGPU(const ReplayImpl& replay,
const schema::TexelCopyTextureInfo& info) {
return wgpu::TexelCopyTextureInfo{
.texture = replay.GetObjectById<wgpu::Texture>(info.textureId),
.mipLevel = info.mipLevel,
.origin = ToWGPU(info.origin),
.aspect = static_cast<wgpu::TextureAspect>(info.aspect),
};
}
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,
uint64_t bufferOffset,
uint64_t size) {
const uint32_t* data;
DAWN_TRY_ASSIGN(data, readHead.GetData(size));
device.GetQueue().WriteBuffer(buffer, bufferOffset, data, size);
return {};
}
MaybeError MapContentIntoBuffer(ReadHead& readHead,
wgpu::Device device,
wgpu::Buffer buffer,
uint64_t bufferOffset,
uint64_t size) {
const uint32_t* data;
DAWN_TRY_ASSIGN(data, readHead.GetData(size));
// Note: We could call MapAsync here, wait for it to map, put in the data, then unmap.
// To do so we'd have to change the code in ReplayImpl::CreateBuffer to leave the buffer
// as MapWrite|CopySrc. That would be more inline with what the user actually did
// though it might be slower as it would be synchronous.
device.GetQueue().WriteBuffer(buffer, bufferOffset, data, size);
return {};
}
MaybeError ReadContentIntoTexture(const ReplayImpl& replay,
ReadHead& readHead,
wgpu::Device device,
const schema::RootCommandWriteTextureCmdData& cmdData) {
const uint64_t dataSize = (cmdData.dataSize + 3) & ~3;
const uint32_t* data;
DAWN_TRY_ASSIGN(data, readHead.GetData(dataSize));
wgpu::TexelCopyTextureInfo dst = ToWGPU(replay, cmdData.destination);
wgpu::TexelCopyBufferLayout layout = ToWGPU(cmdData.layout);
wgpu::Extent3D size = ToWGPU(cmdData.size);
device.GetQueue().WriteTexture(&dst, data, cmdData.dataSize, &layout, &size);
return {};
}
ResultOrError<wgpu::BindGroup> CreateBindGroup(const ReplayImpl& replay,
wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::BindGroup bg;
DAWN_TRY(Deserialize(readHead, &bg));
std::vector<wgpu::BindGroupEntry> entries;
for (uint32_t i = 0; i < bg.numEntries; ++i) {
schema::BindGroupLayoutEntryType entryType;
uint32_t binding;
DAWN_TRY(Deserialize(readHead, &entryType));
DAWN_TRY(Deserialize(readHead, &binding));
switch (entryType) {
case schema::BindGroupLayoutEntryType::BufferBinding: {
schema::BindGroupEntryTypeBufferBindingData data;
DAWN_TRY(Deserialize(readHead, &data));
entries.push_back(wgpu::BindGroupEntry{
.binding = binding,
.buffer = replay.GetObjectById<wgpu::Buffer>(data.bufferId),
.offset = data.offset,
.size = data.size,
});
break;
}
case schema::BindGroupLayoutEntryType::SamplerBinding: {
schema::BindGroupEntryTypeSamplerBindingData data;
DAWN_TRY(Deserialize(readHead, &data));
entries.push_back(wgpu::BindGroupEntry{
.binding = binding,
.sampler = replay.GetObjectById<wgpu::Sampler>(data.samplerId),
});
break;
}
case schema::BindGroupLayoutEntryType::TextureBinding: {
schema::BindGroupEntryTypeTextureBindingData data;
DAWN_TRY(Deserialize(readHead, &data));
entries.push_back(wgpu::BindGroupEntry{
.binding = binding,
.textureView = replay.GetObjectById<wgpu::TextureView>(data.textureViewId),
});
break;
}
default:
return DAWN_INTERNAL_ERROR("unsupported bind group entry type");
}
}
wgpu::BindGroupDescriptor desc{
.label = wgpu::StringView(label),
.layout = replay.GetObjectById<wgpu::BindGroupLayout>(bg.layoutId),
.entryCount = entries.size(),
.entries = entries.data(),
};
wgpu::BindGroup bindGroup = device.CreateBindGroup(&desc);
return {bindGroup};
}
ResultOrError<wgpu::BindGroupLayout> CreateBindGroupLayout(const ReplayImpl& replay,
wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::BindGroupLayout bgl;
DAWN_TRY(Deserialize(readHead, &bgl));
std::vector<wgpu::BindGroupLayoutEntry> entries;
for (uint32_t i = 0; i < bgl.numEntries; ++i) {
schema::BindGroupLayoutEntryType entryType;
schema::BindGroupLayoutBinding binding;
DAWN_TRY(Deserialize(readHead, &entryType));
DAWN_TRY(Deserialize(readHead, &binding));
switch (entryType) {
case schema::BindGroupLayoutEntryType::BufferBinding: {
schema::BindGroupLayoutEntryTypeBufferBindingData data;
DAWN_TRY(Deserialize(readHead, &data));
entries.push_back({
.binding = binding.binding,
.visibility = binding.visibility,
.bindingArraySize = binding.bindingArraySize,
.buffer =
{
.type = data.type,
.hasDynamicOffset = data.hasDynamicOffset,
.minBindingSize = data.minBindingSize,
},
});
break;
}
case schema::BindGroupLayoutEntryType::SamplerBinding: {
schema::BindGroupLayoutEntryTypeSamplerBindingData data;
DAWN_TRY(Deserialize(readHead, &data));
entries.push_back({
.binding = binding.binding,
.visibility = binding.visibility,
.bindingArraySize = binding.bindingArraySize,
.sampler =
{
.type = data.type,
},
});
break;
}
case schema::BindGroupLayoutEntryType::StorageTextureBinding: {
schema::BindGroupLayoutEntryTypeStorageTextureBindingData data;
DAWN_TRY(Deserialize(readHead, &data));
entries.push_back({
.binding = binding.binding,
.visibility = binding.visibility,
.bindingArraySize = binding.bindingArraySize,
.storageTexture =
{
.access = data.access,
.format = data.format,
.viewDimension = data.viewDimension,
},
});
break;
}
case schema::BindGroupLayoutEntryType::TextureBinding: {
schema::BindGroupLayoutEntryTypeTextureBindingData data;
DAWN_TRY(Deserialize(readHead, &data));
entries.push_back({
.binding = binding.binding,
.visibility = binding.visibility,
.bindingArraySize = binding.bindingArraySize,
.texture =
{
.sampleType = data.sampleType,
.viewDimension = data.viewDimension,
.multisampled = data.multisampled,
},
});
break;
}
default:
return DAWN_INTERNAL_ERROR("unhandled bind group layout entry type");
}
}
wgpu::BindGroupLayoutDescriptor desc{
.label = wgpu::StringView(label),
.entryCount = entries.size(),
.entries = entries.data(),
};
wgpu::BindGroupLayout bindGroupLayout = device.CreateBindGroupLayout(&desc);
return {bindGroupLayout};
}
ResultOrError<wgpu::Buffer> CreateBuffer(wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::Buffer buf;
DAWN_TRY(Deserialize(readHead, &buf));
wgpu::BufferUsage usage =
(buf.usage & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite))
? buf.usage
: (buf.usage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
// Remap mappable write buffers as CopySrc|CopyDst as we use WriteBuffer to set their contents.
if (usage == (wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc)) {
usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
}
wgpu::BufferDescriptor desc{
.label = wgpu::StringView(label),
.usage = usage,
.size = buf.size,
};
wgpu::Buffer buffer = device.CreateBuffer(&desc);
return {buffer};
}
ResultOrError<wgpu::ComputePipeline> CreateComputePipeline(const ReplayImpl& replay,
wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::ComputePipeline pipeline;
DAWN_TRY(Deserialize(readHead, &pipeline));
std::vector<wgpu::ConstantEntry> constants = ToWGPU(pipeline.compute.constants);
wgpu::ComputePipelineDescriptor desc{
.label = wgpu::StringView(label),
.layout = replay.GetObjectById<wgpu::PipelineLayout>(pipeline.layoutId),
.compute =
{
.module = replay.GetObjectById<wgpu::ShaderModule>(pipeline.compute.moduleId),
.entryPoint = wgpu::StringView(pipeline.compute.entryPoint),
.constantCount = constants.size(),
.constants = constants.data(),
},
};
wgpu::ComputePipeline computePipeline = device.CreateComputePipeline(&desc);
return {computePipeline};
}
ResultOrError<wgpu::PipelineLayout> CreatePipelineLayout(const ReplayImpl& replay,
wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::PipelineLayout layout;
DAWN_TRY(Deserialize(readHead, &layout));
std::vector<wgpu::BindGroupLayout> bindGroupLayouts;
for (const auto bindGroupLayoutId : layout.bindGroupLayoutIds) {
bindGroupLayouts.push_back(replay.GetObjectById<wgpu::BindGroupLayout>(bindGroupLayoutId));
}
wgpu::PipelineLayoutDescriptor desc{
.label = wgpu::StringView(label),
.bindGroupLayoutCount = bindGroupLayouts.size(),
.bindGroupLayouts = bindGroupLayouts.data(),
.immediateSize = layout.immediateSize,
};
wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&desc);
return {pipelineLayout};
}
ResultOrError<wgpu::QuerySet> CreateQuerySet(const ReplayImpl& replay,
wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::QuerySet querySetData;
DAWN_TRY(Deserialize(readHead, &querySetData));
wgpu::QuerySetDescriptor desc{
.label = wgpu::StringView(label),
.type = querySetData.type,
.count = querySetData.count,
};
wgpu::QuerySet querySet = device.CreateQuerySet(&desc);
return {querySet};
}
template <typename T>
MaybeError ProcessWriteTimestamp(const ReplayImpl& replay, T pass, ReadHead& readHead) {
schema::CommandBufferCommandWriteTimestampCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.WriteTimestamp(replay.GetObjectById<wgpu::QuerySet>(data.querySetId), data.queryIndex);
return {};
}
template <typename T>
MaybeError ProcessSharedCommands(const ReplayImpl& replay,
T pass,
schema::CommandBufferCommand cmd,
ReadHead& readHead) {
switch (cmd) {
case schema::CommandBufferCommand::SetBindGroup: {
schema::CommandBufferCommandSetBindGroupCmdData 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::CommandBufferCommand::SetImmediates: {
schema::CommandBufferCommandSetImmediatesCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.SetImmediates(data.offset, data.data.data(), data.data.size());
break;
}
default:
DAWN_UNREACHABLE();
}
return {};
}
template <typename T>
MaybeError ProcessDebugCommands(T pass, schema::CommandBufferCommand cmd, ReadHead& readHead) {
switch (cmd) {
case schema::CommandBufferCommand::PushDebugGroup: {
schema::CommandBufferCommandPushDebugGroupCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.PushDebugGroup(wgpu::StringView(data.groupLabel));
break;
}
case schema::CommandBufferCommand::InsertDebugMarker: {
schema::CommandBufferCommandInsertDebugMarkerCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.InsertDebugMarker(wgpu::StringView(data.markerLabel));
break;
}
case schema::CommandBufferCommand::PopDebugGroup: {
// PopDebugGroup has no data
pass.PopDebugGroup();
break;
}
default:
DAWN_UNREACHABLE();
}
return {};
}
template <typename T>
MaybeError ProcessRenderCommand(const ReplayImpl& replay,
ReadHead& readHead,
wgpu::Device device,
schema::CommandBufferCommand cmd,
T pass) {
switch (cmd) {
case schema::CommandBufferCommand::SetRenderPipeline: {
schema::CommandBufferCommandSetRenderPipelineCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.SetPipeline(replay.GetObjectById<wgpu::RenderPipeline>(data.pipelineId));
break;
}
case schema::CommandBufferCommand::SetVertexBuffer: {
schema::CommandBufferCommandSetVertexBufferCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.SetVertexBuffer(data.slot, replay.GetObjectById<wgpu::Buffer>(data.bufferId),
data.offset, data.size);
break;
}
case schema::CommandBufferCommand::SetIndexBuffer: {
schema::CommandBufferCommandSetIndexBufferCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.SetIndexBuffer(replay.GetObjectById<wgpu::Buffer>(data.bufferId), data.format,
data.offset, data.size);
break;
}
case schema::CommandBufferCommand::Draw: {
schema::CommandBufferCommandDrawCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.Draw(data.vertexCount, data.instanceCount, data.firstVertex, data.firstInstance);
break;
}
case schema::CommandBufferCommand::DrawIndexed: {
schema::CommandBufferCommandDrawIndexedCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.DrawIndexed(data.indexCount, data.instanceCount, data.firstIndex, data.baseVertex,
data.firstInstance);
break;
}
case schema::CommandBufferCommand::DrawIndirect: {
schema::CommandBufferCommandDrawIndirectCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.DrawIndirect(replay.GetObjectById<wgpu::Buffer>(data.indirectBufferId),
data.indirectOffset);
break;
}
case schema::CommandBufferCommand::DrawIndexedIndirect: {
schema::CommandBufferCommandDrawIndexedIndirectCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.DrawIndexedIndirect(replay.GetObjectById<wgpu::Buffer>(data.indirectBufferId),
data.indirectOffset);
break;
}
case schema::CommandBufferCommand::SetBindGroup:
case schema::CommandBufferCommand::SetImmediates:
DAWN_TRY(ProcessSharedCommands(replay, pass, cmd, readHead));
break;
case schema::CommandBufferCommand::PushDebugGroup:
case schema::CommandBufferCommand::InsertDebugMarker:
case schema::CommandBufferCommand::PopDebugGroup:
DAWN_TRY(ProcessDebugCommands(pass, cmd, readHead));
break;
default:
return DAWN_INTERNAL_ERROR("Render Pass/Bundle Command not implemented");
}
return {};
}
MaybeError ProcessRenderBundleCommands(const ReplayImpl& replay,
ReadHead& readHead,
wgpu::Device device,
wgpu::RenderBundleEncoder pass) {
schema::CommandBufferCommand cmd;
while (!readHead.IsDone()) {
DAWN_TRY(Deserialize(readHead, &cmd));
switch (cmd) {
case schema::CommandBufferCommand::End: {
return {};
}
default:
DAWN_TRY(ProcessRenderCommand(replay, readHead, device, cmd, pass));
break;
}
}
return DAWN_INTERNAL_ERROR("Missing RenderBundle End command");
}
ResultOrError<wgpu::RenderBundle> CreateRenderBundle(const ReplayImpl& replay,
wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::RenderBundle bundle;
DAWN_TRY(Deserialize(readHead, &bundle));
wgpu::RenderBundleEncoderDescriptor desc{
.colorFormatCount = bundle.colorFormats.size(),
.colorFormats = bundle.colorFormats.data(),
.depthStencilFormat = bundle.depthStencilFormat,
.sampleCount = bundle.sampleCount,
.depthReadOnly = bundle.depthReadOnly,
.stencilReadOnly = bundle.stencilReadOnly,
};
wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&desc);
DAWN_TRY(ProcessRenderBundleCommands(replay, readHead, device, encoder));
wgpu::RenderBundleDescriptor bundleDesc{
.label = wgpu::StringView(label),
};
wgpu::RenderBundle renderBundle = encoder.Finish(&bundleDesc);
return {renderBundle};
}
ResultOrError<wgpu::RenderPipeline> CreateRenderPipeline(const ReplayImpl& replay,
wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
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());
std::vector<wgpu::VertexBufferLayout> buffers;
std::vector<wgpu::VertexAttribute> attributes(kMaxVertexAttributes);
uint32_t attributeCount = 0;
for (const auto& buffer : pipeline.vertex.buffers) {
const auto attributesForBuffer = &attributes[attributeCount];
for (const auto& attrib : buffer.attributes) {
auto& attr = attributes[attributeCount++];
attr.format = attrib.format;
attr.offset = attrib.offset;
attr.shaderLocation = attrib.shaderLocation;
}
buffers.push_back({
.stepMode = buffer.stepMode,
.arrayStride = buffer.arrayStride,
.attributeCount = buffer.attributes.size(),
.attributes = attributesForBuffer,
});
}
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(),
.bufferCount = buffers.size(),
.buffers = buffers.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);
return {renderPipeline};
}
ResultOrError<wgpu::Sampler> CreateSampler(wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::Sampler sampler;
DAWN_TRY(Deserialize(readHead, &sampler));
wgpu::SamplerDescriptor desc{
.label = wgpu::StringView(label),
.addressModeU = sampler.addressModeU,
.addressModeV = sampler.addressModeV,
.addressModeW = sampler.addressModeW,
.magFilter = sampler.magFilter,
.minFilter = sampler.minFilter,
.mipmapFilter = sampler.mipmapFilter,
.lodMinClamp = sampler.lodMinClamp,
.lodMaxClamp = sampler.lodMaxClamp,
.compare = sampler.compare,
.maxAnisotropy = sampler.maxAnisotropy,
};
return {device.CreateSampler(&desc)};
}
ResultOrError<wgpu::ShaderModule> CreateShaderModule(wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::ShaderModule mod;
DAWN_TRY(Deserialize(readHead, &mod));
// TODO(452840621): Make this use a chain instead of hard coded to WGSL only and handle other
// chained structs.
wgpu::ShaderSourceWGSL source({
.nextInChain = nullptr,
.code = wgpu::StringView(mod.code),
});
wgpu::ShaderModuleDescriptor desc{
.nextInChain = &source,
.label = wgpu::StringView(label),
};
wgpu::ShaderModule shaderModule = device.CreateShaderModule(&desc);
return {shaderModule};
}
ResultOrError<wgpu::Texture> CreateTexture(wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
schema::Texture tex;
DAWN_TRY(Deserialize(readHead, &tex));
wgpu::TextureDescriptor desc{
.label = wgpu::StringView(label),
.usage = tex.usage | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst,
.dimension = tex.dimension,
.size = ToWGPU(tex.size),
.format = tex.format,
.mipLevelCount = tex.mipLevelCount,
.sampleCount = tex.sampleCount,
.viewFormatCount = tex.viewFormats.size(),
.viewFormats = tex.viewFormats.data(),
};
wgpu::Texture texture = device.CreateTexture(&desc);
return {texture};
}
ResultOrError<wgpu::TextureView> CreateTextureView(const ReplayImpl& replay,
ReadHead& readHead,
const std::string& label) {
schema::TextureView view;
DAWN_TRY(Deserialize(readHead, &view));
wgpu::TextureViewDescriptor desc{
.label = wgpu::StringView(label),
.format = view.format,
.dimension = view.dimension,
.baseMipLevel = view.baseMipLevel,
.mipLevelCount = view.mipLevelCount,
.baseArrayLayer = view.baseArrayLayer,
.arrayLayerCount = view.arrayLayerCount,
.aspect = view.aspect,
.usage = view.usage,
};
wgpu::Texture texture = replay.GetObjectById<wgpu::Texture>(view.textureId);
wgpu::TextureView textureView = texture.CreateView(&desc);
return {textureView};
}
MaybeError ProcessComputePassCommands(const ReplayImpl& replay,
ReadHead& readHead,
wgpu::Device device,
wgpu::ComputePassEncoder pass) {
schema::CommandBufferCommand cmd;
while (!readHead.IsDone()) {
DAWN_TRY(Deserialize(readHead, &cmd));
switch (cmd) {
case schema::CommandBufferCommand::End: {
pass.End();
return {};
}
case schema::CommandBufferCommand::SetComputePipeline: {
schema::CommandBufferCommandSetComputePipelineCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.SetPipeline(replay.GetObjectById<wgpu::ComputePipeline>(data.pipelineId));
break;
}
case schema::CommandBufferCommand::Dispatch: {
schema::CommandBufferCommandDispatchCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.DispatchWorkgroups(data.x, data.y, data.z);
break;
}
case schema::CommandBufferCommand::DispatchIndirect: {
schema::CommandBufferCommandDispatchIndirectCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.DispatchWorkgroupsIndirect(replay.GetObjectById<wgpu::Buffer>(data.bufferId),
data.offset);
break;
}
case schema::CommandBufferCommand::WriteTimestamp:
DAWN_TRY(ProcessWriteTimestamp(replay, pass, readHead));
break;
case schema::CommandBufferCommand::SetBindGroup:
case schema::CommandBufferCommand::SetImmediates:
DAWN_TRY(ProcessSharedCommands(replay, pass, cmd, readHead));
break;
case schema::CommandBufferCommand::PushDebugGroup:
case schema::CommandBufferCommand::InsertDebugMarker:
case schema::CommandBufferCommand::PopDebugGroup:
DAWN_TRY(ProcessDebugCommands(pass, cmd, readHead));
break;
default:
return DAWN_INTERNAL_ERROR("Compute Pass Command not implemented");
}
}
return DAWN_INTERNAL_ERROR("Missing ComputePass End command");
}
MaybeError ProcessRenderPassCommands(const ReplayImpl& replay,
ReadHead& readHead,
wgpu::Device device,
wgpu::RenderPassEncoder pass) {
schema::CommandBufferCommand cmd;
while (!readHead.IsDone()) {
DAWN_TRY(Deserialize(readHead, &cmd));
switch (cmd) {
case schema::CommandBufferCommand::End: {
pass.End();
return {};
}
case schema::CommandBufferCommand::ExecuteBundles: {
schema::CommandBufferCommandExecuteBundlesCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
std::vector<wgpu::RenderBundle> bundles;
for (auto bundleId : data.bundleIds) {
bundles.push_back(replay.GetObjectById<wgpu::RenderBundle>(bundleId));
}
pass.ExecuteBundles(bundles.size(), bundles.data());
break;
}
case schema::CommandBufferCommand::BeginOcclusionQuery: {
schema::CommandBufferCommandBeginOcclusionQueryCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.BeginOcclusionQuery(data.queryIndex);
break;
}
case schema::CommandBufferCommand::EndOcclusionQuery: {
pass.EndOcclusionQuery();
break;
}
case schema::CommandBufferCommand::SetBlendConstant: {
schema::CommandBufferCommandSetBlendConstantCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
wgpu::Color color = ToWGPU(data.color);
pass.SetBlendConstant(&color);
break;
}
case schema::CommandBufferCommand::SetScissorRect: {
schema::CommandBufferCommandSetScissorRectCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.SetScissorRect(data.x, data.y, data.width, data.height);
break;
}
case schema::CommandBufferCommand::SetStencilReference: {
schema::CommandBufferCommandSetStencilReferenceCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.SetStencilReference(data.reference);
break;
}
case schema::CommandBufferCommand::SetViewport: {
schema::CommandBufferCommandSetViewportCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
pass.SetViewport(data.x, data.y, data.width, data.height, data.minDepth,
data.maxDepth);
break;
}
case schema::CommandBufferCommand::WriteTimestamp:
DAWN_TRY(ProcessWriteTimestamp(replay, pass, readHead));
break;
default:
DAWN_TRY(ProcessRenderCommand(replay, readHead, device, cmd, pass));
break;
}
}
return DAWN_INTERNAL_ERROR("Missing RenderPass End command");
}
MaybeError ProcessEncoderCommands(const ReplayImpl& replay,
ReadHead& readHead,
wgpu::Device device,
wgpu::CommandEncoder encoder) {
schema::CommandBufferCommand cmd;
while (!readHead.IsDone()) {
DAWN_TRY(Deserialize(readHead, &cmd));
switch (cmd) {
case schema::CommandBufferCommand::BeginComputePass: {
schema::CommandBufferCommandBeginComputePassCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
wgpu::PassTimestampWrites timestampWrites = ToWGPU(replay, data.timestampWrites);
wgpu::ComputePassDescriptor desc{
.label = wgpu::StringView(data.label),
.timestampWrites = timestampWrites.querySet ? &timestampWrites : nullptr,
};
wgpu::ComputePassEncoder pass = encoder.BeginComputePass(&desc);
DAWN_TRY(ProcessComputePassCommands(replay, readHead, device, pass));
break;
}
case schema::CommandBufferCommand::BeginRenderPass: {
schema::CommandBufferCommandBeginRenderPassCmdData 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 ? &timestampWrites : nullptr,
};
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&desc);
DAWN_TRY(ProcessRenderPassCommands(replay, readHead, device, pass));
break;
}
case schema::CommandBufferCommand::ClearBuffer: {
schema::CommandBufferCommandClearBufferCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
encoder.ClearBuffer(replay.GetObjectById<wgpu::Buffer>(data.bufferId), data.offset,
data.size);
break;
}
case schema::CommandBufferCommand::CopyBufferToBuffer: {
schema::CommandBufferCommandCopyBufferToBufferCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
encoder.CopyBufferToBuffer(replay.GetObjectById<wgpu::Buffer>(data.srcBufferId),
data.srcOffset,
replay.GetObjectById<wgpu::Buffer>(data.dstBufferId),
data.dstOffset, data.size);
break;
}
case schema::CommandBufferCommand::CopyBufferToTexture: {
schema::CommandBufferCommandCopyBufferToTextureCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
wgpu::TexelCopyBufferInfo src = ToWGPU(replay, data.source);
wgpu::TexelCopyTextureInfo dst = ToWGPU(replay, data.destination);
wgpu::Extent3D copySize = ToWGPU(data.copySize);
encoder.CopyBufferToTexture(&src, &dst, &copySize);
break;
}
case schema::CommandBufferCommand::CopyTextureToBuffer: {
schema::CommandBufferCommandCopyTextureToBufferCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
wgpu::TexelCopyTextureInfo src = ToWGPU(replay, data.source);
wgpu::TexelCopyBufferInfo dst = ToWGPU(replay, data.destination);
wgpu::Extent3D copySize = ToWGPU(data.copySize);
encoder.CopyTextureToBuffer(&src, &dst, &copySize);
break;
}
case schema::CommandBufferCommand::CopyTextureToTexture: {
schema::CommandBufferCommandCopyTextureToTextureCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
wgpu::TexelCopyTextureInfo src = ToWGPU(replay, data.source);
wgpu::TexelCopyTextureInfo dst = ToWGPU(replay, data.destination);
wgpu::Extent3D copySize = ToWGPU(data.copySize);
encoder.CopyTextureToTexture(&src, &dst, &copySize);
break;
}
case schema::CommandBufferCommand::End: {
return {};
}
case schema::CommandBufferCommand::ResolveQuerySet: {
schema::CommandBufferCommandResolveQuerySetCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
encoder.ResolveQuerySet(replay.GetObjectById<wgpu::QuerySet>(data.querySetId),
data.firstQuery, data.queryCount,
replay.GetObjectById<wgpu::Buffer>(data.destinationId),
data.destinationOffset);
break;
}
case schema::CommandBufferCommand::WriteBuffer: {
schema::CommandBufferCommandWriteBufferCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
wgpu::Buffer buffer = replay.GetObjectById<wgpu::Buffer>(data.bufferId);
encoder.WriteBuffer(buffer, data.bufferOffset, data.data.data(), data.data.size());
break;
}
case schema::CommandBufferCommand::WriteTimestamp:
DAWN_TRY(ProcessWriteTimestamp(replay, encoder, readHead));
break;
case schema::CommandBufferCommand::PushDebugGroup:
case schema::CommandBufferCommand::InsertDebugMarker:
case schema::CommandBufferCommand::PopDebugGroup:
DAWN_TRY(ProcessDebugCommands(encoder, cmd, readHead));
break;
default:
return DAWN_INTERNAL_ERROR("Encoder Command not implemented");
}
}
return DAWN_INTERNAL_ERROR("Missing End command");
}
ResultOrError<wgpu::CommandBuffer> CreateCommandBuffer(const ReplayImpl& replay,
wgpu::Device device,
ReadHead& readHead,
const std::string& label) {
wgpu::CommandEncoderDescriptor desc{
.label = wgpu::StringView(label),
};
wgpu::CommandEncoder encoder = device.CreateCommandEncoder(&desc);
DAWN_TRY(ProcessEncoderCommands(replay, readHead, device, encoder));
return {encoder.Finish()};
}
} // anonymous namespace
std::unique_ptr<ReplayImpl> ReplayImpl::Create(wgpu::Device device,
std::unique_ptr<Capture> capture) {
auto captureImpl = std::unique_ptr<CaptureImpl>(static_cast<CaptureImpl*>(capture.release()));
return std::unique_ptr<ReplayImpl>(new ReplayImpl(device, std::move(captureImpl)));
}
ReplayImpl::ReplayImpl(wgpu::Device device, std::unique_ptr<CaptureImpl> capture)
: mDevice(device), mCapture(std::move(capture)) {
mResources.insert({schema::kDeviceId, {"", device}});
}
MaybeError ReplayImpl::CreateResource(wgpu::Device device, ReadHead& readHead) {
schema::LabeledResource resource;
DAWN_TRY(Deserialize(readHead, &resource));
switch (resource.type) {
case schema::ObjectType::BindGroup: {
wgpu::BindGroup bindGroup;
DAWN_TRY_ASSIGN(bindGroup, CreateBindGroup(*this, device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, bindGroup}});
return {};
}
case schema::ObjectType::BindGroupLayout: {
wgpu::BindGroupLayout bindGroupLayout;
DAWN_TRY_ASSIGN(bindGroupLayout,
CreateBindGroupLayout(*this, device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, bindGroupLayout}});
return {};
}
case schema::ObjectType::Buffer: {
wgpu::Buffer buffer;
DAWN_TRY_ASSIGN(buffer, CreateBuffer(device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, buffer}});
return {};
}
case schema::ObjectType::CommandBuffer: {
// Command buffers are special and don't have any resources to create.
// They are just a sequence of commands.
wgpu::CommandBuffer commandBuffer;
DAWN_TRY_ASSIGN(commandBuffer,
CreateCommandBuffer(*this, device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, commandBuffer}});
return {};
}
case schema::ObjectType::ComputePipeline: {
wgpu::ComputePipeline computePipeline;
DAWN_TRY_ASSIGN(computePipeline,
CreateComputePipeline(*this, device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, computePipeline}});
return {};
}
case schema::ObjectType::PipelineLayout: {
wgpu::PipelineLayout pipelineLayout;
DAWN_TRY_ASSIGN(pipelineLayout,
CreatePipelineLayout(*this, device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, pipelineLayout}});
return {};
}
case schema::ObjectType::QuerySet: {
wgpu::QuerySet querySet;
DAWN_TRY_ASSIGN(querySet, CreateQuerySet(*this, device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, querySet}});
return {};
}
case schema::ObjectType::RenderBundle: {
// Command buffers are special and don't have any resources to create.
// They are just a sequence of commands.
wgpu::RenderBundle renderBundle;
DAWN_TRY_ASSIGN(renderBundle,
CreateRenderBundle(*this, device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, renderBundle}});
return {};
}
case schema::ObjectType::RenderPipeline: {
wgpu::RenderPipeline renderPipeline;
DAWN_TRY_ASSIGN(renderPipeline,
CreateRenderPipeline(*this, device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, renderPipeline}});
return {};
}
case schema::ObjectType::Sampler: {
wgpu::Sampler sampler;
DAWN_TRY_ASSIGN(sampler, CreateSampler(device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, sampler}});
return {};
}
case schema::ObjectType::ShaderModule: {
wgpu::ShaderModule shaderModule;
DAWN_TRY_ASSIGN(shaderModule, CreateShaderModule(device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, shaderModule}});
return {};
}
case schema::ObjectType::Texture: {
wgpu::Texture texture;
DAWN_TRY_ASSIGN(texture, CreateTexture(device, readHead, resource.label));
mResources.insert({resource.id, {resource.label, texture}});
return {};
}
case schema::ObjectType::TextureView: {
wgpu::TextureView textureView;
DAWN_TRY_ASSIGN(textureView, CreateTextureView(*this, readHead, resource.label));
mResources.insert({resource.id, {resource.label, textureView}});
return {};
}
default:
return DAWN_INTERNAL_ERROR("unhandled resource type");
}
}
MaybeError ReplayImpl::SetLabel(schema::ObjectId id,
schema::ObjectType type,
const std::string& label) {
// We update both the object's label and our own copy of the label
// as there is no API to get an object's label from WebGPU
#define DAWN_SET_LABEL(type) \
case schema::ObjectType::type: { \
auto iter = mResources.find(id); \
std::get_if<wgpu::type>(&iter->second.resource)->SetLabel(wgpu::StringView(label)); \
iter->second.label = label; \
break; \
}
switch (type) {
DAWN_SET_LABEL(BindGroup)
DAWN_SET_LABEL(BindGroupLayout)
DAWN_SET_LABEL(Buffer)
DAWN_SET_LABEL(CommandBuffer)
DAWN_SET_LABEL(ComputePipeline)
DAWN_SET_LABEL(Device)
DAWN_SET_LABEL(PipelineLayout)
DAWN_SET_LABEL(QuerySet)
DAWN_SET_LABEL(RenderBundle)
DAWN_SET_LABEL(RenderPipeline)
DAWN_SET_LABEL(Sampler)
DAWN_SET_LABEL(ShaderModule)
DAWN_SET_LABEL(Texture)
DAWN_SET_LABEL(TextureView)
default:
return DAWN_INTERNAL_ERROR("unhandled resource type");
}
return {};
}
MaybeError ReplayImpl::Play() {
auto readHead = mCapture->GetCommandReadHead();
auto contentReadHead = mCapture->GetContentReadHead();
schema::RootCommand cmd;
while (!readHead.IsDone()) {
DAWN_TRY(Deserialize(readHead, &cmd));
switch (cmd) {
case schema::RootCommand::CreateResource: {
DAWN_TRY(CreateResource(mDevice, readHead));
break;
}
case schema::RootCommand::WriteBuffer: {
schema::RootCommandWriteBufferCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
wgpu::Buffer buffer = GetObjectById<wgpu::Buffer>(data.bufferId);
DAWN_TRY(ReadContentIntoBuffer(contentReadHead, mDevice, buffer, data.bufferOffset,
data.size));
break;
}
case schema::RootCommand::WriteTexture: {
// TODO(451460573): Support textures with multiple subresources
schema::RootCommandWriteTextureCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
DAWN_TRY(ReadContentIntoTexture(*this, contentReadHead, mDevice, data));
break;
}
case schema::RootCommand::QueueSubmit: {
schema::RootCommandQueueSubmitCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
std::vector<wgpu::CommandBuffer> commandBuffers;
std::transform(data.commandBuffers.begin(), data.commandBuffers.end(),
std::back_inserter(commandBuffers), [&](const auto id) {
return GetObjectById<wgpu::CommandBuffer>(id);
});
mDevice.GetQueue().Submit(commandBuffers.size(), commandBuffers.data());
break;
}
case schema::RootCommand::UnmapBuffer: {
schema::RootCommandUnmapBufferCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
wgpu::Buffer buffer = GetObjectById<wgpu::Buffer>(data.bufferId);
DAWN_TRY(MapContentIntoBuffer(contentReadHead, mDevice, buffer, data.bufferOffset,
data.size));
break;
}
case schema::RootCommand::SetLabel: {
schema::RootCommandSetLabelCmdData data;
DAWN_TRY(Deserialize(readHead, &data));
DAWN_TRY(SetLabel(data.id, data.type, data.label));
break;
}
default: {
return DAWN_INTERNAL_ERROR("unimplemented root command");
}
}
}
return {};
}
} // namespace dawn::replay