| // 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*>(¶ms[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 |