blob: 0acc1fba68b5ec0f60b58439dcddcdb1cf3ea7f2 [file] [log] [blame] [edit]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "dawn/replay/BlitBufferToDepthTexture.h"
#include <string>
namespace dawn::replay {
namespace {
constexpr std::string_view kShaderSrc = R"(
const kPixelSize = 4;
@vertex fn vert_fullscreen_quad(
@builtin(vertex_index) vertex_index : u32
) -> @builtin(position) vec4f {
const pos = array(
vec2f(-1.0, -1.0),
vec2f( 3.0, -1.0),
vec2f(-1.0, 3.0));
return vec4f(pos[vertex_index], 0.0, 1.0);
}
struct Params {
srcOffset : u32,
bytesPerRow : u32,
dstOrigin : vec2u
};
@group(0) @binding(0) var<storage, read> src_buf : array<f32>;
@group(0) @binding(1) var<uniform> params : Params;
@fragment fn blit_buffer_to_texture(
@builtin(position) screen_position : vec4f
) -> @builtin(frag_depth) f32 {
let iposition = vec2u(screen_position.xy) - params.dstOrigin;
let srcOffset = params.srcOffset + iposition.x * kPixelSize + iposition.y * params.bytesPerRow;
return src_buf[srcOffset >> 2];
}
)";
ResultOrError<wgpu::RenderPipeline> CreatePipeline(wgpu::Device device,
wgpu::TextureFormat format) {
wgpu::ShaderSourceWGSL wgslDesc = {};
wgpu::ShaderModuleDescriptor shaderModuleDesc = {};
shaderModuleDesc.nextInChain = &wgslDesc;
wgslDesc.code = kShaderSrc;
wgpu::ShaderModule shaderModule = device.CreateShaderModule(&shaderModuleDesc);
wgpu::FragmentState fragmentState = {};
fragmentState.module = shaderModule;
wgpu::DepthStencilState depthStencilState = {};
depthStencilState.format = format;
depthStencilState.depthWriteEnabled = true;
depthStencilState.depthCompare = wgpu::CompareFunction::Always;
wgpu::RenderPipelineDescriptor renderPipelineDesc = {};
renderPipelineDesc.label = "blit_buffer_to_texture";
renderPipelineDesc.vertex.module = shaderModule;
renderPipelineDesc.fragment = &fragmentState;
renderPipelineDesc.depthStencil = &depthStencilState;
return device.CreateRenderPipeline(&renderPipelineDesc);
}
} // namespace
MaybeError BlitBufferToDepthTexture::Blit(wgpu::Device device,
const wgpu::TexelCopyTextureInfo& dst,
void const* data,
size_t dataSize,
const wgpu::TexelCopyBufferLayout& src,
const wgpu::Extent3D& copyExtent) {
// This function assumes bytesPerRow is multiples of 4. Normally it's required that
// bytesPerRow is aligned to 256. However some backends might enable
// DawnTexelCopyBufferRowAlignment feature to relax the alignment. Currently only D3D11 backend
// enables this feature, and the relaxed alignment there is 4.
DAWN_ASSERT((src.bytesPerRow % 4) == 0);
wgpu::BufferDescriptor bufferDesc;
bufferDesc.label = "Init Texture Data Buffer";
bufferDesc.size = dataSize;
bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Storage;
wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
device.GetQueue().WriteBuffer(buffer, 0, data, dataSize);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPipeline pipeline;
auto it = mPipelines.find(dst.texture.GetFormat());
if (it != mPipelines.end()) {
pipeline = it->second;
} else {
DAWN_TRY_ASSIGN(pipeline, CreatePipeline(device, dst.texture.GetFormat()));
mPipelines[dst.texture.GetFormat()] = pipeline;
}
wgpu::BindGroupLayout bgl = pipeline.GetBindGroupLayout(0);
wgpu::TextureViewDimension viewDimension;
uint32_t baseDepth = 0;
uint32_t baseArray = 0;
uint32_t depthStep = 0;
uint32_t arrayStep = 0;
switch (dst.texture.GetDimension()) {
case wgpu::TextureDimension::e1D:
DAWN_UNREACHABLE();
break;
case wgpu::TextureDimension::e3D:
viewDimension = wgpu::TextureViewDimension::e3D;
baseDepth = static_cast<uint32_t>(dst.origin.z);
depthStep = 1;
break;
default:
viewDimension = wgpu::TextureViewDimension::e2D;
baseArray = static_cast<uint32_t>(dst.origin.z);
arrayStep = 1;
break;
}
for (uint32_t z{0}; z < copyExtent.depthOrArrayLayers; ++z) {
wgpu::TextureView dstView;
{
wgpu::TextureViewDescriptor viewDesc = {};
viewDesc.dimension = viewDimension;
viewDesc.baseArrayLayer = baseArray + arrayStep * static_cast<uint32_t>(z);
viewDesc.arrayLayerCount = 1;
viewDesc.baseMipLevel = dst.mipLevel;
viewDesc.mipLevelCount = 1;
dstView = dst.texture.CreateView(&viewDesc);
}
const uint64_t srcOffset = src.offset + z * src.rowsPerImage * src.bytesPerRow;
const uint32_t shaderReadOffset = uint32_t(srcOffset);
wgpu::Buffer paramsBuffer;
{
wgpu::BufferDescriptor desc;
desc.label = "Init Texture Param Buffer";
desc.size = sizeof(uint32_t) * 4;
desc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
paramsBuffer = device.CreateBuffer(&desc);
uint32_t params[4];
params[0] = shaderReadOffset;
params[1] = src.bytesPerRow;
params[2] = dst.origin.x;
params[3] = dst.origin.y;
encoder.WriteBuffer(paramsBuffer.Get(), 0, reinterpret_cast<const uint8_t*>(&params[0]),
sizeof(params));
}
wgpu::BindGroupEntry bgEntries[2] = {};
bgEntries[0].binding = 0;
bgEntries[0].buffer = buffer;
bgEntries[1].binding = 1;
bgEntries[1].buffer = paramsBuffer;
wgpu::BindGroupDescriptor bgDesc = {};
bgDesc.layout = bgl;
bgDesc.entryCount = 2;
bgDesc.entries = &bgEntries[0];
wgpu::BindGroup bindGroup = device.CreateBindGroup(&bgDesc);
wgpu::RenderPassColorAttachment colorAttachment;
colorAttachment.view = dstView;
if (depthStep) {
colorAttachment.depthSlice = baseDepth + depthStep * z;
}
colorAttachment.loadOp = wgpu::LoadOp::Load;
colorAttachment.storeOp = wgpu::StoreOp::Store;
wgpu::RenderPassDescriptor rpDesc = {};
rpDesc.colorAttachmentCount = 0;
wgpu::RenderPassDepthStencilAttachment depthStencilAttachment = {};
depthStencilAttachment.view = dstView;
depthStencilAttachment.depthLoadOp = wgpu::LoadOp::Load;
depthStencilAttachment.depthStoreOp = wgpu::StoreOp::Store;
rpDesc.depthStencilAttachment = &depthStencilAttachment;
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&rpDesc);
// Bind the resources.
pass.SetBindGroup(0, bindGroup);
pass.SetViewport(static_cast<float>(dst.origin.x), static_cast<float>(dst.origin.y),
static_cast<float>(copyExtent.width),
static_cast<float>(copyExtent.height), 0.f, 1.f);
// Draw to perform the blit.
pass.SetPipeline(pipeline);
pass.Draw(3, 1, 0, 0);
pass.End();
}
wgpu::CommandBuffer commandBuffer = encoder.Finish();
device.GetQueue().Submit(1, &commandBuffer);
buffer.Destroy();
return {};
}
} // namespace dawn::replay