Vulkan: Support binding texture levels as a rendertarget

This CL refactors how TextureVk handles rendertargets.  It removes
the single rendertarget that previously supported 2D, and expands
the layer/level list of rendertargets to handle all cases.

Bug: angleproject:3184
Bug: angleproject:3996
Test: Texture2DTestES3.FramebufferTextureChangingBaselevel/ES3_Vulkan
Test: FramebufferRenderMipmapTest.RenderToMipmap/ES2_Vulkan
Test: FramebufferRenderMipmapTest.RenderToMipmap/ES3_Vulkan
Test: ComputeShaderTest.ImageStoreMipmapSlice/ES3_1_Vulkan
Change-Id: I466d0389cc6744994f88c40cc388fca694b53a99
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1854895
Reviewed-by: Ian Elliott <ianelliott@google.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Cody Northrop <cnorthrop@google.com>
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
index e910be1..025a718 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
@@ -79,6 +79,11 @@
 {
     switch (index.getType())
     {
+        case gl::TextureType::_2D:
+            *layerIndex = 0;
+            *layerCount = 1;
+            return;
+
         case gl::TextureType::CubeMap:
             *layerIndex = index.cubeMapFaceIndex();
             *layerCount = gl::kCubeFaceCount;
@@ -894,10 +899,12 @@
     mImage->initStagingBuffer(contextVk->getRenderer(), format, vk::kStagingBufferFlags,
                               mStagingBufferInitialSize);
 
-    mRenderTarget.init(mImage, &mDrawImageView, getNativeImageLevel(0), getNativeImageLayer(0));
-
-    // Force re-creation of layered render targets next time they are needed
-    mLayerRenderTargets.clear();
+    // Force re-creation of render targets next time they are needed
+    for (vk::RenderTargetVector &renderTargetLevels : mRenderTargets)
+    {
+        renderTargetLevels.clear();
+    }
+    mRenderTargets.clear();
 
     mSerial = contextVk->generateTextureSerial();
 }
@@ -1251,31 +1258,18 @@
                                                    GLsizei samples,
                                                    FramebufferAttachmentRenderTarget **rtOut)
 {
-    // Non-zero mip level attachments are an ES 3.0 feature.
-    ASSERT(imageIndex.getLevelIndex() == 0);
+    ASSERT(imageIndex.getLevelIndex() >= 0);
 
     ContextVk *contextVk = vk::GetImpl(context);
     ANGLE_TRY(ensureImageInitialized(contextVk));
 
     GLuint layerIndex = 0, layerCount = 0;
+    GetRenderTargetLayerCountAndIndex(mImage, imageIndex, &layerCount, &layerIndex);
 
-    switch (imageIndex.getType())
-    {
-        case gl::TextureType::_2D:
-            *rtOut = &mRenderTarget;
-            break;
-        case gl::TextureType::CubeMap:
-        case gl::TextureType::_2DArray:
-        case gl::TextureType::_3D:
-            // Special handling required for different types, grab the count and index
-            GetRenderTargetLayerCountAndIndex(mImage, imageIndex, &layerCount, &layerIndex);
+    ANGLE_TRY(initRenderTargets(contextVk, layerCount, imageIndex.getLevelIndex()));
 
-            ANGLE_TRY(initLayerRenderTargets(contextVk, layerCount));
-            *rtOut = &mLayerRenderTargets[layerIndex];
-            break;
-        default:
-            UNREACHABLE();
-    }
+    ASSERT(imageIndex.getLevelIndex() < static_cast<int32_t>(mRenderTargets.size()));
+    *rtOut = &mRenderTargets[imageIndex.getLevelIndex()][layerIndex];
 
     return angle::Result::Continue;
 }
@@ -1317,20 +1311,27 @@
                                       commandBuffer);
 }
 
-angle::Result TextureVk::initLayerRenderTargets(ContextVk *contextVk, GLuint layerCount)
+angle::Result TextureVk::initRenderTargets(ContextVk *contextVk,
+                                           GLuint layerCount,
+                                           GLuint levelIndex)
 {
+    if (mRenderTargets.size() <= levelIndex)
+    {
+        mRenderTargets.resize(levelIndex + 1);
+    }
+
     // Lazy init. Check if already initialized.
-    if (!mLayerRenderTargets.empty())
+    if (!mRenderTargets[levelIndex].empty())
         return angle::Result::Continue;
 
-    mLayerRenderTargets.resize(layerCount);
+    mRenderTargets[levelIndex].resize(layerCount);
 
     for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex)
     {
         const vk::ImageView *drawView;
-        ANGLE_TRY(getLayerLevelDrawImageView(contextVk, layerIndex, 0, &drawView));
-        mLayerRenderTargets[layerIndex].init(mImage, drawView, getNativeImageLevel(0),
-                                             getNativeImageLayer(layerIndex));
+        ANGLE_TRY(getLayerLevelDrawImageView(contextVk, layerIndex, levelIndex, &drawView));
+        mRenderTargets[levelIndex][layerIndex].init(
+            mImage, drawView, getNativeImageLevel(levelIndex), getNativeImageLayer(layerIndex));
     }
     return angle::Result::Continue;
 }
@@ -1692,13 +1693,6 @@
                                              layerCount));
     }
 
-    if (!format.imageFormat().isBlock)
-    {
-        ANGLE_TRY(mImage->initLayerImageView(contextVk, mState.getType(), aspectFlags,
-                                             gl::SwizzleState(), &mDrawImageView, baseLevel, 1,
-                                             baseLayer, layerCount));
-    }
-
     return angle::Result::Continue;
 }
 
@@ -1718,7 +1712,13 @@
 
     releaseImageViews(contextVk);
 
-    mLayerRenderTargets.clear();
+    for (vk::RenderTargetVector &renderTargetLevels : mRenderTargets)
+    {
+        // Clear the layers tracked for each level
+        renderTargetLevels.clear();
+    }
+    // Then clear the levels
+    mRenderTargets.clear();
 
     onStagingBufferChange();
 }
@@ -1728,7 +1728,6 @@
     contextVk->addGarbage(&mReadImageView);
     contextVk->addGarbage(&mFetchImageView);
     contextVk->addGarbage(&mStencilReadImageView);
-    contextVk->addGarbage(&mDrawImageView);
 
     for (vk::ImageViewVector &layerViews : mLayerLevelDrawImageViews)
     {
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.h b/src/libANGLE/renderer/vulkan/TextureVk.h
index dc48211..6802996 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.h
+++ b/src/libANGLE/renderer/vulkan/TextureVk.h
@@ -299,7 +299,7 @@
                                  const bool sized,
                                  uint32_t levelCount,
                                  uint32_t layerCount);
-    angle::Result initLayerRenderTargets(ContextVk *contextVk, GLuint layerCount);
+    angle::Result initRenderTargets(ContextVk *contextVk, GLuint layerCount, GLuint levelIndex);
     vk::ImageView *getLevelImageViewImpl(vk::ImageViewVector *imageViews, size_t level);
     vk::ImageView *getLayerLevelImageViewImpl(vk::LayerLevelImageViewVector *imageViews,
                                               size_t layer,
@@ -334,15 +334,16 @@
     vk::ImageView mStencilReadImageView;
 
     // Draw views.
-    vk::ImageView mDrawImageView;
     vk::LayerLevelImageViewVector mLayerLevelDrawImageViews;
 
     // Storage image views.
     vk::ImageViewVector mLevelStorageImageViews;
 
     vk::Sampler mSampler;
-    RenderTargetVk mRenderTarget;
-    std::vector<RenderTargetVk> mLayerRenderTargets;
+
+    // Render targets stored as vector of vectors
+    // Level is first dimension, layer is second
+    std::vector<vk::RenderTargetVector> mRenderTargets;
 
     // The serial is used for cache indexing.
     Serial mSerial;
diff --git a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
index 2edcfec..79269fc 100644
--- a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
@@ -124,6 +124,8 @@
     // Vulkan natively supports 32-bit indices, entry in kIndexTypeMap
     mNativeExtensions.elementIndexUint = true;
 
+    mNativeExtensions.fboRenderMipmap = true;
+
     // https://vulkan.lunarg.com/doc/view/1.0.30.0/linux/vkspec.chunked/ch31s02.html
     mNativeCaps.maxElementIndex       = std::numeric_limits<GLuint>::max() - 1;
     mNativeCaps.max3DTextureSize      = limitsVk.maxImageDimension3D;
diff --git a/src/libANGLE/renderer/vulkan/vk_utils.h b/src/libANGLE/renderer/vulkan/vk_utils.h
index b3e91f0..11e47d9 100644
--- a/src/libANGLE/renderer/vulkan/vk_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_utils.h
@@ -597,6 +597,8 @@
 using ImageViewVector = std::vector<ImageView>;
 // A vector of vector of image views.  Primary index is layer, secondary index is level.
 using LayerLevelImageViewVector = std::vector<ImageViewVector>;
+// A vector of rendertargets
+using RenderTargetVector = std::vector<RenderTargetVk>;
 
 }  // namespace vk
 
diff --git a/src/tests/gl_tests/ComputeShaderTest.cpp b/src/tests/gl_tests/ComputeShaderTest.cpp
index 87b8a8a..4716c80 100644
--- a/src/tests/gl_tests/ComputeShaderTest.cpp
+++ b/src/tests/gl_tests/ComputeShaderTest.cpp
@@ -3010,9 +3010,6 @@
     // TODO(xinghua.cao@intel.com): http://anglebug.com/3101
     ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL());
 
-    // Non-zero-level render target attachments are not yet supported.  http://anglebug.com/3184
-    ANGLE_SKIP_TEST_IF(IsVulkan());
-
     GLTexture texture[2];
     GLFramebuffer framebuffer;
     constexpr char kCS[] = R"(#version 310 es
diff --git a/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp b/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp
index b6aa154..930864b 100644
--- a/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp
+++ b/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp
@@ -163,4 +163,5 @@
                        ES3_OPENGL(),
                        ES2_OPENGLES(),
                        ES3_OPENGLES(),
-                       ES2_VULKAN());
+                       ES2_VULKAN(),
+                       ES3_VULKAN());
diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp
index e7485fb..2e5ca72 100644
--- a/src/tests/gl_tests/TextureTest.cpp
+++ b/src/tests/gl_tests/TextureTest.cpp
@@ -2017,8 +2017,8 @@
     // TODO(geofflang): Investigate on D3D11. http://anglebug.com/2291
     ANGLE_SKIP_TEST_IF(IsD3D11());
 
-    // TODO(cnorthrop): Framebuffer level support. http://anglebug.com/3184
-    ANGLE_SKIP_TEST_IF(IsVulkan());
+    // TODO(cnorthrop): Failing on Vulkan/Windows/AMD. http://anglebug.com/3996
+    ANGLE_SKIP_TEST_IF(IsVulkan() && IsWindows() && IsAMD());
 
     setUpProgram();