From 33af94fd8d592438a73c9cbeb5687737345418cd Mon Sep 17 00:00:00 2001 From: Jiawei Shao Date: Wed, 5 Dec 2018 03:22:04 +0000 Subject: [PATCH] Implement using a layer of a texture as color attachment - Part I This patch implements using a layer of a texture as a color attachment on D3D12, Metal and Vulkan. This feature is not implemented on OpenGL back-ends in this patch. BUG=dawn:16 TEST=dawn_unittests Change-Id: Iffc194c6117f6e3e6506c6fcbfd51ca2fbe9ede8 Reviewed-on: https://dawn-review.googlesource.com/c/2660 Commit-Queue: Jiawei Shao Reviewed-by: Kai Ninomiya --- src/dawn_native/RenderPassDescriptor.cpp | 9 +- src/dawn_native/Texture.cpp | 10 + src/dawn_native/Texture.h | 4 + src/dawn_native/d3d12/TextureD3D12.cpp | 25 +- src/dawn_native/d3d12/TextureD3D12.h | 6 +- src/dawn_native/metal/CommandBufferMTL.mm | 3 +- src/tests/DawnTest.cpp | 3 +- src/tests/DawnTest.h | 10 +- src/tests/end2end/CopyTests.cpp | 2 +- src/tests/end2end/RenderPassLoadOpTests.cpp | 8 +- src/tests/end2end/TextureViewTests.cpp | 282 ++++++++++++++++---- 11 files changed, 288 insertions(+), 74 deletions(-) diff --git a/src/dawn_native/RenderPassDescriptor.cpp b/src/dawn_native/RenderPassDescriptor.cpp index 1426095021..22f3b9f07c 100644 --- a/src/dawn_native/RenderPassDescriptor.cpp +++ b/src/dawn_native/RenderPassDescriptor.cpp @@ -103,19 +103,20 @@ namespace dawn_native { RenderPassDescriptorBase* RenderPassDescriptorBuilder::GetResultImpl() { auto CheckOrSetSize = [this](const TextureViewBase* attachment) -> bool { + uint32_t mipLevel = attachment->GetBaseMipLevel(); if (this->mWidth == 0) { ASSERT(this->mHeight == 0); - this->mWidth = attachment->GetTexture()->GetSize().width; - this->mHeight = attachment->GetTexture()->GetSize().height; + this->mWidth = attachment->GetTexture()->GetSize().width >> mipLevel; + this->mHeight = attachment->GetTexture()->GetSize().height >> mipLevel; ASSERT(this->mWidth != 0 && this->mHeight != 0); return true; } ASSERT(this->mWidth != 0 && this->mHeight != 0); - return this->mWidth == attachment->GetTexture()->GetSize().width && - this->mHeight == attachment->GetTexture()->GetSize().height; + return this->mWidth == attachment->GetTexture()->GetSize().width >> mipLevel && + this->mHeight == attachment->GetTexture()->GetSize().height >> mipLevel; }; uint32_t attachmentCount = 0; diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp index 0923fccb79..00c8b9f955 100644 --- a/src/dawn_native/Texture.cpp +++ b/src/dawn_native/Texture.cpp @@ -300,7 +300,9 @@ namespace dawn_native { : ObjectBase(texture->GetDevice()), mTexture(texture), mFormat(descriptor->format), + mBaseMipLevel(descriptor->baseMipLevel), mLevelCount(descriptor->levelCount), + mBaseArrayLayer(descriptor->baseArrayLayer), mLayerCount(descriptor->layerCount) { } @@ -316,10 +318,18 @@ namespace dawn_native { return mFormat; } + uint32_t TextureViewBase::GetBaseMipLevel() const { + return mBaseMipLevel; + } + uint32_t TextureViewBase::GetLevelCount() const { return mLevelCount; } + uint32_t TextureViewBase::GetBaseArrayLayer() const { + return mBaseArrayLayer; + } + uint32_t TextureViewBase::GetLayerCount() const { return mLayerCount; } diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h index f1194f05b2..2020ec5348 100644 --- a/src/dawn_native/Texture.h +++ b/src/dawn_native/Texture.h @@ -76,14 +76,18 @@ namespace dawn_native { TextureBase* GetTexture(); dawn::TextureFormat GetFormat() const; + uint32_t GetBaseMipLevel() const; uint32_t GetLevelCount() const; + uint32_t GetBaseArrayLayer() const; uint32_t GetLayerCount() const; private: Ref mTexture; dawn::TextureFormat mFormat; + uint32_t mBaseMipLevel; uint32_t mLevelCount; + uint32_t mBaseArrayLayer; uint32_t mLayerCount; }; diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index 4fd16ab0f8..fb49dea1e2 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -226,22 +226,33 @@ namespace dawn_native { namespace d3d12 { } } + DXGI_FORMAT TextureView::GetD3D12Format() const { + return D3D12TextureFormat(GetFormat()); + } + const D3D12_SHADER_RESOURCE_VIEW_DESC& TextureView::GetSRVDescriptor() const { return mSrvDesc; } - // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture. - D3D12_RENDER_TARGET_VIEW_DESC TextureView::GetRTVDescriptor() { + D3D12_RENDER_TARGET_VIEW_DESC TextureView::GetRTVDescriptor() const { + ASSERT(GetTexture()->GetDimension() == dawn::TextureDimension::e2D); D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; - rtvDesc.Format = ToBackend(GetTexture())->GetD3D12Format(); - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - rtvDesc.Texture2D.MipSlice = 0; - rtvDesc.Texture2D.PlaneSlice = 0; + rtvDesc.Format = GetD3D12Format(); + // Currently we always use D3D12_TEX2D_ARRAY_RTV because we cannot specify base array layer + // and layer count in D3D12_TEX2D_RTV. For 2D texture views, we treat them as 1-layer 2D + // array textures. (Just like how we treat SRVs) + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_rtv + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_rtv + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.FirstArraySlice = GetBaseArrayLayer(); + rtvDesc.Texture2DArray.ArraySize = GetLayerCount(); + rtvDesc.Texture2DArray.MipSlice = GetBaseMipLevel(); + rtvDesc.Texture2DArray.PlaneSlice = 0; return rtvDesc; } // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture. - D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() { + D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() const { D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; dsvDesc.Format = ToBackend(GetTexture())->GetD3D12Format(); dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h index 23a0d52de1..2edfb7ce44 100644 --- a/src/dawn_native/d3d12/TextureD3D12.h +++ b/src/dawn_native/d3d12/TextureD3D12.h @@ -49,9 +49,11 @@ namespace dawn_native { namespace d3d12 { public: TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor); + DXGI_FORMAT GetD3D12Format() const; + const D3D12_SHADER_RESOURCE_VIEW_DESC& GetSRVDescriptor() const; - D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(); - D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(); + D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor() const; + D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor() const; private: D3D12_SHADER_RESOURCE_VIEW_DESC mSrvDesc; diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm index bf37d1f4ce..26b2bb1351 100644 --- a/src/dawn_native/metal/CommandBufferMTL.mm +++ b/src/dawn_native/metal/CommandBufferMTL.mm @@ -63,9 +63,8 @@ namespace dawn_native { namespace metal { descriptor.colorAttachments[i].loadAction = MTLLoadActionLoad; } - // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture. descriptor.colorAttachments[i].texture = - ToBackend(attachmentInfo.view->GetTexture())->GetMTLTexture(); + ToBackend(attachmentInfo.view)->GetMTLTexture(); descriptor.colorAttachments[i].storeAction = MTLStoreActionStore; } diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp index 8001941bf5..6e54cfe8bd 100644 --- a/src/tests/DawnTest.cpp +++ b/src/tests/DawnTest.cpp @@ -292,6 +292,7 @@ std::ostringstream& DawnTest::AddTextureExpectation(const char* file, uint32_t width, uint32_t height, uint32_t level, + uint32_t slice, uint32_t pixelSize, detail::Expectation* expectation) { uint32_t rowPitch = Align(width * pixelSize, kTextureRowPitchAlignment); @@ -302,7 +303,7 @@ std::ostringstream& DawnTest::AddTextureExpectation(const char* file, // We need to enqueue the copy immediately because by the time we resolve the expectation, // the texture might have been modified. dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(texture, level, 0, {x, y, 0}, dawn::TextureAspect::Color); + utils::CreateTextureCopyView(texture, level, slice, {x, y, 0}, dawn::TextureAspect::Color); dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(readback.buffer, readback.offset, rowPitch, 0); dawn::Extent3D copySize = {width, height, 1}; diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h index 1c681169b3..f0c88239c8 100644 --- a/src/tests/DawnTest.h +++ b/src/tests/DawnTest.h @@ -35,12 +35,13 @@ new detail::ExpectEq(expected)) // Test a pixel of the mip level 0 of a 2D texture. -#define EXPECT_PIXEL_RGBA8_EQ(expected, texture, x, y) \ - AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, sizeof(RGBA8), \ +#define EXPECT_PIXEL_RGBA8_EQ(expected, texture, x, y) \ + AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(RGBA8), \ new detail::ExpectEq(expected)) -#define EXPECT_TEXTURE_RGBA8_EQ(expected, texture, x, y, width, height, level) \ - AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, sizeof(RGBA8), \ +#define EXPECT_TEXTURE_RGBA8_EQ(expected, texture, x, y, width, height, level, slice) \ + AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \ + sizeof(RGBA8), \ new detail::ExpectEq(expected, (width) * (height))) struct RGBA8 { @@ -122,6 +123,7 @@ class DawnTest : public ::testing::TestWithParam { uint32_t width, uint32_t height, uint32_t level, + uint32_t slice, uint32_t pixelSize, detail::Expectation* expectation); diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp index 37323032f5..02c2e51e96 100644 --- a/src/tests/end2end/CopyTests.cpp +++ b/src/tests/end2end/CopyTests.cpp @@ -235,7 +235,7 @@ protected: std::vector expected(rowPitch / kBytesPerTexel * (textureSpec.copyHeight - 1) + textureSpec.copyWidth); PackTextureData(&bufferData[bufferSpec.offset / kBytesPerTexel], textureSpec.copyWidth, textureSpec.copyHeight, bufferSpec.rowPitch / kBytesPerTexel, expected.data(), textureSpec.copyWidth); - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, textureSpec.x, textureSpec.y, textureSpec.copyWidth, textureSpec.copyHeight, textureSpec.level) << + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, textureSpec.x, textureSpec.y, textureSpec.copyWidth, textureSpec.copyHeight, textureSpec.level, 0) << "Buffer to Texture copy failed copying " << bufferSpec.size << "-byte buffer with offset " << bufferSpec.offset << " and row pitch " << bufferSpec.rowPitch << " to [(" << textureSpec.x << ", " << textureSpec.y << "), (" << textureSpec.x + textureSpec.copyWidth << ", " << textureSpec.y + textureSpec.copyHeight << diff --git a/src/tests/end2end/RenderPassLoadOpTests.cpp b/src/tests/end2end/RenderPassLoadOpTests.cpp index 0c8cee3e86..d8efd8edd8 100644 --- a/src/tests/end2end/RenderPassLoadOpTests.cpp +++ b/src/tests/end2end/RenderPassLoadOpTests.cpp @@ -133,10 +133,10 @@ TEST_P(RenderPassLoadOpTests, ColorClearThenLoadAndDraw) { auto commandsClearGreen = commandsClearGreenBuilder.GetResult(); queue.Submit(1, &commandsClearZero); - EXPECT_TEXTURE_RGBA8_EQ(expectZero.data(), renderTarget, 0, 0, kRTSize, kRTSize, 0); + EXPECT_TEXTURE_RGBA8_EQ(expectZero.data(), renderTarget, 0, 0, kRTSize, kRTSize, 0, 0); queue.Submit(1, &commandsClearGreen); - EXPECT_TEXTURE_RGBA8_EQ(expectGreen.data(), renderTarget, 0, 0, kRTSize, kRTSize, 0); + EXPECT_TEXTURE_RGBA8_EQ(expectGreen.data(), renderTarget, 0, 0, kRTSize, kRTSize, 0, 0); // Part 2: draw a blue quad into the right half of the render target, and check result @@ -155,9 +155,9 @@ TEST_P(RenderPassLoadOpTests, ColorClearThenLoadAndDraw) { queue.Submit(1, &commandsLoad); // Left half should still be green - EXPECT_TEXTURE_RGBA8_EQ(expectGreen.data(), renderTarget, 0, 0, kRTSize / 2, kRTSize, 0); + EXPECT_TEXTURE_RGBA8_EQ(expectGreen.data(), renderTarget, 0, 0, kRTSize / 2, kRTSize, 0, 0); // Right half should now be blue - EXPECT_TEXTURE_RGBA8_EQ(expectBlue.data(), renderTarget, kRTSize / 2, 0, kRTSize / 2, kRTSize, 0); + EXPECT_TEXTURE_RGBA8_EQ(expectBlue.data(), renderTarget, kRTSize / 2, 0, kRTSize / 2, kRTSize, 0, 0); } DAWN_INSTANTIATE_TEST(RenderPassLoadOpTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend) diff --git a/src/tests/end2end/TextureViewTests.cpp b/src/tests/end2end/TextureViewTests.cpp index 9a334706e9..98926a1884 100644 --- a/src/tests/end2end/TextureViewTests.cpp +++ b/src/tests/end2end/TextureViewTests.cpp @@ -16,13 +16,59 @@ #include "common/Assert.h" #include "common/Constants.h" +#include "common/Math.h" #include "utils/DawnHelpers.h" #include constexpr static unsigned int kRTSize = 64; +constexpr dawn::TextureFormat kDefaultFormat = dawn::TextureFormat::R8G8B8A8Unorm; +constexpr uint32_t kBytesPerTexel = 4; -class TextureViewTest : public DawnTest { +namespace { + dawn::Texture Create2DTexture(dawn::Device device, + uint32_t width, + uint32_t height, + uint32_t layerCount, + uint32_t levelCount, + dawn::TextureUsageBit usage) { + dawn::TextureDescriptor descriptor; + descriptor.dimension = dawn::TextureDimension::e2D; + descriptor.size.width = width; + descriptor.size.height = height; + descriptor.size.depth = 1; + descriptor.arrayLayer = layerCount; + descriptor.format = kDefaultFormat; + descriptor.levelCount = levelCount; + descriptor.usage = usage; + return device.CreateTexture(&descriptor); + } + + dawn::ShaderModule CreateDefaultVertexShaderModule(dawn::Device device) { + return 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), + vec2( 2.f, -2.f), + 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]; + } + )"); + } +} // anonymous namespace + +class TextureViewSamplingTest : public DawnTest { protected: // Generates an arbitrary pixel value per-layer-per-level, used for the "actual" uploaded // textures and the "expected" results. @@ -55,50 +101,22 @@ protected: mPipelineLayout = utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); - 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), - vec2( 2.f, -2.f), - 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]; - } - )"); + mVSModule = CreateDefaultVertexShaderModule(device); } void initTexture(uint32_t layerCount, uint32_t levelCount) { ASSERT(layerCount > 0 && levelCount > 0); - constexpr dawn::TextureFormat kFormat = dawn::TextureFormat::R8G8B8A8Unorm; - const uint32_t textureWidthLevel0 = 1 << levelCount; const uint32_t textureHeightLevel0 = 1 << levelCount; - - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = textureWidthLevel0; - descriptor.size.height = textureHeightLevel0; - descriptor.size.depth = 1; - descriptor.arrayLayer = layerCount; - descriptor.format = kFormat; - descriptor.levelCount = levelCount; - descriptor.usage = dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled; - mTexture = device.CreateTexture(&descriptor); + constexpr dawn::TextureUsageBit kUsage = + dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled; + mTexture = Create2DTexture( + device, textureWidthLevel0, textureHeightLevel0, layerCount, levelCount, kUsage); mDefaultTextureViewDescriptor.nextInChain = nullptr; mDefaultTextureViewDescriptor.dimension = dawn::TextureViewDimension::e2DArray; - mDefaultTextureViewDescriptor.format = kFormat; + mDefaultTextureViewDescriptor.format = kDefaultFormat; mDefaultTextureViewDescriptor.baseMipLevel = 0; mDefaultTextureViewDescriptor.levelCount = levelCount; mDefaultTextureViewDescriptor.baseArrayLayer = 0; @@ -328,7 +346,7 @@ protected: }; // Test drawing a rect with a 2D array texture. -TEST_P(TextureViewTest, Default2DArrayTexture) { +TEST_P(TextureViewSamplingTest, Default2DArrayTexture) { // TODO(cwallez@chromium.org) understand what the issue is DAWN_SKIP_TEST_IF(IsVulkan() && IsNvidia()); @@ -359,58 +377,58 @@ TEST_P(TextureViewTest, Default2DArrayTexture) { } // Test sampling from a 2D texture view created on a 2D array texture. -TEST_P(TextureViewTest, Texture2DViewOn2DArrayTexture) { +TEST_P(TextureViewSamplingTest, Texture2DViewOn2DArrayTexture) { Texture2DViewTest(6, 1, 4, 0); } // Test sampling from a 2D array texture view created on a 2D array texture. -TEST_P(TextureViewTest, Texture2DArrayViewOn2DArrayTexture) { +TEST_P(TextureViewSamplingTest, Texture2DArrayViewOn2DArrayTexture) { DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); Texture2DArrayViewTest(6, 1, 2, 0); } // Test sampling from a 2D texture view created on a mipmap level of a 2D texture. -TEST_P(TextureViewTest, Texture2DViewOnOneLevelOf2DTexture) { +TEST_P(TextureViewSamplingTest, Texture2DViewOnOneLevelOf2DTexture) { Texture2DViewTest(1, 6, 0, 4); } // Test sampling from a 2D texture view created on a mipmap level of a 2D array texture layer. -TEST_P(TextureViewTest, Texture2DViewOnOneLevelOf2DArrayTexture) { +TEST_P(TextureViewSamplingTest, Texture2DViewOnOneLevelOf2DArrayTexture) { Texture2DViewTest(6, 6, 3, 4); } // Test sampling from a 2D array texture view created on a mipmap level of a 2D array texture. -TEST_P(TextureViewTest, Texture2DArrayViewOnOneLevelOf2DArrayTexture) { +TEST_P(TextureViewSamplingTest, Texture2DArrayViewOnOneLevelOf2DArrayTexture) { DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); Texture2DArrayViewTest(6, 6, 2, 4); } // Test sampling from a cube map texture view that covers a whole 2D array texture. -TEST_P(TextureViewTest, TextureCubeMapOnWholeTexture) { +TEST_P(TextureViewSamplingTest, 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) { +TEST_P(TextureViewSamplingTest, 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) { +TEST_P(TextureViewSamplingTest, 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, TextureCubeMapArrayOnWholeTexture) { +TEST_P(TextureViewSamplingTest, TextureCubeMapArrayOnWholeTexture) { 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) { +TEST_P(TextureViewSamplingTest, TextureCubeMapArrayViewOnPartOfTexture) { // Test failing on the GPU FYI Mac Pro (AMD), see // https://bugs.chromium.org/p/dawn/issues/detail?id=58 DAWN_SKIP_TEST_IF(IsMacOS() && IsMetal() && IsAMD()); @@ -419,7 +437,7 @@ TEST_P(TextureViewTest, TextureCubeMapArrayViewOnPartOfTexture) { } // Test sampling from a cube map texture array view that covers the last layer of a 2D array texture. -TEST_P(TextureViewTest, TextureCubeMapArrayViewCoveringLastLayer) { +TEST_P(TextureViewSamplingTest, TextureCubeMapArrayViewCoveringLastLayer) { // Test failing on the GPU FYI Mac Pro (AMD), see // https://bugs.chromium.org/p/dawn/issues/detail?id=58 DAWN_SKIP_TEST_IF(IsMacOS() && IsMetal() && IsAMD()); @@ -430,7 +448,7 @@ TEST_P(TextureViewTest, TextureCubeMapArrayViewCoveringLastLayer) { } // Test sampling from a cube map array texture view that only has a single cube map. -TEST_P(TextureViewTest, TextureCubeMapArrayViewSingleCubeMap) { +TEST_P(TextureViewSamplingTest, TextureCubeMapArrayViewSingleCubeMap) { // Test failing on the GPU FYI Mac Pro (AMD), see // https://bugs.chromium.org/p/dawn/issues/detail?id=58 DAWN_SKIP_TEST_IF(IsMacOS() && IsMetal() && IsAMD()); @@ -438,4 +456,170 @@ TEST_P(TextureViewTest, TextureCubeMapArrayViewSingleCubeMap) { TextureCubeMapTest(20, 7, 6, true); } -DAWN_INSTANTIATE_TEST(TextureViewTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend) +class TextureViewRenderingTest : public DawnTest { + protected: + void TextureLayerAsColorAttachmentTest(dawn::TextureViewDimension dimension, + uint32_t layerCount, + uint32_t levelCount, + uint32_t textureViewBaseLayer, + uint32_t textureViewBaseLevel) { + ASSERT(dimension == dawn::TextureViewDimension::e2D || + dimension == dawn::TextureViewDimension::e2DArray); + ASSERT_LT(textureViewBaseLayer, layerCount); + ASSERT_LT(textureViewBaseLevel, levelCount); + + const uint32_t textureWidthLevel0 = 1 << levelCount; + const uint32_t textureHeightLevel0 = 1 << levelCount; + constexpr dawn::TextureUsageBit kUsage = dawn::TextureUsageBit::OutputAttachment | + dawn::TextureUsageBit::TransferSrc; + dawn::Texture texture = Create2DTexture( + device, textureWidthLevel0, textureHeightLevel0, layerCount, levelCount, kUsage); + + dawn::TextureViewDescriptor descriptor; + descriptor.format = kDefaultFormat; + descriptor.dimension = dimension; + descriptor.baseArrayLayer = textureViewBaseLayer; + descriptor.layerCount = 1; + descriptor.baseMipLevel = textureViewBaseLevel; + descriptor.levelCount = 1; + dawn::TextureView textureView = texture.CreateTextureView(&descriptor); + + dawn::ShaderModule vsModule = CreateDefaultVertexShaderModule(device); + + // Clear textureView with Red(255, 0, 0, 255) and render Green(0, 255, 0, 255) into it + dawn::RenderPassDescriptor renderPassInfo = device.CreateRenderPassDescriptorBuilder() + .SetColorAttachment(0, textureView, dawn::LoadOp::Clear) + .SetColorAttachmentClearColor(0, 1.0, 0.0, 0.0, 1.0) + .GetResult(); + + const char* oneColorFragmentShader = R"( + #version 450 + layout(location = 0) out vec4 fragColor; + + void main() { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } + )"; + dawn::ShaderModule oneColorFsModule = + utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, oneColorFragmentShader); + + dawn::RenderPipeline oneColorPipeline = device.CreateRenderPipelineBuilder() + .SetColorAttachmentFormat(0, kDefaultFormat) + .SetStage(dawn::ShaderStage::Vertex, vsModule, "main") + .SetStage(dawn::ShaderStage::Fragment, oneColorFsModule, "main") + .GetResult(); + dawn::CommandBufferBuilder commandBufferBuilder = device.CreateCommandBufferBuilder(); + { + dawn::RenderPassEncoder pass = + commandBufferBuilder.BeginRenderPass(renderPassInfo); + pass.SetRenderPipeline(oneColorPipeline); + pass.DrawArrays(6, 1, 0, 0); + pass.EndPass(); + } + + dawn::CommandBuffer commands = commandBufferBuilder.GetResult(); + queue.Submit(1, &commands); + + // Check if the right pixels (Green) have been written into the right part of the texture. + uint32_t textureViewWidth = textureWidthLevel0 >> textureViewBaseLevel; + uint32_t textureViewHeight = textureHeightLevel0 >> textureViewBaseLevel; + uint32_t rowPitch = Align(kBytesPerTexel * textureWidthLevel0, kTextureRowPitchAlignment); + uint32_t expectedDataSize = + rowPitch / kBytesPerTexel * (textureWidthLevel0 - 1) + textureHeightLevel0; + constexpr RGBA8 kExpectedPixel(0, 255, 0, 255); + std::vector expected(expectedDataSize, kExpectedPixel); + EXPECT_TEXTURE_RGBA8_EQ( + expected.data(), texture, 0, 0, textureViewWidth, textureViewHeight, + textureViewBaseLevel, textureViewBaseLayer); + } +}; + +// Test rendering into a 2D texture view created on a mipmap level of a 2D texture. +TEST_P(TextureViewRenderingTest, Texture2DViewOnALevelOf2DTextureAsColorAttachment) { + constexpr uint32_t kLayers = 1; + constexpr uint32_t kMipLevels = 4; + constexpr uint32_t kBaseLayer = 0; + + // Rendering into the first level + { + constexpr uint32_t kBaseLevel = 0; + TextureLayerAsColorAttachmentTest( + dawn::TextureViewDimension::e2D, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + } + + // Rendering into the last level + { + constexpr uint32_t kBaseLevel = kMipLevels - 1; + TextureLayerAsColorAttachmentTest( + dawn::TextureViewDimension::e2D, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + } +} + +// Test rendering into a 2D texture view created on a layer of a 2D array texture. +TEST_P(TextureViewRenderingTest, Texture2DViewOnALayerOf2DArrayTextureAsColorAttachment) { + constexpr uint32_t kMipLevels = 1; + constexpr uint32_t kBaseLevel = 0; + constexpr uint32_t kLayers = 10; + + // Rendering into the first layer + { + constexpr uint32_t kBaseLayer = 0; + TextureLayerAsColorAttachmentTest( + dawn::TextureViewDimension::e2D, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + } + + // Rendering into the last layer + { + constexpr uint32_t kBaseLayer = kLayers - 1; + TextureLayerAsColorAttachmentTest( + dawn::TextureViewDimension::e2D, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + } + +} + +// Test rendering into a 1-layer 2D array texture view created on a mipmap level of a 2D texture. +TEST_P(TextureViewRenderingTest, Texture2DArrayViewOnALevelOf2DTextureAsColorAttachment) { + constexpr uint32_t kLayers = 1; + constexpr uint32_t kMipLevels = 4; + constexpr uint32_t kBaseLayer = 0; + + // Rendering into the first level + { + constexpr uint32_t kBaseLevel = 0; + TextureLayerAsColorAttachmentTest( + dawn::TextureViewDimension::e2DArray, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + } + + // Rendering into the last level + { + constexpr uint32_t kBaseLevel = kMipLevels - 1; + TextureLayerAsColorAttachmentTest( + dawn::TextureViewDimension::e2DArray, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + } +} + +// Test rendering into a 1-layer 2D array texture view created on a layer of a 2D array texture. +TEST_P(TextureViewRenderingTest, Texture2DArrayViewOnALayerOf2DArrayTextureAsColorAttachment) { + constexpr uint32_t kMipLevels = 1; + constexpr uint32_t kBaseLevel = 0; + constexpr uint32_t kLayers = 10; + + // Rendering into the first layer + { + constexpr uint32_t kBaseLayer = 0; + TextureLayerAsColorAttachmentTest( + dawn::TextureViewDimension::e2DArray, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + } + + // Rendering into the last layer + { + constexpr uint32_t kBaseLayer = kLayers - 1; + TextureLayerAsColorAttachmentTest( + dawn::TextureViewDimension::e2DArray, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + } +} + +DAWN_INSTANTIATE_TEST(TextureViewSamplingTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend) + +// TODO(jiawei.shao@intel.com): support using a layer of a texture as color attachment on OpenGL +DAWN_INSTANTIATE_TEST(TextureViewRenderingTest, D3D12Backend, MetalBackend, VulkanBackend)