Support *-srgb format as dst formats in CopyTextureForBrowser
Dawn allows texture-to-texture copy happens between the textures that
formats only have diff on srgb-ness.
CopyTextureForBrowser could align on this rule to achieve copying to
*-srgb dst texture and keep the bytes the same as copying to non-srgb
formats.
This CL add support for *-srgb textures as dst textures and using an
extra gamma decoding step for this.
Bug: dawn:1195
Change-Id: I665dbca473aa84b9d87b7a35c4f90ce1897ade7b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/74580
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
diff --git a/src/dawn_native/CopyTextureForBrowserHelper.cpp b/src/dawn_native/CopyTextureForBrowserHelper.cpp
index 919a248..8fd87bd 100644
--- a/src/dawn_native/CopyTextureForBrowserHelper.cpp
+++ b/src/dawn_native/CopyTextureForBrowserHelper.cpp
@@ -48,14 +48,15 @@
padding: u32;
};
- struct Uniforms { // offset align size
- scale: vec2<f32>; // 0 8 8
- offset: vec2<f32>; // 8 8 8
- steps_mask: u32; // 16 4 4
- // implicit padding; // 20 12
- conversion_matrix: mat3x3<f32>; // 32 16 48
- gamma_decoding_params: GammaTransferParams; // 80 4 32
- gamma_encoding_params: GammaTransferParams; // 112 4 32
+ struct Uniforms { // offset align size
+ scale: vec2<f32>; // 0 8 8
+ offset: vec2<f32>; // 8 8 8
+ steps_mask: u32; // 16 4 4
+ // implicit padding; // 20 12
+ conversion_matrix: mat3x3<f32>; // 32 16 48
+ gamma_decoding_params: GammaTransferParams; // 80 4 32
+ gamma_encoding_params: GammaTransferParams; // 112 4 32
+ gamma_decoding_for_dst_srgb_params: GammaTransferParams; // 144 4 32
};
[[binding(0), group(0)]] var<uniform> uniforms : Uniforms;
@@ -141,6 +142,7 @@
let kConvertToDstGamutStep = 0x04u;
let kEncodeToGammaStep = 0x08u;
let kPremultiplyStep = 0x10u;
+ let kDecodeForSrgbDstFormat = 0x20u;
// Unpremultiply step. Appling color space conversion op on premultiplied source texture
// also needs to unpremultiply first.
@@ -180,6 +182,14 @@
color = vec4<f32>(color.rgb * color.a, color.a);
}
+ // Decode for copying from non-srgb formats to srgb formats
+ if (bool(uniforms.steps_mask & kDecodeForSrgbDstFormat)) {
+ color = vec4<f32>(gamma_conversion(color.r, uniforms.gamma_decoding_for_dst_srgb_params),
+ gamma_conversion(color.g, uniforms.gamma_decoding_for_dst_srgb_params),
+ gamma_conversion(color.b, uniforms.gamma_decoding_for_dst_srgb_params),
+ color.a);
+ }
+
return color;
}
)";
@@ -207,8 +217,9 @@
std::array<float, 12> conversionMatrix = {};
GammaTransferParams gammaDecodingParams = {};
GammaTransferParams gammaEncodingParams = {};
+ GammaTransferParams gammaDecodingForDstSrgbParams = {};
};
- static_assert(sizeof(Uniform) == 144, "");
+ static_assert(sizeof(Uniform) == 176, "");
// TODO(crbug.com/dawn/856): Expand copyTextureForBrowser to support any
// non-depth, non-stencil, non-compressed texture format pair copy. Now this API
@@ -232,7 +243,9 @@
case wgpu::TextureFormat::RG16Float:
case wgpu::TextureFormat::RG32Float:
case wgpu::TextureFormat::RGBA8Unorm:
+ case wgpu::TextureFormat::RGBA8UnormSrgb:
case wgpu::TextureFormat::BGRA8Unorm:
+ case wgpu::TextureFormat::BGRA8UnormSrgb:
case wgpu::TextureFormat::RGB10A2Unorm:
case wgpu::TextureFormat::RGBA16Float:
case wgpu::TextureFormat::RGBA32Float:
@@ -362,6 +375,17 @@
return {};
}
+ // Whether the format of dst texture of CopyTextureForBrowser() is srgb or non-srgb.
+ bool IsSrgbDstFormat(wgpu::TextureFormat format) {
+ switch (format) {
+ case wgpu::TextureFormat::RGBA8UnormSrgb:
+ case wgpu::TextureFormat::BGRA8UnormSrgb:
+ return true;
+ default:
+ return false;
+ }
+ }
+
MaybeError DoCopyTextureForBrowser(DeviceBase* device,
const ImageCopyTexture* source,
const ImageCopyTexture* destination,
@@ -375,6 +399,7 @@
return {};
}
+ bool isSrgbDstFormat = IsSrgbDstFormat(destination->texture->GetFormat().format);
RenderPipelineBase* pipeline;
DAWN_TRY_ASSIGN(pipeline, GetOrCreateCopyTextureForBrowserPipeline(
device, destination->texture->GetFormat().format));
@@ -422,6 +447,7 @@
constexpr uint32_t kConvertToDstGamutStep = 0x04;
constexpr uint32_t kEncodeToGammaStep = 0x08;
constexpr uint32_t kPremultiplyStep = 0x10;
+ constexpr uint32_t kDecodeForSrgbDstFormat = 0x20;
if (options->srcAlphaMode == wgpu::AlphaMode::Premultiplied) {
if (options->needsColorSpaceConversion ||
@@ -470,6 +496,25 @@
}
}
+ // Copy to *-srgb texture should keep the bytes exactly the same as copy
+ // to non-srgb texture. Add an extra decode-to-linear step so that after the
+ // sampler of *-srgb format texture applying encoding, the bytes keeps the same
+ // as non-srgb format texture.
+ // NOTE: CopyTextureForBrowser() doesn't need to accept *-srgb format texture as
+ // source input. But above operation also valid for *-srgb format texture input and
+ // non-srgb format dst texture.
+ // TODO(crbug.com/dawn/1195): Reinterpret to non-srgb texture view on *-srgb texture
+ // and use it as render attachment when possible.
+ // TODO(crbug.com/dawn/1195): Opt the condition for this extra step. It is possible to
+ // bypass this extra step in some cases.
+ if (isSrgbDstFormat) {
+ stepsMask |= kDecodeForSrgbDstFormat;
+ // Get gamma-linear conversion params from https://en.wikipedia.org/wiki/SRGB with some
+ // mathematics. Order: {G, A, B, C, D, E, F, }
+ uniformData.gammaDecodingForDstSrgbParams = {
+ 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 4.045e-02, 0.0, 0.0};
+ }
+
uniformData.stepsMask = stepsMask;
Ref<BufferBase> uniformBuffer;
@@ -511,9 +556,9 @@
dstTextureViewDesc.baseArrayLayer = destination->origin.z;
dstTextureViewDesc.arrayLayerCount = 1;
Ref<TextureViewBase> dstView;
+
DAWN_TRY_ASSIGN(dstView,
device->CreateTextureView(destination->texture, &dstTextureViewDesc));
-
// Prepare render pass color attachment descriptor.
RenderPassColorAttachment colorAttachmentDesc;
@@ -546,7 +591,6 @@
// Submit command buffer.
device->GetQueue()->APISubmit(1, &submitCommandBuffer);
-
return {};
}
diff --git a/src/tests/end2end/CopyTextureForBrowserTests.cpp b/src/tests/end2end/CopyTextureForBrowserTests.cpp
index afd7bbb..028dfd7 100644
--- a/src/tests/end2end/CopyTextureForBrowserTests.cpp
+++ b/src/tests/end2end/CopyTextureForBrowserTests.cpp
@@ -571,6 +571,17 @@
GetParam().mDstFormat == wgpu::TextureFormat::BGRA8UnormSrgb;
}
+ wgpu::TextureFormat GetNonSrgbFormat(wgpu::TextureFormat format) {
+ switch (format) {
+ case wgpu::TextureFormat::RGBA8UnormSrgb:
+ return wgpu::TextureFormat::RGBA8Unorm;
+ case wgpu::TextureFormat::BGRA8UnormSrgb:
+ return wgpu::TextureFormat::BGRA8Unorm;
+ default:
+ return format;
+ }
+ }
+
void DoColorConversionTest() {
TextureSpec srcTextureSpec;
srcTextureSpec.format = GetParam().mSrcFormat;
@@ -627,8 +638,40 @@
RunCopyExternalImageToTexture(srcTextureSpec, srcTexture, dstTextureSpec, dstTexture,
copySize, options);
+ wgpu::Texture result;
+ TextureSpec resultSpec = dstTextureSpec;
+
+ // To construct the expected value for the case that dst texture is srgb format,
+ // we need to ensure it is byte level equal to the comparable non-srgb format texture.
+ // We schedule an copy from srgb texture to non-srgb texture which keeps the bytes
+ // same and bypass the sampler to do gamma correction when comparing the expected values
+ // in compute shader.
+ if (IsDstFormatSrgbFormats()) {
+ resultSpec.format = GetNonSrgbFormat(dstTextureSpec.format);
+ wgpu::Texture intermediateTexture = CreateTexture(
+ resultSpec, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding |
+ wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc);
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+
+ // Perform the texture to texture copy
+ wgpu::ImageCopyTexture dstImageCopyTexture =
+ utils::CreateImageCopyTexture(dstTexture, 0, {0, 0, 0});
+ wgpu::ImageCopyTexture intermediateImageCopyTexture =
+ utils::CreateImageCopyTexture(intermediateTexture, 0, {0, 0, 0});
+
+ encoder.CopyTextureToTexture(&dstImageCopyTexture, &intermediateImageCopyTexture,
+ &(dstTextureSpec.textureSize));
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ result = intermediateTexture;
+ } else {
+ result = dstTexture;
+ }
+
// Check Result
- CheckResultInBuiltInComputePipeline(srcTextureSpec, srcTexture, dstTextureSpec, dstTexture,
+ CheckResultInBuiltInComputePipeline(srcTextureSpec, srcTexture, resultSpec, result,
copySize, options);
}
};
@@ -1045,7 +1088,8 @@
{wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R16Float, wgpu::TextureFormat::R32Float,
wgpu::TextureFormat::RG8Unorm, wgpu::TextureFormat::RG16Float,
wgpu::TextureFormat::RG32Float, wgpu::TextureFormat::RGBA8Unorm,
- wgpu::TextureFormat::BGRA8Unorm, wgpu::TextureFormat::RGB10A2Unorm,
+ wgpu::TextureFormat::RGBA8UnormSrgb, wgpu::TextureFormat::BGRA8Unorm,
+ wgpu::TextureFormat::BGRA8UnormSrgb, wgpu::TextureFormat::RGB10A2Unorm,
wgpu::TextureFormat::RGBA16Float, wgpu::TextureFormat::RGBA32Float}));
// Verify |CopyTextureForBrowser| doing subrect copy.