Add depth texture sampling and tests for only depth32float

This is currently the only depth format that can be sampled.

Bug: dawn:367
Change-Id: Ie35c3f7eeee03661838e301453f387ae99e671d9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/19702
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_native/Format.cpp b/src/dawn_native/Format.cpp
index 0bd879b..9c7fa58 100644
--- a/src/dawn_native/Format.cpp
+++ b/src/dawn_native/Format.cpp
@@ -74,11 +74,6 @@
     }
 
     bool Format::HasComponentType(Type componentType) const {
-        // Depth stencil textures need to be special cased but we don't support sampling them yet.
-        if (aspect != Color) {
-            return false;
-        }
-
         return componentType == type;
     }
 
@@ -150,6 +145,22 @@
             AddFormat(internalFormat);
         };
 
+        auto AddDepthFormat = [&AddFormat](wgpu::TextureFormat format, uint32_t byteSize,
+                                           Type type) {
+            Format internalFormat;
+            internalFormat.format = format;
+            internalFormat.isRenderable = true;
+            internalFormat.isCompressed = false;
+            internalFormat.isSupported = true;
+            internalFormat.supportsStorageUsage = false;
+            internalFormat.aspect = Aspect::Depth;
+            internalFormat.type = type;
+            internalFormat.blockByteSize = byteSize;
+            internalFormat.blockWidth = 1;
+            internalFormat.blockHeight = 1;
+            AddFormat(internalFormat);
+        };
+
         auto AddCompressedFormat = [&AddFormat](wgpu::TextureFormat format, uint32_t byteSize,
                                                 uint32_t width, uint32_t height, bool isSupported) {
             Format internalFormat;
@@ -214,8 +225,10 @@
         AddColorFormat(wgpu::TextureFormat::RGBA32Sint, true, true, 16, Type::Sint);
         AddColorFormat(wgpu::TextureFormat::RGBA32Float, true, true, 16, Type::Float);
 
-        // Depth stencil formats
-        AddDepthStencilFormat(wgpu::TextureFormat::Depth32Float, Aspect::Depth, 4);
+        // Depth only formats
+        AddDepthFormat(wgpu::TextureFormat::Depth32Float, 4, Type::Float);
+
+        // Packed depth/depth-stencil formats
         AddDepthStencilFormat(wgpu::TextureFormat::Depth24Plus, Aspect::Depth, 4);
         // TODO(cwallez@chromium.org): It isn't clear if this format should be copyable
         // because its size isn't well defined, is it 4, 5 or 8?
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index 28511ee..7e41d40 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -229,6 +229,7 @@
     "end2end/BufferTests.cpp",
     "end2end/ClipSpaceTests.cpp",
     "end2end/ColorStateTests.cpp",
+    "end2end/ComparisonSamplerTests.cpp",
     "end2end/CompressedTextureFormatTests.cpp",
     "end2end/ComputeCopyStorageBufferTests.cpp",
     "end2end/ComputeIndirectTests.cpp",
diff --git a/src/tests/end2end/ComparisonSamplerTests.cpp b/src/tests/end2end/ComparisonSamplerTests.cpp
new file mode 100644
index 0000000..5bcc984
--- /dev/null
+++ b/src/tests/end2end/ComparisonSamplerTests.cpp
@@ -0,0 +1,219 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "common/Assert.h"
+#include "common/Constants.h"
+#include "tests/DawnTest.h"
+#include "utils/ComboRenderPipelineDescriptor.h"
+#include "utils/WGPUHelpers.h"
+
+class ComparisonSamplerTest : public DawnTest {
+  protected:
+    void TestSetUp() override {
+        DawnTest::TestSetUp();
+
+        wgpu::ShaderModule vsModule =
+            utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
+                #version 450
+                void main() {
+                    gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
+                    gl_PointSize = 1.0;
+                }
+            )");
+
+        wgpu::ShaderModule fsModule =
+            utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
+                #version 450
+                layout(set = 0, binding = 0) uniform samplerShadow samp;
+                layout(set = 0, binding = 1) uniform texture2D tex;
+                layout(set = 0, binding = 2) uniform Uniforms {
+                    float compareRef;
+                };
+
+                layout(location = 0) out vec4 samplerResult;
+
+                void main() {
+                    samplerResult = vec4(texture(sampler2DShadow(tex, samp), vec3(0.5, 0.5, compareRef)));
+                }
+            )");
+
+        wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
+            device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ComparisonSampler},
+                     {1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture},
+                     {2, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}});
+
+        utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
+        pipelineDescriptor.vertexStage.module = vsModule;
+        pipelineDescriptor.cFragmentStage.module = fsModule;
+        pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
+        pipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
+
+        mRenderPipeline = device.CreateRenderPipeline(&pipelineDescriptor);
+
+        wgpu::BufferDescriptor uniformBufferDesc = {
+            .usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst,
+            .size = sizeof(float),
+        };
+        mUniformBuffer = device.CreateBuffer(&uniformBufferDesc);
+
+        wgpu::BufferDescriptor textureUploadDesc = {
+            .usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst,
+            .size = sizeof(float),
+        };
+        mTextureUploadBuffer = device.CreateBuffer(&textureUploadDesc);
+
+        wgpu::TextureDescriptor inputTextureDesc = {
+            .usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled |
+                     wgpu::TextureUsage::OutputAttachment,
+            .size = {1, 1, 1},
+            .format = wgpu::TextureFormat::Depth32Float,
+        };
+        mInputTexture = device.CreateTexture(&inputTextureDesc);
+
+        wgpu::TextureDescriptor outputTextureDesc = {
+            .usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc,
+            .size = {1, 1, 1},
+            .format = wgpu::TextureFormat::RGBA8Unorm,
+        };
+        mOutputTexture = device.CreateTexture(&outputTextureDesc);
+    }
+
+    void DoCompareRefTest(float compareRef,
+                          wgpu::CompareFunction compare,
+                          std::vector<float> textureValues) {
+        mUniformBuffer.SetSubData(0, sizeof(float), &compareRef);
+
+        wgpu::SamplerDescriptor samplerDesc = {
+            .compare = compare,
+        };
+        wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
+
+        wgpu::BindGroup bindGroup =
+            utils::MakeBindGroup(device, mRenderPipeline.GetBindGroupLayout(0),
+                                 {
+                                     {0, sampler},
+                                     {1, mInputTexture.CreateView()},
+                                     {2, mUniformBuffer},
+                                 });
+
+        for (float textureValue : textureValues) {
+            bool success = false;
+            switch (compare) {
+                case wgpu::CompareFunction::Never:
+                    success = false;
+                    break;
+                case wgpu::CompareFunction::Less:
+                    success = compareRef < textureValue;
+                    break;
+                case wgpu::CompareFunction::LessEqual:
+                    success = compareRef <= textureValue;
+                    break;
+                case wgpu::CompareFunction::Greater:
+                    success = compareRef > textureValue;
+                    break;
+                case wgpu::CompareFunction::GreaterEqual:
+                    success = compareRef >= textureValue;
+                    break;
+                case wgpu::CompareFunction::Equal:
+                    success = compareRef == textureValue;
+                    break;
+                case wgpu::CompareFunction::NotEqual:
+                    success = compareRef != textureValue;
+                    break;
+                case wgpu::CompareFunction::Always:
+                    success = true;
+                    break;
+                default:
+                    UNREACHABLE();
+                    break;
+            }
+
+            wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+
+            // Set the input depth texture to the provided texture value
+            if (textureValue >= 0.0 && textureValue <= 1.0) {
+                // For valid loadOp values, use a loadOp.
+                utils::ComboRenderPassDescriptor passDescriptor({}, mInputTexture.CreateView());
+                passDescriptor.cDepthStencilAttachmentInfo.clearDepth = textureValue;
+
+                wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
+                pass.EndPass();
+            } else {
+                if (IsOpenGL()) {
+                    // TODO(enga): We don't support copying to depth textures yet on OpenGL.
+                    return;
+                }
+                mTextureUploadBuffer.SetSubData(0, sizeof(float), &textureValue);
+                wgpu::BufferCopyView bufferCopyView = {
+                    .buffer = mTextureUploadBuffer,
+                    .offset = 0,
+                    .rowPitch = kTextureRowPitchAlignment,
+                    .imageHeight = 1,
+                };
+                wgpu::TextureCopyView textureCopyView = {
+                    .texture = mInputTexture,
+                    .origin = {0, 0, 0},
+                };
+                wgpu::Extent3D copySize = {1, 1, 1};
+                commandEncoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
+            }
+
+            // Render into the output texture
+            {
+                utils::ComboRenderPassDescriptor passDescriptor({mOutputTexture.CreateView()});
+                wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
+                pass.SetPipeline(mRenderPipeline);
+                pass.SetBindGroup(0, bindGroup);
+                pass.Draw(3);
+                pass.EndPass();
+            }
+
+            wgpu::CommandBuffer commands = commandEncoder.Finish();
+            queue.Submit(1, &commands);
+
+            EXPECT_PIXEL_RGBA8_EQ(success ? RGBA8(255, 255, 255, 255) : RGBA8(0, 0, 0, 0),
+                                  mOutputTexture, 0, 0);
+        }
+    }
+
+  private:
+    wgpu::RenderPipeline mRenderPipeline;
+    wgpu::Buffer mUniformBuffer;
+    wgpu::Buffer mTextureUploadBuffer;
+    wgpu::Texture mInputTexture;
+    wgpu::Texture mOutputTexture;
+};
+
+// Test that sampling with all of the compare functions works.
+TEST_P(ComparisonSamplerTest, CompareFunctions) {
+    // Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
+    for (float compareRef : {-0.1, 0.4, 1.2}) {
+        // Test negative, 0, below the ref, equal to, above the ref, 1, and above 1.
+        std::vector<float> values = {-0.2, 0.0, 0.3, 0.4, 0.5, 1.0, 1.3};
+
+        DoCompareRefTest(compareRef, wgpu::CompareFunction::Never, values);
+        DoCompareRefTest(compareRef, wgpu::CompareFunction::Less, values);
+        DoCompareRefTest(compareRef, wgpu::CompareFunction::LessEqual, values);
+        DoCompareRefTest(compareRef, wgpu::CompareFunction::Greater, values);
+        DoCompareRefTest(compareRef, wgpu::CompareFunction::GreaterEqual, values);
+        DoCompareRefTest(compareRef, wgpu::CompareFunction::Equal, values);
+        DoCompareRefTest(compareRef, wgpu::CompareFunction::NotEqual, values);
+        DoCompareRefTest(compareRef, wgpu::CompareFunction::Always, values);
+    }
+}
+
+// TODO(crbug.com/dawn/367): Does not work on D3D12 because we need to reinterpret the texture view
+// as R32Float to sample it. See tables here:
+// https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-1-formats
+DAWN_INSTANTIATE_TEST(ComparisonSamplerTest, MetalBackend(), OpenGLBackend(), VulkanBackend());