Support cube map and cube map array

This patch implements cube map and cube map array texture views on
all back-ends and adds related end2end tests.

BUG=dawn:16
TEST=dawn_end2end_tests

Change-Id: I3ac3f493eb92ac551371041039bd5cf39df53050
Reviewed-on: https://dawn-review.googlesource.com/c/2220
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index f200a46..4fd16ab 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -210,7 +210,17 @@
                 mSrvDesc.Texture2DArray.PlaneSlice = 0;
                 mSrvDesc.Texture2DArray.ResourceMinLODClamp = 0;
                 break;
-
+            case dawn::TextureViewDimension::Cube:
+            case dawn::TextureViewDimension::CubeArray:
+                ASSERT(texture->GetDimension() == dawn::TextureDimension::e2D);
+                ASSERT(descriptor->layerCount % 6 == 0);
+                mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
+                mSrvDesc.TextureCubeArray.First2DArrayFace = descriptor->baseArrayLayer;
+                mSrvDesc.TextureCubeArray.NumCubes = descriptor->layerCount / 6;
+                mSrvDesc.TextureCubeArray.MostDetailedMip = descriptor->baseMipLevel;
+                mSrvDesc.TextureCubeArray.MipLevels = descriptor->levelCount;
+                mSrvDesc.TextureCubeArray.ResourceMinLODClamp = 0;
+                break;
             default:
                 UNREACHABLE();
         }
diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm
index 5102055..818c8da 100644
--- a/src/dawn_native/metal/TextureMTL.mm
+++ b/src/dawn_native/metal/TextureMTL.mm
@@ -76,6 +76,10 @@
                     return MTLTextureType2D;
                 case dawn::TextureViewDimension::e2DArray:
                     return MTLTextureType2DArray;
+                case dawn::TextureViewDimension::Cube:
+                    return MTLTextureTypeCube;
+                case dawn::TextureViewDimension::CubeArray:
+                    return MTLTextureTypeCubeArray;
                 default:
                     UNREACHABLE();
                     return MTLTextureType2D;
diff --git a/src/dawn_native/opengl/TextureGL.cpp b/src/dawn_native/opengl/TextureGL.cpp
index bbe6a12..a16b07a 100644
--- a/src/dawn_native/opengl/TextureGL.cpp
+++ b/src/dawn_native/opengl/TextureGL.cpp
@@ -41,6 +41,10 @@
                     return GL_TEXTURE_2D;
                 case dawn::TextureViewDimension::e2DArray:
                     return GL_TEXTURE_2D_ARRAY;
+                case dawn::TextureViewDimension::Cube:
+                    return GL_TEXTURE_CUBE_MAP;
+                case dawn::TextureViewDimension::CubeArray:
+                    return GL_TEXTURE_CUBE_MAP_ARRAY;
                 default:
                     UNREACHABLE();
                     return GL_TEXTURE_2D;
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index f486dc1..6cba63cf 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -40,6 +40,10 @@
                     return VK_IMAGE_VIEW_TYPE_2D;
                 case dawn::TextureViewDimension::e2DArray:
                     return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
+                case dawn::TextureViewDimension::Cube:
+                    return VK_IMAGE_VIEW_TYPE_CUBE;
+                case dawn::TextureViewDimension::CubeArray:
+                    return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
                 default:
                     UNREACHABLE();
             }
diff --git a/src/tests/end2end/TextureViewTests.cpp b/src/tests/end2end/TextureViewTests.cpp
index 0c839fb..91dbd13 100644
--- a/src/tests/end2end/TextureViewTests.cpp
+++ b/src/tests/end2end/TextureViewTests.cpp
@@ -18,6 +18,8 @@
 #include "common/Constants.h"
 #include "utils/DawnHelpers.h"
 
+#include <array>
+
 constexpr static unsigned int kRTSize = 64;
 
 class TextureViewTest : public DawnTest {
@@ -55,6 +57,7 @@
 
         mVSModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
             #version 450
+            layout (location = 0) out vec2 o_texCoord;
             void main() {
                 const vec2 pos[6] = vec2[6](vec2(-2.f, -2.f),
                                             vec2(-2.f,  2.f),
@@ -62,7 +65,14 @@
                                             vec2(-2.f,  2.f),
                                             vec2( 2.f, -2.f),
                                             vec2( 2.f,  2.f));
+                const vec2 texCoord[6] = vec2[6](vec2(0.f, 0.f),
+                                                 vec2(0.f, 1.f),
+                                                 vec2(1.f, 0.f),
+                                                 vec2(0.f, 1.f),
+                                                 vec2(1.f, 0.f),
+                                                 vec2(1.f, 1.f));
                 gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f);
+                o_texCoord = texCoord[gl_VertexIndex];
             }
         )");
     }
@@ -180,11 +190,12 @@
             #version 450
             layout(set = 0, binding = 0) uniform sampler sampler0;
             layout(set = 0, binding = 1) uniform texture2D texture0;
+            layout(location = 0) in vec2 texCoord;
             layout(location = 0) out vec4 fragColor;
 
             void main() {
                 fragColor =
-                    texture(sampler2D(texture0, sampler0), vec2(gl_FragCoord.xy / 2.0));
+                    texture(sampler2D(texture0, sampler0), texCoord);
             }
         )";
 
@@ -218,13 +229,14 @@
             #version 450
             layout(set = 0, binding = 0) uniform sampler sampler0;
             layout(set = 0, binding = 1) uniform texture2DArray texture0;
+            layout(location = 0) in vec2 texCoord;
             layout(location = 0) out vec4 fragColor;
 
             void main() {
                 fragColor =
-                    texture(sampler2DArray(texture0, sampler0), vec3(gl_FragCoord.xy / 2.0, 0)) +
-                    texture(sampler2DArray(texture0, sampler0), vec3(gl_FragCoord.xy / 2.0, 1)) +
-                    texture(sampler2DArray(texture0, sampler0), vec3(gl_FragCoord.xy / 2.0, 2));
+                    texture(sampler2DArray(texture0, sampler0), vec3(texCoord, 0)) +
+                    texture(sampler2DArray(texture0, sampler0), vec3(texCoord, 1)) +
+                    texture(sampler2DArray(texture0, sampler0), vec3(texCoord, 2));
             }
         )";
 
@@ -235,6 +247,74 @@
         Verify(textureView, fragmentShader, expected);
     }
 
+    std::string CreateFragmentShaderForCubeMapFace(uint32_t layer, bool isCubeMapArray) {
+        // Reference: https://en.wikipedia.org/wiki/Cube_mapping
+        const std::array<std::string, 6> kCoordsToCubeMapFace = {{
+             " 1.f,   tc,  -sc",  // Positive X
+             "-1.f,   tc,   sc",  // Negative X
+             "  sc,  1.f,  -tc",  // Positive Y
+             "  sc, -1.f,   tc",  // Negative Y
+             "  sc,   tc,  1.f",  // Positive Z
+             " -sc,   tc, -1.f",  // Negative Z
+            }};
+
+        const std::string textureType = isCubeMapArray ? "textureCubeArray" : "textureCube";
+        const std::string samplerType = isCubeMapArray ? "samplerCubeArray" : "samplerCube";
+        const uint32_t cubeMapArrayIndex = layer / 6;
+        const std::string coordToCubeMapFace = kCoordsToCubeMapFace[layer % 6];
+
+        std::ostringstream stream;
+        stream << R"(
+            #version 450
+            layout(set = 0, binding = 0) uniform sampler sampler0;
+            layout(set = 0, binding = 1) uniform )" << textureType << R"( texture0;
+            layout(location = 0) in vec2 texCoord;
+            layout(location = 0) out vec4 fragColor;
+            void main() {
+                float sc = 2.f * texCoord.x - 1.f;
+                float tc = 2.f * texCoord.y - 1.f;
+                fragColor = texture()" << samplerType << "(texture0, sampler0), ";
+
+        if (isCubeMapArray) {
+            stream << "vec4(" << coordToCubeMapFace << ", " << cubeMapArrayIndex;
+        } else {
+            stream << "vec3(" << coordToCubeMapFace;
+        }
+
+        stream << R"());
+            })";
+
+        return stream.str();
+    }
+
+    void TextureCubeMapTest(uint32_t textureArrayLayers,
+                            uint32_t textureViewBaseLayer,
+                            uint32_t textureViewLayerCount,
+                            bool isCubeMapArray) {
+        constexpr uint32_t kMipLevels = 1u;
+        initTexture(textureArrayLayers, kMipLevels);
+
+        ASSERT_TRUE((textureViewLayerCount == 6) ||
+                    (isCubeMapArray && textureViewLayerCount % 6 == 0));
+
+        dawn::TextureViewDescriptor descriptor = mDefaultTextureViewDescriptor;
+        descriptor.dimension = (isCubeMapArray) ?
+            dawn::TextureViewDimension::CubeArray : dawn::TextureViewDimension::Cube;
+        descriptor.baseArrayLayer = textureViewBaseLayer;
+        descriptor.layerCount = textureViewLayerCount;
+
+        dawn::TextureView cubeMapTextureView = mTexture.CreateTextureView(&descriptor);
+
+        // Check the data in the every face of the cube map (array) texture view.
+        for (uint32_t layer = 0; layer < textureViewLayerCount; ++layer) {
+            const std::string &fragmentShader =
+                CreateFragmentShaderForCubeMapFace(layer, isCubeMapArray);
+
+            int expected = GenerateTestPixelValue(textureViewBaseLayer + layer, 0);
+            Verify(cubeMapTextureView, fragmentShader.c_str(), expected);
+        }
+    }
+
     dawn::BindGroupLayout mBindGroupLayout;
     dawn::PipelineLayout mPipelineLayout;
     dawn::Sampler mSampler;
@@ -259,13 +339,14 @@
             #version 450
             layout(set = 0, binding = 0) uniform sampler sampler0;
             layout(set = 0, binding = 1) uniform texture2DArray texture0;
+            layout(location = 0) in vec2 texCoord;
             layout(location = 0) out vec4 fragColor;
 
             void main() {
                 fragColor =
-                    texture(sampler2DArray(texture0, sampler0), vec3(gl_FragCoord.xy / 2.0, 0)) +
-                    texture(sampler2DArray(texture0, sampler0), vec3(gl_FragCoord.xy / 2.0, 1)) +
-                    texture(sampler2DArray(texture0, sampler0), vec3(gl_FragCoord.xy / 2.0, 2));
+                    texture(sampler2DArray(texture0, sampler0), vec3(texCoord, 0)) +
+                    texture(sampler2DArray(texture0, sampler0), vec3(texCoord, 1)) +
+                    texture(sampler2DArray(texture0, sampler0), vec3(texCoord, 2));
             }
         )";
 
@@ -299,4 +380,45 @@
     Texture2DArrayViewTest(6, 6, 2, 4);
 }
 
+// Test sampling from a cube map texture view that covers a whole 2D array texture.
+TEST_P(TextureViewTest, TextureCubeMapOnWholeTexture) {
+    constexpr uint32_t kTotalLayers = 6;
+    TextureCubeMapTest(kTotalLayers, 0, kTotalLayers, false);
+}
+
+// Test sampling from a cube map texture view that covers a sub part of a 2D array texture.
+TEST_P(TextureViewTest, TextureCubeMapViewOnPartOfTexture) {
+    TextureCubeMapTest(10, 2, 6, false);
+}
+
+// Test sampling from a cube map texture view that covers the last layer of a 2D array texture.
+TEST_P(TextureViewTest, TextureCubeMapViewCoveringLastLayer) {
+    constexpr uint32_t kTotalLayers = 10;
+    constexpr uint32_t kBaseLayer = 4;
+    TextureCubeMapTest(kTotalLayers, kBaseLayer, kTotalLayers - kBaseLayer, false);
+}
+
+// Test sampling from a cube map texture array view that covers a whole 2D array texture.
+TEST_P(TextureViewTest, TextureCubeMapArrayaOnWholeTexture) {
+    constexpr uint32_t kTotalLayers = 12;
+    TextureCubeMapTest(kTotalLayers, 0, kTotalLayers, true);
+}
+
+// Test sampling from a cube map texture array view that covers a sub part of a 2D array texture.
+TEST_P(TextureViewTest, TextureCubeMapArrayViewOnPartOfTexture) {
+    TextureCubeMapTest(20, 3, 12, true);
+}
+
+// Test sampling from a cube map texture array view that covers the last layer of a 2D array texture.
+TEST_P(TextureViewTest, TextureCubeMapArrayViewCoveringLastLayer) {
+    constexpr uint32_t kTotalLayers = 20;
+    constexpr uint32_t kBaseLayer = 8;
+    TextureCubeMapTest(kTotalLayers, kBaseLayer, kTotalLayers - kBaseLayer, true);
+}
+
+// Test sampling from a cube map array texture view that only has a single cube map.
+TEST_P(TextureViewTest, TextureCubeMapArrayViewSingleCubeMap) {
+    TextureCubeMapTest(20, 7, 6, true);
+}
+
 DAWN_INSTANTIATE_TEST(TextureViewTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend)