diff --git a/filament/backend/src/webgpu/WebGPUDriver.cpp b/filament/backend/src/webgpu/WebGPUDriver.cpp index b62cd6e1f1f2..3e8391fd17ef 100644 --- a/filament/backend/src/webgpu/WebGPUDriver.cpp +++ b/filament/backend/src/webgpu/WebGPUDriver.cpp @@ -1472,6 +1472,7 @@ void WebGPUDriver::readPixels(Handle sourceRenderTargetHandle, c const auto srcTarget{ handleCast(sourceRenderTargetHandle) }; assert_invariant(srcTarget); + uint32_t srcMipLevel = 0; wgpu::Texture srcTexture{ nullptr }; if (srcTarget->isDefaultRenderTarget()) { assert_invariant(mSwapChain); @@ -1482,6 +1483,7 @@ void WebGPUDriver::readPixels(Handle sourceRenderTargetHandle, c // TODO we are currently assuming the first attachment is the desired texture. if (colorAttachmentInfos[0].handle) { auto texture = handleCast(colorAttachmentInfos[0].handle); + srcMipLevel = colorAttachmentInfos[0].level; if (texture) { srcTexture = texture->getTexture(); } @@ -1494,15 +1496,20 @@ void WebGPUDriver::readPixels(Handle sourceRenderTargetHandle, c return; } const uint32_t srcWidth {srcTexture.GetWidth()}; - const uint32_t srcHeight{srcTexture.GetHeight()}; + const uint32_t srcHeight {srcTexture.GetHeight()}; + const uint32_t srcMipLevelCount {srcTexture.GetMipLevelCount()}; + + constexpr uint32_t minValidTextureSize = 1; + const uint32_t mipLeveledSrcWidth = std::max(minValidTextureSize, srcWidth >> srcMipLevel); + const uint32_t mipLeveledSrcHeight = std::max(minValidTextureSize, srcHeight >> srcMipLevel); // Clamp read region to texture bounds - if (UTILS_UNLIKELY(x >= srcWidth || y >= srcHeight)) { + if (UTILS_UNLIKELY(x >= mipLeveledSrcWidth || y >= mipLeveledSrcHeight)) { scheduleDestroy(std::move(pixelBufferDescriptor)); return; } - auto actualWidth{ std::min(width, srcWidth - x) }; - auto actualHeight{ std::min(height, srcHeight - y)}; + auto actualWidth{ std::min(width, mipLeveledSrcWidth - x) }; + auto actualHeight{ std::min(height, mipLeveledSrcHeight - y)}; if (UTILS_UNLIKELY(actualWidth == 0 || actualHeight == 0)) { scheduleDestroy(std::move(pixelBufferDescriptor)); return; @@ -1526,6 +1533,7 @@ void WebGPUDriver::readPixels(Handle sourceRenderTargetHandle, c // If the source format is different from the destination (e.g. BGRA vs RGBA), // we need to perform a conversion using an intermediate blit. if (conversionNecessary(srcFormat, dstFormat, pixelBufferDescriptor.type)) { + // TODO: check if the blit process here is correct when mipmap level > 0 const wgpu::TextureDescriptor stagingDescriptor{ .label = "readpixels_staging_texture", .usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, @@ -1536,7 +1544,7 @@ void WebGPUDriver::readPixels(Handle sourceRenderTargetHandle, c .depthOrArrayLayers = 1, }, .format = dstFormat, - .mipLevelCount = 1, + .mipLevelCount = srcMipLevelCount, .sampleCount = srcTexture.GetSampleCount(), }; stagingTexture = mDevice.CreateTexture(&stagingDescriptor); @@ -1586,12 +1594,11 @@ void WebGPUDriver::readPixels(Handle sourceRenderTargetHandle, c // WebGPU's texture coordinates for copies are top-left, but Filament's y-coordinate is // bottom-left. We must flip the y-coordinate relative to the texture we are reading from. - const uint32_t textureHeight{ textureToReadFrom.GetHeight() }; - const uint32_t flippedY{ textureHeight - readY - actualHeight }; + const uint32_t flippedY{ mipLeveledSrcHeight - readY - actualHeight }; const wgpu::TexelCopyTextureInfo source{ .texture = textureToReadFrom, // Read from the original or intermediate texture - .mipLevel = 0, + .mipLevel = srcMipLevel, .origin = {.x = readX, .y = flippedY, .z = 0,}, }; const wgpu::TexelCopyBufferInfo destination{ diff --git a/filament/backend/test/test_Scissor.cpp b/filament/backend/test/test_Scissor.cpp index 8b7d5bf7882d..ecccd73e7755 100644 --- a/filament/backend/test/test_Scissor.cpp +++ b/filament/backend/test/test_Scissor.cpp @@ -83,8 +83,10 @@ TEST_F(BackendTest, ScissorViewportRegion) { Handle srcTexture = addCleanup(api.createTexture(SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1, TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT TEXTURE_USAGE_READ_PIXELS)); - Handle depthTexture = addCleanup(api.createTexture(SamplerType::SAMPLER_2D, 1, - TextureFormat::DEPTH16, 1, 512, 512, 1, TextureUsage::DEPTH_ATTACHMENT)); + + Handle depthTexture = + addCleanup(api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::DEPTH16, 1, + 512, 512, 1, TextureUsage::DEPTH_ATTACHMENT | TextureUsage::SAMPLEABLE)); // Render into the bottom-left quarter of the texture. Viewport srcRect = {