diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index f200a4683c..4fd16ab0f8 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -210,7 +210,17 @@ namespace dawn_native { namespace d3d12 { 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 5102055846..818c8daf6d 100644 --- a/src/dawn_native/metal/TextureMTL.mm +++ b/src/dawn_native/metal/TextureMTL.mm @@ -76,6 +76,10 @@ namespace dawn_native { namespace metal { 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 bbe6a1223b..a16b07ab11 100644 --- a/src/dawn_native/opengl/TextureGL.cpp +++ b/src/dawn_native/opengl/TextureGL.cpp @@ -41,6 +41,10 @@ namespace dawn_native { namespace opengl { 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 f486dc1aa4..6cba63cf3d 100644 --- a/src/dawn_native/vulkan/TextureVk.cpp +++ b/src/dawn_native/vulkan/TextureVk.cpp @@ -40,6 +40,10 @@ namespace dawn_native { namespace vulkan { 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 0c839fbdde..91dbd13db1 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 + constexpr static unsigned int kRTSize = 64; class TextureViewTest : public DawnTest { @@ -55,6 +57,7 @@ protected: 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 @@ protected: 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 @@ protected: #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 @@ protected: #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 @@ protected: Verify(textureView, fragmentShader, expected); } + std::string CreateFragmentShaderForCubeMapFace(uint32_t layer, bool isCubeMapArray) { + // Reference: https://en.wikipedia.org/wiki/Cube_mapping + const std::array 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 @@ TEST_P(TextureViewTest, Default2DArrayTexture) { #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 @@ TEST_P(TextureViewTest, Texture2DArrayViewOnOneLevelOf2DArrayTexture) { 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)