[dawn][opengl] Add texture component swizzle feature support Bug: 414312052 Change-Id: I40bc798ad188eca376fb6e3e8cb4c8e94b23868e Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/253156 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Fr <beaufort.francois@gmail.com> Reviewed-by: Zhaoming Jiang <zhaoming.jiang@microsoft.com>
diff --git a/src/dawn/native/CommandBufferStateTracker.cpp b/src/dawn/native/CommandBufferStateTracker.cpp index 728c0bd..a17fb58 100644 --- a/src/dawn/native/CommandBufferStateTracker.cpp +++ b/src/dawn/native/CommandBufferStateTracker.cpp
@@ -287,7 +287,10 @@ a->GetDimension() == b->GetDimension() && a->GetBaseMipLevel() == b->GetBaseMipLevel() && a->GetLevelCount() == b->GetLevelCount() && a->GetBaseArrayLayer() == b->GetBaseArrayLayer() && - a->GetLayerCount() == b->GetLayerCount(); + a->GetLayerCount() == b->GetLayerCount() && a->GetSwizzleRed() == b->GetSwizzleRed() && + a->GetSwizzleGreen() == b->GetSwizzleGreen() && + a->GetSwizzleBlue() == b->GetSwizzleBlue() && + a->GetSwizzleAlpha() == b->GetSwizzleAlpha(); } using VectorOfTextureViews = absl::InlinedVector<const TextureViewBase*, 8>;
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp index 3d87d4a..03b29c0 100644 --- a/src/dawn/native/opengl/CommandBufferGL.cpp +++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -57,6 +57,26 @@ namespace { +GLenum ComponentSwizzle(wgpu::ComponentSwizzle swizzle) { + switch (swizzle) { + case wgpu::ComponentSwizzle::Zero: + return GL_ZERO; + case wgpu::ComponentSwizzle::One: + return GL_ONE; + case wgpu::ComponentSwizzle::R: + return GL_RED; + case wgpu::ComponentSwizzle::G: + return GL_GREEN; + case wgpu::ComponentSwizzle::B: + return GL_BLUE; + case wgpu::ComponentSwizzle::A: + return GL_ALPHA; + + case wgpu::ComponentSwizzle::Undefined: + DAWN_UNREACHABLE(); + } +} + GLenum IndexFormatType(wgpu::IndexFormat format) { switch (format) { case wgpu::IndexFormat::Uint16: @@ -407,6 +427,20 @@ DAWN_GL_TRY( gl, TexParameteri(target, GL_TEXTURE_MAX_LEVEL, view->GetBaseMipLevel() + view->GetLevelCount() - 1)); + if (mPipelineLayout->GetDevice()->HasFeature( + Feature::TextureComponentSwizzle)) { + DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_SWIZZLE_R, + ComponentSwizzle(view->GetSwizzleRed()))); + DAWN_GL_TRY(gl, + TexParameteri(target, GL_TEXTURE_SWIZZLE_G, + ComponentSwizzle(view->GetSwizzleGreen()))); + DAWN_GL_TRY(gl, + TexParameteri(target, GL_TEXTURE_SWIZZLE_B, + ComponentSwizzle(view->GetSwizzleBlue()))); + DAWN_GL_TRY(gl, + TexParameteri(target, GL_TEXTURE_SWIZZLE_A, + ComponentSwizzle(view->GetSwizzleAlpha()))); + } } // Some texture builtin function data needs emulation to update into the
diff --git a/src/dawn/native/opengl/PhysicalDeviceGL.cpp b/src/dawn/native/opengl/PhysicalDeviceGL.cpp index 1110457..e3124ad 100644 --- a/src/dawn/native/opengl/PhysicalDeviceGL.cpp +++ b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
@@ -300,6 +300,11 @@ if (mFunctions.IsGLExtensionSupported("GL_EXT_float_blend")) { EnableFeature(Feature::Float32Blendable); } + + // TextureComponentSwizzle + if (mFunctions.IsAtLeastGLES(3, 0) || mFunctions.IsAtLeastGL(3, 3)) { + EnableFeature(Feature::TextureComponentSwizzle); + } } namespace {
diff --git a/src/dawn/native/opengl/TextureGL.cpp b/src/dawn/native/opengl/TextureGL.cpp index 4c3e99e..7c572a7 100644 --- a/src/dawn/native/opengl/TextureGL.cpp +++ b/src/dawn/native/opengl/TextureGL.cpp
@@ -125,6 +125,16 @@ return true; } + // TODO(414312052): Use TextureViewBase::UsesNonDefaultSwizzle() instead of + // textureViewDescriptor. + if (auto* swizzleDesc = textureViewDescriptor.Get<TextureComponentSwizzleDescriptor>()) { + auto swizzle = swizzleDesc->swizzle.WithTrivialFrontendDefaults(); + if (swizzle.r != wgpu::ComponentSwizzle::R || swizzle.g != wgpu::ComponentSwizzle::G || + swizzle.b != wgpu::ComponentSwizzle::B || swizzle.a != wgpu::ComponentSwizzle::A) { + return true; + } + } + return false; }
diff --git a/src/dawn/tests/end2end/TextureComponentSwizzleTests.cpp b/src/dawn/tests/end2end/TextureComponentSwizzleTests.cpp index 3ff2e2f..a38ff8d 100644 --- a/src/dawn/tests/end2end/TextureComponentSwizzleTests.cpp +++ b/src/dawn/tests/end2end/TextureComponentSwizzleTests.cpp
@@ -46,6 +46,35 @@ void SetUp() override { DawnTest::SetUp(); DAWN_TEST_UNSUPPORTED_IF(!device.HasFeature(wgpu::FeatureName::TextureComponentSwizzle)); + + wgpu::ShaderModule module = utils::CreateShaderModule(device, R"( + @group(0) @binding(0) var texture : texture_2d<f32>; + + @vertex fn vs_main(@builtin(vertex_index) vertexIndex : u32) -> @builtin(position) vec4f { + var pos = array<vec2f, 3>( + vec2f(-1.0, -1.0), + vec2f(-1.0, 3.0), + vec2f(3.0, -1.0) + ); + return vec4f(pos[vertexIndex], 0.0, 1.0); + } + + @fragment fn fs_main() -> @location(0) vec4f { + // textureLoad samples at an integer coordinate and mip level 0. + return textureLoad(texture, vec2i(0, 0), 0); + })"); + + utils::ComboRenderPipelineDescriptor pipelineDesc; + pipelineDesc.vertex.module = module; + pipelineDesc.cFragment.module = module; + pipeline = device.CreateRenderPipeline(&pipelineDesc); + + wgpu::TextureDescriptor outputTextureDesc = {}; + outputTextureDesc.usage = + wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc; + outputTextureDesc.size = {1, 1, 1}; + outputTextureDesc.format = wgpu::TextureFormat::RGBA8Unorm; + outputTexture = device.CreateTexture(&outputTextureDesc); } std::vector<wgpu::FeatureName> GetRequiredFeatures() override { @@ -89,118 +118,92 @@ } } + void RunSwizzleTest(TestParams params, + wgpu::Texture inputTexture, + wgpu::ComponentSwizzle swizzleRed, + wgpu::ComponentSwizzle swizzleGreen, + wgpu::ComponentSwizzle swizzleBlue, + wgpu::ComponentSwizzle swizzleAlpha) { + wgpu::TexelCopyTextureInfo dest = {.texture = inputTexture}; + wgpu::TexelCopyBufferLayout dataLayout = {.bytesPerRow = 256, .rowsPerImage = 1}; + wgpu::Extent3D writeSize = {1, 1, 1}; + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(params.format); + device.GetQueue().WriteTexture(&dest, params.inputData.data(), bytesPerTexel, &dataLayout, + &writeSize); + + // Create the TextureView for the input texture with the specified swizzle. + wgpu::TextureViewDescriptor viewDesc = {}; + wgpu::TextureComponentSwizzleDescriptor swizzleDesc = {}; + swizzleDesc.swizzle.r = swizzleRed; + swizzleDesc.swizzle.g = swizzleGreen; + swizzleDesc.swizzle.b = swizzleBlue; + swizzleDesc.swizzle.a = swizzleAlpha; + viewDesc.nextInChain = &swizzleDesc; + wgpu::TextureView textureView = inputTexture.CreateView(&viewDesc); + + // Set up bind group using the swizzled texture view. + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, textureView}}); + + // Issue render commands. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassDescriptor renderPassDesc = {}; + wgpu::RenderPassColorAttachment colorAttachment = {}; + colorAttachment.view = outputTexture.CreateView(); + colorAttachment.loadOp = wgpu::LoadOp::Clear; + colorAttachment.storeOp = wgpu::StoreOp::Store; + colorAttachment.clearValue = {0.0f, 0.0f, 0.0f, 0.0f}; + renderPassDesc.colorAttachmentCount = 1; + renderPassDesc.colorAttachments = &colorAttachment; + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(3); + pass.End(); + + // Submit commands to the queue. + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + utils::RGBA8 expectedColor = + utils::RGBA8(GetExpectedValue(swizzleRed, params.baseLoadValues), + GetExpectedValue(swizzleGreen, params.baseLoadValues), + GetExpectedValue(swizzleBlue, params.baseLoadValues), + GetExpectedValue(swizzleAlpha, params.baseLoadValues)); + EXPECT_PIXEL_RGBA8_EQ(expectedColor, outputTexture, 0, 0); + } + void RunTest(TestParams params) { - wgpu::ShaderModule module = utils::CreateShaderModule(device, R"( - @group(0) @binding(0) var texture : texture_2d<f32>; - - @vertex fn vs_main(@builtin(vertex_index) vertexIndex : u32) -> @builtin(position) vec4f { - var pos = array<vec2f, 3>( - vec2f(-1.0, -1.0), - vec2f(-1.0, 3.0), - vec2f(3.0, -1.0) - ); - return vec4f(pos[vertexIndex], 0.0, 1.0); - } - - @fragment fn fs_main() -> @location(0) vec4f { - // textureLoad samples at an integer coordinate and mip level 0. - return textureLoad(texture, vec2i(0, 0), 0); - })"); - - utils::ComboRenderPipelineDescriptor pipelineDesc; - pipelineDesc.vertex.module = module; - pipelineDesc.cFragment.module = module; - wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc); - - wgpu::TextureDescriptor outputTextureDesc = {}; - outputTextureDesc.usage = - wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc; - outputTextureDesc.size = {1, 1, 1}; - outputTextureDesc.format = wgpu::TextureFormat::RGBA8Unorm; - wgpu::Texture outputTexture = device.CreateTexture(&outputTextureDesc); - - auto RunSwizzleTest = [this, params, pipeline, outputTexture]( - wgpu::ComponentSwizzle swizzleRed, - wgpu::ComponentSwizzle swizzleGreen, - wgpu::ComponentSwizzle swizzleBlue, - wgpu::ComponentSwizzle swizzleAlpha) { - // Create source texture. - wgpu::TextureDescriptor textureDesc = {}; - textureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding; - textureDesc.size = {1, 1, 1}; - textureDesc.format = params.format; - wgpu::Texture texture = device.CreateTexture(&textureDesc); - - wgpu::TexelCopyTextureInfo dest = {.texture = texture}; - wgpu::TexelCopyBufferLayout dataLayout = {.bytesPerRow = 256, .rowsPerImage = 1}; - wgpu::Extent3D writeSize = {1, 1, 1}; - const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(params.format); - device.GetQueue().WriteTexture(&dest, params.inputData.data(), bytesPerTexel, - &dataLayout, &writeSize); - - // Create the TextureView for the source texture with the specified swizzle. - wgpu::TextureViewDescriptor viewDesc = {}; - wgpu::TextureComponentSwizzleDescriptor swizzleDesc = {}; - swizzleDesc.swizzle.r = swizzleRed; - swizzleDesc.swizzle.g = swizzleGreen; - swizzleDesc.swizzle.b = swizzleBlue; - swizzleDesc.swizzle.a = swizzleAlpha; - viewDesc.nextInChain = &swizzleDesc; - wgpu::TextureView textureView = texture.CreateView(&viewDesc); - - // Set up bind group using the swizzled texture view. - wgpu::BindGroup bindGroup = - utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, textureView}}); - - // Issue render commands. - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - wgpu::RenderPassDescriptor renderPassDesc = {}; - wgpu::RenderPassColorAttachment colorAttachment = {}; - colorAttachment.view = outputTexture.CreateView(); - colorAttachment.loadOp = wgpu::LoadOp::Clear; - colorAttachment.storeOp = wgpu::StoreOp::Store; - colorAttachment.clearValue = {0.0f, 0.0f, 0.0f, 0.0f}; - renderPassDesc.colorAttachmentCount = 1; - renderPassDesc.colorAttachments = &colorAttachment; - - wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); - pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup); - pass.Draw(3); - pass.End(); - - // Submit commands to the queue. - wgpu::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - utils::RGBA8 expectedColor = - utils::RGBA8(GetExpectedValue(swizzleRed, params.baseLoadValues), - GetExpectedValue(swizzleGreen, params.baseLoadValues), - GetExpectedValue(swizzleBlue, params.baseLoadValues), - GetExpectedValue(swizzleAlpha, params.baseLoadValues)); - EXPECT_PIXEL_RGBA8_EQ(expectedColor, outputTexture, 0, 0); - }; + wgpu::TextureDescriptor textureDesc = {}; + textureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding; + textureDesc.size = {1, 1, 1}; + textureDesc.format = params.format; + wgpu::Texture inputTexture = device.CreateTexture(&textureDesc); for (auto swizzleRed : kComponentSwizzles) { - RunSwizzleTest(swizzleRed, wgpu::ComponentSwizzle::G, wgpu::ComponentSwizzle::B, - wgpu::ComponentSwizzle::A); + RunSwizzleTest(params, inputTexture, swizzleRed, wgpu::ComponentSwizzle::G, + wgpu::ComponentSwizzle::B, wgpu::ComponentSwizzle::A); } for (auto swizzleGreen : kComponentSwizzles) { - RunSwizzleTest(wgpu::ComponentSwizzle::R, swizzleGreen, wgpu::ComponentSwizzle::B, - wgpu::ComponentSwizzle::A); + RunSwizzleTest(params, inputTexture, wgpu::ComponentSwizzle::R, swizzleGreen, + wgpu::ComponentSwizzle::B, wgpu::ComponentSwizzle::A); } for (auto swizzleBlue : kComponentSwizzles) { - RunSwizzleTest(wgpu::ComponentSwizzle::R, wgpu::ComponentSwizzle::G, swizzleBlue, - wgpu::ComponentSwizzle::A); + RunSwizzleTest(params, inputTexture, wgpu::ComponentSwizzle::R, + wgpu::ComponentSwizzle::G, swizzleBlue, wgpu::ComponentSwizzle::A); } for (auto swizzleAlpha : kComponentSwizzles) { - RunSwizzleTest(wgpu::ComponentSwizzle::R, wgpu::ComponentSwizzle::G, - wgpu::ComponentSwizzle::B, swizzleAlpha); + RunSwizzleTest(params, inputTexture, wgpu::ComponentSwizzle::R, + wgpu::ComponentSwizzle::G, wgpu::ComponentSwizzle::B, swizzleAlpha); } } + + wgpu::RenderPipeline pipeline; + wgpu::Texture outputTexture; }; // Test that texture component swizzle works as expected when the 'texture-component-swizzle' @@ -230,7 +233,35 @@ RunTest(params); } -DAWN_INSTANTIATE_TEST(TextureComponentSwizzleTest, D3D12Backend(), MetalBackend(), VulkanBackend()); +// Test that custom swizzle correctly reorders texture components and that a subsequent use of the +// default swizzle correctly reverts to the standard component mapping. +TEST_P(TextureComponentSwizzleTest, UseDefaultSwizzleAfterNonDefaultSwizzle) { + TestParams params; + params.format = wgpu::TextureFormat::RGBA8Unorm; + params.inputData = {255, 128, 64, 0}; + params.baseLoadValues = {255, 128, 64, 0}; + + wgpu::TextureDescriptor textureDesc = {}; + textureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding; + textureDesc.size = {1, 1, 1}; + textureDesc.format = params.format; + wgpu::Texture inputTexture = device.CreateTexture(&textureDesc); + + // First, use a non-default swizzle. + RunSwizzleTest(params, inputTexture, wgpu::ComponentSwizzle::One, wgpu::ComponentSwizzle::One, + wgpu::ComponentSwizzle::One, wgpu::ComponentSwizzle::One); + + // Then, use a default swizzle. + RunSwizzleTest(params, inputTexture, wgpu::ComponentSwizzle::R, wgpu::ComponentSwizzle::G, + wgpu::ComponentSwizzle::B, wgpu::ComponentSwizzle::A); +} + +DAWN_INSTANTIATE_TEST(TextureComponentSwizzleTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + OpenGLESBackend(), + VulkanBackend()); } // anonymous namespace } // namespace dawn
diff --git a/src/dawn/tests/unittests/validation/CompatValidationTests.cpp b/src/dawn/tests/unittests/validation/CompatValidationTests.cpp index ea74a2a..c163322 100644 --- a/src/dawn/tests/unittests/validation/CompatValidationTests.cpp +++ b/src/dawn/tests/unittests/validation/CompatValidationTests.cpp
@@ -2003,6 +2003,86 @@ TestMaxVertexAttributes(true, true); } +enum class SwizzleChannel { + Red, + Green, + Blue, + Alpha, +}; + +class CompatTextureViewSwizzleValidationTests : public CompatTextureViewValidationTests { + protected: + std::vector<wgpu::FeatureName> GetRequiredFeatures() override { + std::vector<wgpu::FeatureName> requiredFeatures = + CompatTextureViewValidationTests::GetRequiredFeatures(); + requiredFeatures.push_back(wgpu::FeatureName::TextureComponentSwizzle); + return requiredFeatures; + } + + void SetSwizzleChannel(wgpu::TextureComponentSwizzle& swizzle, + SwizzleChannel channel, + wgpu::ComponentSwizzle value) { + switch (channel) { + case SwizzleChannel::Red: + swizzle.r = value; + break; + case SwizzleChannel::Green: + swizzle.g = value; + break; + case SwizzleChannel::Blue: + swizzle.b = value; + break; + case SwizzleChannel::Alpha: + swizzle.a = value; + break; + default: + DAWN_UNREACHABLE(); + } + } +}; + +// Test we get a validation error if we have 2 different swizzles of a texture +// in the same bind group. Unless FlexibleTextureViews is enabled. +TEST_P(CompatTextureViewSwizzleValidationTests, + CanNotDrawDifferentSwizzleSameTextureSameBindGroup) { + for (auto swizzleChannel : {SwizzleChannel::Red, SwizzleChannel::Green, SwizzleChannel::Blue, + SwizzleChannel::Alpha}) { + TestMultipleTextureViewValidationInRenderPass( + device, wgpu::TextureFormat::RGBA8Unorm, kRenderTwoTexturesOneBindgroupWGSL, + [this, &swizzleChannel](wgpu::Device device, wgpu::Texture texture, + wgpu::RenderPipeline pipeline, + std::function<void(wgpu::RenderPassEncoder pass)> drawFn) { + wgpu::TextureViewDescriptor viewDesc1; + wgpu::TextureComponentSwizzleDescriptor swizzleDesc1 = {}; + SetSwizzleChannel(swizzleDesc1.swizzle, swizzleChannel, + wgpu::ComponentSwizzle::Zero); + viewDesc1.nextInChain = &swizzleDesc1; + + wgpu::TextureViewDescriptor viewDesc2; + wgpu::TextureComponentSwizzleDescriptor swizzleDesc2 = {}; + SetSwizzleChannel(swizzleDesc2.swizzle, swizzleChannel, + wgpu::ComponentSwizzle::One); + viewDesc2.nextInChain = &swizzleDesc2; + + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), + {{0, texture.CreateView(&viewDesc1)}, {1, texture.CreateView(&viewDesc2)}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + utils::BasicRenderPass rp = utils::CreateBasicRenderPass(device, 4, 1); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&rp.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + drawFn(pass); + pass.End(); + + ASSERT_TEXTURE_VIEW_ERROR_IF_NO_FLEXIBLE_FEATURE( + encoder.Finish(), testing::HasSubstr("different views")); + }); + } +} + INSTANTIATE_TEST_SUITE_P(, CompatTextureViewValidationTests, ::testing::Values(FlexibleTextureViewsFeature::Disabled, @@ -2015,6 +2095,12 @@ FlexibleTextureViewsFeature::Enabled), CompatTextureViewValidationTests::PrintToStringParamName); +INSTANTIATE_TEST_SUITE_P(, + CompatTextureViewSwizzleValidationTests, + ::testing::Values(FlexibleTextureViewsFeature::Disabled, + FlexibleTextureViewsFeature::Enabled), + CompatTextureViewValidationTests::PrintToStringParamName); + class CompatLayoutLimitsTests : public CompatValidationTest { protected: void GetRequiredLimits(const dawn::utils::ComboLimits& supported,