| // Copyright 2026 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <sstream> |
| #include <vector> |
| |
| #include "dawn/tests/DawnTest.h" |
| #include "dawn/utils/ComboRenderPipelineDescriptor.h" |
| #include "dawn/utils/WGPUHelpers.h" |
| |
| namespace dawn { |
| namespace { |
| |
| enum class TextureType { |
| Texture2D, |
| Texture2DArray, |
| }; |
| |
| std::ostream& operator<<(std::ostream& o, TextureType type) { |
| switch (type) { |
| case TextureType::Texture2D: |
| o << "2D"; |
| break; |
| case TextureType::Texture2DArray: |
| o << "2DArray"; |
| break; |
| } |
| return o; |
| } |
| |
| DAWN_TEST_PARAM_STRUCT(TextureSampleCompare2DTestParams, TextureType); |
| |
| class TextureSampleCompare2DTest : public DawnTestWithParams<TextureSampleCompare2DTestParams> { |
| protected: |
| void SetUp() override { DawnTestWithParams<TextureSampleCompare2DTestParams>::SetUp(); } |
| }; |
| |
| // Test that textureSampleCompare with 2D polyfill returns the same result as the native |
| // implementation. |
| TEST_P(TextureSampleCompare2DTest, Compare2D) { |
| // Only run on Vulkan for this specific polyfill test. |
| DAWN_TEST_UNSUPPORTED_IF(!IsVulkan()); |
| |
| const bool isArray = GetParam().mTextureType == TextureType::Texture2DArray; |
| |
| static constexpr uint32_t kSize = 4; |
| wgpu::Extent3D textureSize = {kSize, kSize, isArray ? 2u : 1u}; |
| |
| // Create a depth texture. |
| wgpu::TextureDescriptor depthDesc; |
| depthDesc.format = wgpu::TextureFormat::Depth32Float; |
| depthDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding; |
| depthDesc.size = textureSize; |
| wgpu::Texture depthTexture = device.CreateTexture(&depthDesc); |
| |
| // Create a pipeline to initialize the depth texture. |
| utils::ComboRenderPipelineDescriptor setupPipelineDesc; |
| setupPipelineDesc.vertex.module = utils::CreateShaderModule(device, R"( |
| @vertex fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4f { |
| var pos = array( |
| vec2f(-1.0, -1.0), |
| vec2f( 3.0, -1.0), |
| vec2f(-1.0, 3.0)); |
| return vec4f(pos[VertexIndex], 0.0, 1.0); |
| } |
| )"); |
| setupPipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"( |
| @fragment fn main(@builtin(position) pos : vec4f) -> @builtin(frag_depth) f32 { |
| // Set only texel (1,1) to 1.0, others to 0.0. |
| if (u32(pos.x) == 1u && u32(pos.y) == 1u) { |
| return 1.0; |
| } |
| return 0.0; |
| } |
| )"); |
| setupPipelineDesc.EnableDepthStencil(wgpu::TextureFormat::Depth32Float); |
| setupPipelineDesc.cDepthStencil.depthWriteEnabled = wgpu::OptionalBool::True; |
| setupPipelineDesc.cFragment.targetCount = 0; |
| wgpu::RenderPipeline setupPipeline = device.CreateRenderPipeline(&setupPipelineDesc); |
| |
| // Initialize the depth texture. |
| { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| for (uint32_t i = 0; i < textureSize.depthOrArrayLayers; ++i) { |
| wgpu::TextureViewDescriptor viewDesc; |
| viewDesc.dimension = wgpu::TextureViewDimension::e2D; |
| viewDesc.baseArrayLayer = i; |
| viewDesc.arrayLayerCount = 1; |
| |
| utils::ComboRenderPassDescriptor renderPass({}, depthTexture.CreateView(&viewDesc)); |
| renderPass.UnsetDepthStencilLoadStoreOpsForFormat(wgpu::TextureFormat::Depth32Float); |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 0.0f; |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); |
| pass.SetPipeline(setupPipeline); |
| pass.Draw(3); |
| pass.End(); |
| } |
| wgpu::CommandBuffer commands = encoder.Finish(); |
| queue.Submit(1, &commands); |
| } |
| |
| // Create a comparison sampler. |
| wgpu::SamplerDescriptor samplerDesc; |
| samplerDesc.minFilter = wgpu::FilterMode::Linear; |
| samplerDesc.magFilter = wgpu::FilterMode::Linear; |
| samplerDesc.mipmapFilter = wgpu::MipmapFilterMode::Linear; |
| samplerDesc.compare = wgpu::CompareFunction::Less; |
| wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); |
| |
| // Create a render pipeline that outputs the result of textureSampleCompare. |
| utils::ComboRenderPipelineDescriptor pipelineDesc; |
| pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"( |
| @vertex fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4f { |
| var pos = array( |
| vec2f(-1.0, -1.0), |
| vec2f( 3.0, -1.0), |
| vec2f(-1.0, 3.0)); |
| return vec4f(pos[VertexIndex], 0.0, 1.0); |
| } |
| )"); |
| const char* textureType = isArray ? "texture_depth_2d_array" : "texture_depth_2d"; |
| const char* compareArgs = isArray ? "0u, 0.5" : "0.5"; |
| |
| std::ostringstream shader; |
| shader << "@group(0) @binding(0) var t : " << textureType << R"(; |
| @group(0) @binding(1) var s : sampler_comparison; |
| |
| @fragment fn main() -> @location(0) f32 { |
| // We are choosing a specific sample location to get a unique bilinear filtered result. |
| // This should match the original and polyfilled for all devices that have correct implementation. |
| return textureSampleCompare(t, s, vec2f(0.28, 0.27), )" |
| << compareArgs << R"(); |
| })"; |
| pipelineDesc.cFragment.module = utils::CreateShaderModule(device, shader.str().c_str()); |
| pipelineDesc.cTargets[0].format = wgpu::TextureFormat::R32Float; |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc); |
| |
| // Create an output texture to read the result. |
| wgpu::TextureDescriptor outputDesc; |
| outputDesc.format = wgpu::TextureFormat::R32Float; |
| outputDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc; |
| outputDesc.size = {1, 1, 1}; |
| wgpu::Texture outputTexture = device.CreateTexture(&outputDesc); |
| wgpu::TextureView outputView = outputTexture.CreateView(); |
| |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPass({outputView}); |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); |
| pass.SetPipeline(pipeline); |
| pass.SetBindGroup(0, utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), |
| {{0, depthTexture.CreateView()}, {1, sampler}})); |
| pass.Draw(3); |
| pass.End(); |
| |
| wgpu::CommandBuffer commands = encoder.Finish(); |
| queue.Submit(1, &commands); |
| // This test only checks the result implementation against a single 'interesting' point. |
| // A more robust test is of course possible but this confirms the bilinear filtering (in x and |
| // y). |
| constexpr float expected2D = 0.3596; |
| |
| EXPECT_PIXEL_FLOAT_TOLERANCE_EQ(expected2D, outputTexture, 0, 0, 0.01f); |
| } |
| |
| DAWN_INSTANTIATE_TEST_P(TextureSampleCompare2DTest, |
| {VulkanBackend(), VulkanBackend({"vulkan_sample_compare_2d_workaround"})}, |
| {TextureType::Texture2D, TextureType::Texture2DArray}); |
| |
| } // anonymous namespace |
| } // namespace dawn |