Compat: Blit T2B copy for RG11B10Ufloat when not color renderable

Bug: 381214487
Change-Id: Ice2e19892bd79c91b147689d56911efa4ee2fe53
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/222254
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/src/dawn/native/BlitTextureToBuffer.cpp b/src/dawn/native/BlitTextureToBuffer.cpp
index 5e64a72..5343b9f 100644
--- a/src/dawn/native/BlitTextureToBuffer.cpp
+++ b/src/dawn/native/BlitTextureToBuffer.cpp
@@ -655,6 +655,39 @@
 }
 )";
 
+// Storing rg11b10ufloat texel values
+// Reference:
+// https://www.khronos.org/opengl/wiki/Small_Float_Formats
+constexpr std::string_view kEncodeRG11B10UfloatInU32 = R"(
+fn encodeVectorInU32General(v: vec4f) -> u32 {
+    const n_rg = 6;    // number of mantissa bits (RG)
+    const n_b = 5;    // number of mantissa bits (B)
+    const e_max = 31;   // max exponent
+    const b = 15;    // exponent bias
+
+    // Calculate the exponent (biased)
+    let rbe = select(i32(floor(log2(v.r))), -b, v.r == 0.0);
+    let gbe = select(i32(floor(log2(v.g))), -b, v.g == 0.0);
+    let bbe = select(i32(floor(log2(v.b))), -b, v.b == 0.0);
+
+    // Calculate the exponent bits value.
+    let re = clamp(rbe + b, 0, e_max);
+    let ge = clamp(gbe + b, 0, e_max);
+    let be = clamp(bbe + b, 0, e_max);
+
+    // Calculate the mantissa for each component.
+    let rm = u32(round( select(v.r * exp2(-f32(re - b)) - 1.0, v.r * exp2(f32(b-1)), re == 0) * f32(1 << n_rg) ));
+    let gm = u32(round( select(v.g * exp2(-f32(ge - b)) - 1.0, v.g * exp2(f32(b-1)), ge == 0) * f32(1 << n_rg) ));
+    let bm = u32(round( select(v.b * exp2(-f32(be - b)) - 1.0, v.b * exp2(f32(b-1)), be == 0) * f32(1 << n_b) ));
+
+    let red = u32(re << n_rg) | rm;
+    let green = u32(ge << n_rg) | gm;
+    let blue = u32(be << n_b) | bm;
+
+    return (blue << 22) | (green << 11) | red;
+}
+)";
+
 // Directly loading float32 values into dst_buf
 // No bit manipulation and packing is needed.
 constexpr std::string_view kLoadR32Float = R"(
@@ -794,6 +827,16 @@
             shader += kCommonEnd;
             textureSampleType = wgpu::TextureSampleType::Float;
             break;
+        case wgpu::TextureFormat::RG11B10Ufloat:
+            AppendFloatTextureHead();
+            shader += kDstBufferU32;
+            shader += kEncodeRG11B10UfloatInU32;
+            shader += kCommonHead;
+            shader += kCommonStart;
+            shader += kPackRGBAToU32;
+            shader += kCommonEnd;
+            textureSampleType = wgpu::TextureSampleType::Float;
+            break;
         case wgpu::TextureFormat::R16Float:
         case wgpu::TextureFormat::RG16Float:
             AppendFloatTextureHead();
@@ -974,6 +1017,7 @@
         case wgpu::TextureFormat::RGBA8Unorm:
         case wgpu::TextureFormat::BGRA8Unorm:
         case wgpu::TextureFormat::RGB9E5Ufloat:
+        case wgpu::TextureFormat::RG11B10Ufloat:
         case wgpu::TextureFormat::R16Float:
         case wgpu::TextureFormat::RG16Float:
         case wgpu::TextureFormat::RGBA16Float:
diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp
index d8dccb7..ba75553 100644
--- a/src/dawn/native/Buffer.cpp
+++ b/src/dawn/native/Buffer.cpp
@@ -129,6 +129,7 @@
             device->IsToggleEnabled(Toggle::UseBlitForSnormTextureToBufferCopy) ||
             device->IsToggleEnabled(Toggle::UseBlitForBGRA8UnormTextureToBufferCopy) ||
             device->IsToggleEnabled(Toggle::UseBlitForRGB9E5UfloatTextureCopy) ||
+            device->IsToggleEnabled(Toggle::UseBlitForRG11B10UfloatTextureCopy) ||
             device->IsToggleEnabled(Toggle::UseBlitForFloat16TextureCopy) ||
             device->IsToggleEnabled(Toggle::UseBlitForFloat32TextureCopy) ||
             device->IsToggleEnabled(Toggle::UseBlitForT2B);
diff --git a/src/dawn/native/CommandEncoder.cpp b/src/dawn/native/CommandEncoder.cpp
index cc5cac3..4f80264 100644
--- a/src/dawn/native/CommandEncoder.cpp
+++ b/src/dawn/native/CommandEncoder.cpp
@@ -971,6 +971,11 @@
         device->IsToggleEnabled(Toggle::UseBlitForRGB9E5UfloatTextureCopy)) {
         return true;
     }
+    // RG11B10Ufloat
+    if (format.format == wgpu::TextureFormat::RG11B10Ufloat &&
+        device->IsToggleEnabled(Toggle::UseBlitForRG11B10UfloatTextureCopy)) {
+        return true;
+    }
     // float16
     if ((format.format == wgpu::TextureFormat::R16Float ||
          format.format == wgpu::TextureFormat::RG16Float ||
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index bd96d05..1365d9d 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -544,6 +544,11 @@
         device->IsToggleEnabled(Toggle::UseBlitForRGB9E5UfloatTextureCopy)) {
         return true;
     }
+    // RG11B10ufloat
+    if (format.format == wgpu::TextureFormat::RG11B10Ufloat &&
+        device->IsToggleEnabled(Toggle::UseBlitForRG11B10UfloatTextureCopy)) {
+        return true;
+    }
     // float16
     if ((format.format == wgpu::TextureFormat::R16Float ||
          format.format == wgpu::TextureFormat::RG16Float ||
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index 3732ca4..a6224c8 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -430,6 +430,11 @@
       "Use a blit instead of a copy command to copy rgb9e5ufloat texture to a texture or a buffer."
       "Workaround for OpenGLES.",
       "https://crbug.com/dawn/2079", ToggleStage::Device}},
+    {Toggle::UseBlitForRG11B10UfloatTextureCopy,
+     {"use_blit_for_rgb9e5ufloat_texture_copy",
+      "Use a blit instead of a copy command to copy rg11b10ufloat texture to a texture or a buffer."
+      "Workaround for OpenGLES.",
+      "https://crbug.com/381214487", ToggleStage::Device}},
     {Toggle::UseBlitForFloat16TextureCopy,
      {"use_blit_for_float_16_texture_copy",
       "Use a blit instead of a copy command to copy float16 texture to a texture or a buffer."
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index 07dcbae..88036dc 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -110,6 +110,7 @@
     UseBlitForSnormTextureToBufferCopy,
     UseBlitForBGRA8UnormTextureToBufferCopy,
     UseBlitForRGB9E5UfloatTextureCopy,
+    UseBlitForRG11B10UfloatTextureCopy,
     UseBlitForFloat16TextureCopy,
     UseBlitForFloat32TextureCopy,
     UseBlitForT2B,
diff --git a/src/dawn/native/opengl/PhysicalDeviceGL.cpp b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
index 67e7f7d..8fcae9e 100644
--- a/src/dawn/native/opengl/PhysicalDeviceGL.cpp
+++ b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
@@ -400,6 +400,7 @@
                                gl.IsGLExtensionSupported("GL_EXT_color_buffer_float");
     bool isFloat16Renderable =
         isFloat32Renderable || gl.IsGLExtensionSupported("GL_EXT_color_buffer_half_float");
+    bool isRG11B10UfloatRenderable = isFloat32Renderable;
 
     // TODO(crbug.com/dawn/343): Investigate emulation.
     deviceToggles->Default(Toggle::DisableIndexedDrawBuffers, !supportsIndexedDrawBuffers);
@@ -429,6 +430,10 @@
     // For OpenGL ES, use compute shader blit to emulate rgb9e5ufloat texture to buffer copies.
     deviceToggles->Default(Toggle::UseBlitForRGB9E5UfloatTextureCopy, gl.GetVersion().IsES());
 
+    // Use compute shader blit to emulate rg11b10ufloat texture to buffer copies if not color
+    // renderable.
+    deviceToggles->Default(Toggle::UseBlitForRG11B10UfloatTextureCopy, !isRG11B10UfloatRenderable);
+
     // Use compute shader blit to emulate float16 texture to buffer copies if not color renderable.
     deviceToggles->Default(Toggle::UseBlitForFloat16TextureCopy, !isFloat16Renderable);
 
diff --git a/src/dawn/tests/end2end/CopyTests.cpp b/src/dawn/tests/end2end/CopyTests.cpp
index 1122f0a..9b4bb10 100644
--- a/src/dawn/tests/end2end/CopyTests.cpp
+++ b/src/dawn/tests/end2end/CopyTests.cpp
@@ -288,8 +288,6 @@
             // GL_EXT_texture_format_BGRA8888 or GL_APPLE_texture_format_BGRA8888 is required for
             // compat mode.
             DAWN_TEST_UNSUPPORTED_IF(format == wgpu::TextureFormat::BGRA8Unorm);
-            // TODO(crbug.com/388318201): GL_R11F_G11F_B10F: Framebuffer incomplete.
-            DAWN_SUPPRESS_TEST_IF(format == wgpu::TextureFormat::RG11B10Ufloat);
         }
 
         // TODO(dawn:2129): Fail for Win ANGLE D3D11
diff --git a/src/dawn/tests/end2end/TextureFormatTests.cpp b/src/dawn/tests/end2end/TextureFormatTests.cpp
index a8fd8ca..cfffef9 100644
--- a/src/dawn/tests/end2end/TextureFormatTests.cpp
+++ b/src/dawn/tests/end2end/TextureFormatTests.cpp
@@ -940,9 +940,10 @@
 
 // Test the RG11B10Ufloat format
 TEST_P(TextureFormatTest, RG11B10Ufloat) {
-    // TODO(crbug.com/388318201): investigate
-    DAWN_SUPPRESS_TEST_IF(IsCompatibilityMode() &&
-                          HasToggleEnabled("gl_force_es_31_and_no_extensions"));
+    // TODO(crbug.com/388318201): sampling test also requires format to be color-renderable
+    DAWN_SUPPRESS_TEST_IF(!IsRG11B10UfloatRenderableSupported());
+    // TODO(crbug.com/388318201): expected: 0xf87e0000, actual: 0xfffff800
+    DAWN_SUPPRESS_TEST_IF(IsD3D11());
 
     constexpr uint32_t kFloat11Zero = 0;
     constexpr uint32_t kFloat11Infinity = 0x7C0;
@@ -985,18 +986,15 @@
         {wgpu::TextureFormat::RG11B10Ufloat, 4, TextureComponentType::Float, 4}, textureData,
         uncompressedData);
 
-    // This format is renderable if "rg11b10ufloat-renderable" feature is enabled
-    if (IsRG11B10UfloatRenderableSupported()) {
-        // TODO(https://crbug.com/swiftshader/147) Rendering INFINITY and NaN isn't handled
-        // correctly by swiftshader
-        if ((IsVulkan() && IsSwiftshader()) || IsANGLE()) {
-            WarningLog() << "Skip Rendering test because Swiftshader doesn't render INFINITY "
-                            "and NaN correctly for RG11B10Ufloat texture format.";
-        } else {
-            DoFormatRenderingTest(
-                {wgpu::TextureFormat::RG11B10Ufloat, 4, TextureComponentType::Float, 4},
-                uncompressedData, textureData, new ExpectRG11B10Ufloat(textureData));
-        }
+    // TODO(https://crbug.com/swiftshader/147) Rendering INFINITY and NaN isn't handled
+    // correctly by swiftshader
+    if ((IsVulkan() && IsSwiftshader()) || IsANGLE()) {
+        WarningLog() << "Skip Rendering test because Swiftshader doesn't render INFINITY "
+                        "and NaN correctly for RG11B10Ufloat texture format.";
+    } else {
+        DoFormatRenderingTest(
+            {wgpu::TextureFormat::RG11B10Ufloat, 4, TextureComponentType::Float, 4},
+            uncompressedData, textureData, new ExpectRG11B10Ufloat(textureData));
     }
 }