Vulkan: Make RenderTargetVk use ImageHelper.

Bug: angleproject:2318
Change-Id: I9bc4bb7f5bcd3029a31c8570809f253cf5e4b12a
Reviewed-on: https://chromium-review.googlesource.com/980773
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Luc Ferron <lucferron@chromium.org>
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/CommandGraph.cpp b/src/libANGLE/renderer/vulkan/CommandGraph.cpp
index 9643c1f..86b7eca 100644
--- a/src/libANGLE/renderer/vulkan/CommandGraph.cpp
+++ b/src/libANGLE/renderer/vulkan/CommandGraph.cpp
@@ -140,17 +140,14 @@
 
 void CommandGraphNode::appendColorRenderTarget(Serial serial, RenderTargetVk *colorRenderTarget)
 {
-    // TODO(jmadill): Layout transition?
-    mRenderPassDesc.packColorAttachment(*colorRenderTarget->format, colorRenderTarget->samples);
+    mRenderPassDesc.packColorAttachment(*colorRenderTarget->image);
     colorRenderTarget->resource->onWriteResource(this, serial);
 }
 
 void CommandGraphNode::appendDepthStencilRenderTarget(Serial serial,
                                                       RenderTargetVk *depthStencilRenderTarget)
 {
-    // TODO(jmadill): Layout transition?
-    mRenderPassDesc.packDepthStencilAttachment(*depthStencilRenderTarget->format,
-                                               depthStencilRenderTarget->samples);
+    mRenderPassDesc.packDepthStencilAttachment(*depthStencilRenderTarget->image);
     depthStencilRenderTarget->resource->onWriteResource(this, serial);
 }
 
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
index b32440c..2d9fdb4 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
@@ -34,7 +34,8 @@
 const gl::InternalFormat &GetReadAttachmentInfo(const gl::Context *context,
                                                 RenderTargetVk *renderTarget)
 {
-    GLenum implFormat = renderTarget->format->textureFormat().fboImplementationInternalFormat;
+    GLenum implFormat =
+        renderTarget->image->getFormat().textureFormat().fboImplementationInternalFormat;
     return gl::GetSizedInternalFormatInfo(implFormat);
 }
 }  // anonymous namespace
@@ -149,11 +150,11 @@
 
         RenderTargetVk *renderTarget = mRenderTargetCache.getDepthStencil();
         renderTarget->resource->onWriteResource(writingNode, currentSerial);
-        renderTarget->image->changeLayoutWithStages(
+        renderTarget->image->getImage().changeLayoutWithStages(
             aspectFlags, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
             VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
 
-        commandBuffer->clearSingleDepthStencilImage(*renderTarget->image, aspectFlags,
+        commandBuffer->clearSingleDepthStencilImage(renderTarget->image->getImage(), aspectFlags,
                                                     clearDepthStencilValue);
 
         if ((mask & GL_COLOR_BUFFER_BIT) == 0)
@@ -188,11 +189,11 @@
         ASSERT(colorRenderTarget);
         colorRenderTarget->resource->onWriteResource(writingNode, currentSerial);
 
-        colorRenderTarget->image->changeLayoutWithStages(
+        colorRenderTarget->image->getImage().changeLayoutWithStages(
             VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
             VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
 
-        commandBuffer->clearSingleColorImage(*colorRenderTarget->image,
+        commandBuffer->clearSingleColorImage(colorRenderTarget->image->getImage(),
                                              contextVk->getClearColorValue().color);
     }
 
@@ -260,10 +261,10 @@
     RenderTargetVk *renderTarget = mRenderTargetCache.getColorRead(mState);
     ASSERT(renderTarget);
 
-    vk::Image *readImage = renderTarget->image;
+    vk::Image &readImage = renderTarget->image->getImage();
     vk::ImageHelper stagingImage;
     ANGLE_TRY(stagingImage.init2DStaging(
-        device, renderer->getMemoryProperties(), *renderTarget->format,
+        device, renderer->getMemoryProperties(), renderTarget->image->getFormat(),
         gl::Extents(area.width, area.height, 1), vk::StagingUsage::Read));
 
     vk::CommandBuffer *commandBuffer = nullptr;
@@ -272,7 +273,7 @@
     stagingImage.getImage().changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL,
                                             commandBuffer);
 
-    readImage->changeLayoutWithStages(
+    readImage.changeLayoutWithStages(
         VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
         VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
 
@@ -295,7 +296,7 @@
     region.extent.height                 = area.height;
     region.extent.depth                  = 1;
 
-    commandBuffer->copyImage(*readImage, stagingImage.getImage(), 1, &region);
+    commandBuffer->copyImage(readImage, stagingImage.getImage(), 1, &region);
 
     // Triggers a full finish.
     // TODO(jmadill): Don't block on asynchronous readback.
@@ -306,7 +307,7 @@
     ANGLE_TRY(stagingImage.getDeviceMemory().map(device, 0, stagingImage.getAllocatedMemorySize(),
                                                  0, &mapPointer));
 
-    const angle::Format &angleFormat = renderTarget->format->textureFormat();
+    const angle::Format &angleFormat = renderTarget->image->getFormat().textureFormat();
 
     // TODO(jmadill): Use pixel bytes from the ANGLE format directly.
     const auto &glFormat = gl::GetSizedInternalFormatInfo(angleFormat.glInternalFormat);
@@ -384,14 +385,13 @@
     {
         RenderTargetVk *colorRenderTarget = colorRenderTargets[colorIndex];
         ASSERT(colorRenderTarget);
-        desc.packColorAttachment(*colorRenderTarget->format, colorRenderTarget->samples);
+        desc.packColorAttachment(*colorRenderTarget->image);
     }
 
     RenderTargetVk *depthStencilRenderTarget = mRenderTargetCache.getDepthStencil();
     if (depthStencilRenderTarget)
     {
-        desc.packDepthStencilAttachment(*depthStencilRenderTarget->format,
-                                        depthStencilRenderTarget->samples);
+        desc.packDepthStencilAttachment(*depthStencilRenderTarget->image);
     }
 
     mRenderPassDesc = desc;
@@ -431,8 +431,9 @@
         ASSERT(colorRenderTarget);
         attachments.push_back(colorRenderTarget->imageView->getHandle());
 
-        ASSERT(attachmentsSize.empty() || attachmentsSize == colorRenderTarget->extents);
-        attachmentsSize = colorRenderTarget->extents;
+        ASSERT(attachmentsSize.empty() ||
+               attachmentsSize == colorRenderTarget->image->getExtents());
+        attachmentsSize = colorRenderTarget->image->getExtents();
     }
 
     RenderTargetVk *depthStencilRenderTarget = mRenderTargetCache.getDepthStencil();
@@ -440,8 +441,9 @@
     {
         attachments.push_back(depthStencilRenderTarget->imageView->getHandle());
 
-        ASSERT(attachmentsSize.empty() || attachmentsSize == depthStencilRenderTarget->extents);
-        attachmentsSize = depthStencilRenderTarget->extents;
+        ASSERT(attachmentsSize.empty() ||
+               attachmentsSize == depthStencilRenderTarget->image->getExtents());
+        attachmentsSize = depthStencilRenderTarget->image->getExtents();
     }
 
     ASSERT(!attachments.empty());
@@ -547,7 +549,7 @@
         ASSERT(colorRenderTarget);
 
         // TODO(jmadill): Use automatic layout transition. http://anglebug.com/2361
-        colorRenderTarget->image->changeLayoutWithStages(
+        colorRenderTarget->image->getImage().changeLayoutWithStages(
             VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
             VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
             commandBuffer);
@@ -559,11 +561,11 @@
     if (depthStencilRenderTarget)
     {
         // TODO(jmadill): Use automatic layout transition. http://anglebug.com/2361
-        const angle::Format &format    = depthStencilRenderTarget->format->textureFormat();
+        const angle::Format &format = depthStencilRenderTarget->image->getFormat().textureFormat();
         VkImageAspectFlags aspectFlags = (format.depthBits > 0 ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) |
                                          (format.stencilBits > 0 ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
 
-        depthStencilRenderTarget->image->changeLayoutWithStages(
+        depthStencilRenderTarget->image->getImage().changeLayoutWithStages(
             aspectFlags, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
             VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
             commandBuffer);
diff --git a/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp b/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp
index 8098bff..55f26d3 100644
--- a/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp
@@ -11,20 +11,7 @@
 
 namespace rx
 {
-
-RenderTargetVk::RenderTargetVk()
+RenderTargetVk::RenderTargetVk() : image(nullptr), imageView(nullptr), resource(nullptr)
 {
-    reset();
 }
-
-void RenderTargetVk::reset()
-{
-    format    = nullptr;
-    image     = nullptr;
-    imageView = nullptr;
-    extents   = gl::Extents();
-    samples   = VK_SAMPLE_COUNT_1_BIT;
-    resource  = nullptr;
-}
-
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/RenderTargetVk.h b/src/libANGLE/renderer/vulkan/RenderTargetVk.h
index 355ae60..b5e0469 100644
--- a/src/libANGLE/renderer/vulkan/RenderTargetVk.h
+++ b/src/libANGLE/renderer/vulkan/RenderTargetVk.h
@@ -20,10 +20,9 @@
 
 namespace vk
 {
-struct Format;
-class Image;
+class ImageHelper;
 class ImageView;
-}
+}  // namespace vk
 
 // This is a very light-weight class that does not own to the resources it points to.
 // It's meant only to copy across some information from a FramebufferAttachment to the
@@ -33,13 +32,8 @@
   public:
     RenderTargetVk();
 
-    void reset();
-
-    const vk::Format *format;
-    vk::Image *image;
+    vk::ImageHelper *image;
     vk::ImageView *imageView;
-    gl::Extents extents;
-    VkSampleCountFlagBits samples;
     ResourceVk *resource;
 };
 
diff --git a/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp b/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
index 0429219..d5162b7 100644
--- a/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
@@ -25,7 +25,7 @@
 
 RenderbufferVk::RenderbufferVk(const gl::RenderbufferState &state) : RenderbufferImpl(state)
 {
-    mRenderTarget.image     = &mImage.getImage();
+    mRenderTarget.image     = &mImage;
     mRenderTarget.imageView = &mImageView;
     mRenderTarget.resource  = this;
 }
@@ -70,13 +70,6 @@
         }
     }
 
-    // Init RenderTarget.
-    mRenderTarget.extents.width  = static_cast<int>(width);
-    mRenderTarget.extents.height = static_cast<int>(height);
-    mRenderTarget.extents.depth  = 1;
-    mRenderTarget.format         = &vkFormat;
-    mRenderTarget.samples        = VK_SAMPLE_COUNT_1_BIT;  // TODO(jmadill): Multisample bits.
-
     if (!mImage.valid() && (width != 0 || height != 0))
     {
         const angle::Format &textureFormat = vkFormat.textureFormat();
@@ -87,7 +80,8 @@
             (textureFormat.redBits > 0 ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : 0) |
             (isDepthOrStencilFormat ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : 0);
 
-        ANGLE_TRY(mImage.init2D(device, mRenderTarget.extents, vkFormat, 1, usage));
+        gl::Extents extents(static_cast<int>(width), static_cast<int>(height), 1);
+        ANGLE_TRY(mImage.init2D(device, extents, vkFormat, 1, usage));
 
         VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
         ANGLE_TRY(mImage.initMemory(device, renderer->getMemoryProperties(), flags));
diff --git a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp
index 763adc2..fca87e2 100644
--- a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp
+++ b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp
@@ -35,7 +35,7 @@
     // TODO(jmadill): Properly select present mode and re-create display if changed.
     VkPresentModeKHR bestChoice = VK_PRESENT_MODE_MAILBOX_KHR;
 
-    for (auto presentMode : presentModes)
+    for (VkPresentModeKHR presentMode : presentModes)
     {
         if (presentMode == bestChoice)
         {
@@ -151,9 +151,17 @@
     return gl::NoError();
 }
 
-WindowSurfaceVk::SwapchainImage::SwapchainImage()                                        = default;
-WindowSurfaceVk::SwapchainImage::SwapchainImage(WindowSurfaceVk::SwapchainImage &&other) = default;
-WindowSurfaceVk::SwapchainImage::~SwapchainImage()                                       = default;
+WindowSurfaceVk::SwapchainImage::SwapchainImage()  = default;
+WindowSurfaceVk::SwapchainImage::~SwapchainImage() = default;
+
+WindowSurfaceVk::SwapchainImage::SwapchainImage(SwapchainImage &&other)
+    : image(std::move(other.image)),
+      imageView(std::move(other.imageView)),
+      framebuffer(std::move(other.framebuffer)),
+      imageAcquiredSemaphore(std::move(other.imageAcquiredSemaphore)),
+      commandsCompleteSemaphore(std::move(other.commandsCompleteSemaphore))
+{
+}
 
 WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState,
                                  EGLNativeWindowType window,
@@ -168,10 +176,7 @@
       mDepthStencilRenderTarget(),
       mCurrentSwapchainImageIndex(0)
 {
-    mColorRenderTarget.extents.width  = static_cast<GLint>(width);
-    mColorRenderTarget.extents.height = static_cast<GLint>(height);
-    mColorRenderTarget.extents.depth  = 1;
-    mColorRenderTarget.resource       = this;
+    mColorRenderTarget.resource = this;
 }
 
 WindowSurfaceVk::~WindowSurfaceVk()
@@ -192,15 +197,14 @@
 
     mAcquireNextImageSemaphore.destroy(device);
 
-    renderer->releaseResource(*this, &mDepthStencilImage);
-    renderer->releaseResource(*this, &mDepthStencilDeviceMemory);
-    renderer->releaseResource(*this, &mDepthStencilImageView);
+    mDepthStencilImage.release(renderer->getCurrentQueueSerial(), renderer);
+    mDepthStencilImageView.destroy(device);
 
-    for (auto &swapchainImage : mSwapchainImages)
+    for (SwapchainImage &swapchainImage : mSwapchainImages)
     {
         // Although we don't own the swapchain image handles, we need to keep our shutdown clean.
-        swapchainImage.image.reset();
-
+        swapchainImage.image.getImage().reset();
+        swapchainImage.image.destroy(device);
         swapchainImage.imageView.destroy(device);
         swapchainImage.framebuffer.destroy(device);
         swapchainImage.imageAcquiredSemaphore.destroy(device);
@@ -248,22 +252,24 @@
     ANGLE_VK_CHECK((surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0,
                    VK_ERROR_INITIALIZATION_FAILED);
 
+    EGLAttrib attribWidth  = mState.attributes.get(EGL_WIDTH, 0);
+    EGLAttrib attribHeight = mState.attributes.get(EGL_HEIGHT, 0);
+
     if (surfaceCaps.currentExtent.width == 0xFFFFFFFFu)
     {
         ASSERT(surfaceCaps.currentExtent.height == 0xFFFFFFFFu);
 
-        if (mColorRenderTarget.extents.width == 0)
+        if (attribWidth == 0)
         {
             width = windowSize.width;
         }
-        if (mColorRenderTarget.extents.height == 0)
+        if (attribHeight == 0)
         {
             height = windowSize.height;
         }
     }
 
-    mColorRenderTarget.extents.width  = static_cast<int>(width);
-    mColorRenderTarget.extents.height = static_cast<int>(height);
+    gl::Extents extents(static_cast<int>(width), static_cast<int>(height), 1);
 
     uint32_t presentModeCount = 0;
     ANGLE_VK_TRY(vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, mSurface,
@@ -306,8 +312,8 @@
     ANGLE_VK_TRY(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface, &surfaceFormatCount,
                                                       surfaceFormats.data()));
 
-    mColorRenderTarget.format = &renderer->getFormat(mState.config->renderTargetFormat);
-    VkFormat nativeFormat     = mColorRenderTarget.format->vkTextureFormat;
+    const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
+    VkFormat nativeFormat    = format.vkTextureFormat;
 
     if (surfaceFormatCount == 1u && surfaceFormats[0].format == VK_FORMAT_UNDEFINED)
     {
@@ -316,7 +322,7 @@
     else
     {
         bool foundFormat = false;
-        for (const auto &surfaceFormat : surfaceFormats)
+        for (const VkSurfaceFormatKHR &surfaceFormat : surfaceFormats)
         {
             if (surfaceFormat.format == nativeFormat)
             {
@@ -388,35 +394,16 @@
 
     for (uint32_t imageIndex = 0; imageIndex < imageCount; ++imageIndex)
     {
-        VkImage swapchainImage = swapchainImages[imageIndex];
-
-        VkImageViewCreateInfo imageViewInfo;
-        imageViewInfo.sType                           = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-        imageViewInfo.pNext                           = nullptr;
-        imageViewInfo.flags                           = 0;
-        imageViewInfo.image                           = swapchainImage;
-        imageViewInfo.viewType                        = VK_IMAGE_VIEW_TYPE_2D;
-        imageViewInfo.format                          = nativeFormat;
-        imageViewInfo.components.r                    = VK_COMPONENT_SWIZZLE_R;
-        imageViewInfo.components.g                    = VK_COMPONENT_SWIZZLE_G;
-        imageViewInfo.components.b                    = VK_COMPONENT_SWIZZLE_B;
-        imageViewInfo.components.a                    = VK_COMPONENT_SWIZZLE_A;
-        imageViewInfo.subresourceRange.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;
-        imageViewInfo.subresourceRange.baseMipLevel   = 0;
-        imageViewInfo.subresourceRange.levelCount     = 1;
-        imageViewInfo.subresourceRange.baseArrayLayer = 0;
-        imageViewInfo.subresourceRange.layerCount     = 1;
-
-        auto &member = mSwapchainImages[imageIndex];
-
-        member.image.setHandle(swapchainImage);
-        ANGLE_TRY(member.imageView.init(device, imageViewInfo));
+        SwapchainImage &member = mSwapchainImages[imageIndex];
+        member.image.init2DWeakReference(swapchainImages[imageIndex], extents, format, 1);
+        member.image.initImageView(device, VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
+                                   &member.imageView);
 
         // Set transfer dest layout, and clear the image to black.
-        member.image.changeLayoutWithStages(
+        member.image.getImage().changeLayoutWithStages(
             VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
             VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
-        commandBuffer->clearSingleColorImage(member.image, transparentBlack);
+        commandBuffer->clearSingleColorImage(member.image.getImage(), transparentBlack);
 
         ANGLE_TRY(member.imageAcquiredSemaphore.init(device));
         ANGLE_TRY(member.commandsCompleteSemaphore.init(device));
@@ -434,32 +421,9 @@
             (VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
              VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
 
-        VkImageCreateInfo imageInfo;
-        imageInfo.sType                 = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-        imageInfo.pNext                 = nullptr;
-        imageInfo.flags                 = 0;
-        imageInfo.imageType             = VK_IMAGE_TYPE_2D;
-        imageInfo.format                = dsFormat.vkTextureFormat;
-        imageInfo.extent.width          = static_cast<uint32_t>(width);
-        imageInfo.extent.height         = static_cast<uint32_t>(height);
-        imageInfo.extent.depth          = 1;
-        imageInfo.mipLevels             = 1;
-        imageInfo.arrayLayers           = 1;
-        imageInfo.samples               = VK_SAMPLE_COUNT_1_BIT;
-        imageInfo.tiling                = VK_IMAGE_TILING_OPTIMAL;
-        imageInfo.usage                 = usage;
-        imageInfo.sharingMode           = VK_SHARING_MODE_EXCLUSIVE;
-        imageInfo.queueFamilyIndexCount = 0;
-        imageInfo.pQueueFamilyIndices   = nullptr;
-        imageInfo.initialLayout         = VK_IMAGE_LAYOUT_UNDEFINED;
-
-        ANGLE_TRY(mDepthStencilImage.init(device, imageInfo));
-
-        // TODO(jmadill): Memory sub-allocation. http://anglebug.com/2162
-        size_t requiredSize;
-        ANGLE_TRY(vk::AllocateImageMemory(renderer, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
-                                          &mDepthStencilImage, &mDepthStencilDeviceMemory,
-                                          &requiredSize));
+        ANGLE_TRY(mDepthStencilImage.init2D(device, extents, dsFormat, 1, usage));
+        ANGLE_TRY(mDepthStencilImage.initMemory(device, renderer->getMemoryProperties(),
+                                                VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
 
         const VkImageAspectFlags aspect =
             (dsFormat.textureFormat().depthBits > 0 ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) |
@@ -468,38 +432,18 @@
         VkClearDepthStencilValue depthStencilClearValue = {1.0f, 0};
 
         // Set transfer dest layout, and clear the image.
-        mDepthStencilImage.changeLayoutWithStages(aspect, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                                                  VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
-                                                  VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
-        commandBuffer->clearSingleDepthStencilImage(mDepthStencilImage, aspect,
+        mDepthStencilImage.getImage().changeLayoutWithStages(
+            aspect, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
+            VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
+        commandBuffer->clearSingleDepthStencilImage(mDepthStencilImage.getImage(), aspect,
                                                     depthStencilClearValue);
 
-        // Depth/Stencil image views.
-        VkImageViewCreateInfo imageViewInfo;
-        imageViewInfo.sType                           = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-        imageViewInfo.pNext                           = nullptr;
-        imageViewInfo.flags                           = 0;
-        imageViewInfo.image                           = mDepthStencilImage.getHandle();
-        imageViewInfo.viewType                        = VK_IMAGE_VIEW_TYPE_2D;
-        imageViewInfo.format                          = dsFormat.vkTextureFormat;
-        imageViewInfo.components.r                    = VK_COMPONENT_SWIZZLE_R;
-        imageViewInfo.components.g                    = VK_COMPONENT_SWIZZLE_G;
-        imageViewInfo.components.b                    = VK_COMPONENT_SWIZZLE_B;
-        imageViewInfo.components.a                    = VK_COMPONENT_SWIZZLE_A;
-        imageViewInfo.subresourceRange.aspectMask     = aspect;
-        imageViewInfo.subresourceRange.baseMipLevel   = 0;
-        imageViewInfo.subresourceRange.levelCount     = 1;
-        imageViewInfo.subresourceRange.baseArrayLayer = 0;
-        imageViewInfo.subresourceRange.layerCount     = 1;
+        ANGLE_TRY(mDepthStencilImage.initImageView(device, aspect, gl::SwizzleState(),
+                                                   &mDepthStencilImageView));
 
-        ANGLE_TRY(mDepthStencilImageView.init(device, imageViewInfo));
-
-        mDepthStencilRenderTarget.extents.width  = static_cast<GLint>(width);
-        mDepthStencilRenderTarget.extents.height = static_cast<GLint>(height);
-        mDepthStencilRenderTarget.extents.depth  = 1;
-        mDepthStencilRenderTarget.resource       = this;
-        mDepthStencilRenderTarget.image          = &mDepthStencilImage;
-        mDepthStencilRenderTarget.format         = &dsFormat;
+        mDepthStencilRenderTarget.resource  = this;
+        mDepthStencilRenderTarget.image     = &mDepthStencilImage;
+        mDepthStencilRenderTarget.imageView = &mDepthStencilImageView;
 
         // TODO(jmadill): Figure out how to pass depth/stencil image views to the RenderTargetVk.
     }
@@ -520,11 +464,11 @@
     vk::CommandBuffer *swapCommands = nullptr;
     ANGLE_TRY(beginWriteResource(renderer, &swapCommands));
 
-    auto &image = mSwapchainImages[mCurrentSwapchainImageIndex];
+    SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
 
-    image.image.changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
-                                       VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
-                                       VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, swapCommands);
+    image.image.getImage().changeLayoutWithStages(
+        VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+        VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, swapCommands);
 
     ANGLE_TRY(
         renderer->flush(context, image.imageAcquiredSemaphore, image.commandsCompleteSemaphore));
@@ -555,7 +499,7 @@
                                        mAcquireNextImageSemaphore.getHandle(), VK_NULL_HANDLE,
                                        &mCurrentSwapchainImageIndex));
 
-    auto &image = mSwapchainImages[mCurrentSwapchainImageIndex];
+    SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
 
     // Swap the unused swapchain semaphore and the now active spare semaphore.
     std::swap(image.imageAcquiredSemaphore, mAcquireNextImageSemaphore);
@@ -607,12 +551,12 @@
 
 EGLint WindowSurfaceVk::getWidth() const
 {
-    return static_cast<EGLint>(mColorRenderTarget.extents.width);
+    return static_cast<EGLint>(mColorRenderTarget.image->getExtents().width);
 }
 
 EGLint WindowSurfaceVk::getHeight() const
 {
-    return static_cast<EGLint>(mColorRenderTarget.extents.height);
+    return static_cast<EGLint>(mColorRenderTarget.image->getExtents().height);
 }
 
 EGLint WindowSurfaceVk::isPostSubBufferSupported() const
@@ -649,7 +593,7 @@
     VkDevice device,
     const vk::RenderPass &compatibleRenderPass)
 {
-    auto &currentFramebuffer = mSwapchainImages[mCurrentSwapchainImageIndex].framebuffer;
+    vk::Framebuffer &currentFramebuffer = mSwapchainImages[mCurrentSwapchainImageIndex].framebuffer;
 
     if (currentFramebuffer.valid())
     {
@@ -659,19 +603,20 @@
 
     VkFramebufferCreateInfo framebufferInfo;
 
+    const gl::Extents &extents            = mColorRenderTarget.image->getExtents();
     std::array<VkImageView, 2> imageViews = {{VK_NULL_HANDLE, mDepthStencilImageView.getHandle()}};
 
     framebufferInfo.sType           = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
     framebufferInfo.pNext           = nullptr;
     framebufferInfo.flags           = 0;
     framebufferInfo.renderPass      = compatibleRenderPass.getHandle();
-    framebufferInfo.attachmentCount = (mDepthStencilImageView.valid() ? 2u : 1u);
+    framebufferInfo.attachmentCount = (mDepthStencilImage.valid() ? 2u : 1u);
     framebufferInfo.pAttachments    = imageViews.data();
-    framebufferInfo.width           = static_cast<uint32_t>(mColorRenderTarget.extents.width);
-    framebufferInfo.height          = static_cast<uint32_t>(mColorRenderTarget.extents.height);
+    framebufferInfo.width           = static_cast<uint32_t>(extents.width);
+    framebufferInfo.height          = static_cast<uint32_t>(extents.height);
     framebufferInfo.layers          = 1;
 
-    for (auto &swapchainImage : mSwapchainImages)
+    for (SwapchainImage &swapchainImage : mSwapchainImages)
     {
         imageViews[0] = swapchainImage.imageView.getHandle();
         ANGLE_TRY(swapchainImage.framebuffer.init(device, framebufferInfo));
diff --git a/src/libANGLE/renderer/vulkan/SurfaceVk.h b/src/libANGLE/renderer/vulkan/SurfaceVk.h
index 58a7822..0bb4699 100644
--- a/src/libANGLE/renderer/vulkan/SurfaceVk.h
+++ b/src/libANGLE/renderer/vulkan/SurfaceVk.h
@@ -128,13 +128,13 @@
     // problem with needing to know the next available image index before we acquire it.
     vk::Semaphore mAcquireNextImageSemaphore;
 
-    struct SwapchainImage
+    struct SwapchainImage : angle::NonCopyable
     {
         SwapchainImage();
         SwapchainImage(SwapchainImage &&other);
         ~SwapchainImage();
 
-        vk::Image image;
+        vk::ImageHelper image;
         vk::ImageView imageView;
         vk::Framebuffer framebuffer;
         vk::Semaphore imageAcquiredSemaphore;
@@ -143,8 +143,7 @@
 
     std::vector<SwapchainImage> mSwapchainImages;
 
-    vk::Image mDepthStencilImage;
-    vk::DeviceMemory mDepthStencilDeviceMemory;
+    vk::ImageHelper mDepthStencilImage;
     vk::ImageView mDepthStencilImageView;
 };
 
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
index 471a957..d8f5168 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
@@ -52,6 +52,9 @@
 
 TextureVk::TextureVk(const gl::TextureState &state) : TextureImpl(state)
 {
+    mRenderTarget.image     = &mImage;
+    mRenderTarget.imageView = &mImageView;
+    mRenderTarget.resource  = this;
 }
 
 TextureVk::~TextureVk()
@@ -105,8 +108,6 @@
         }
     }
 
-    mRenderTarget.reset();
-
     // Early-out on empty textures, don't create a zero-sized storage.
     if (size.width == 0 || size.height == 0 || size.depth == 0)
     {
@@ -173,13 +174,6 @@
         ANGLE_TRY(mSampler.init(device, samplerInfo));
     }
 
-    mRenderTarget.image     = &mImage.getImage();
-    mRenderTarget.imageView = &mImageView;
-    mRenderTarget.format    = &vkFormat;
-    mRenderTarget.extents   = size;
-    mRenderTarget.samples   = VK_SAMPLE_COUNT_1_BIT;
-    mRenderTarget.resource  = this;
-
     // Handle initial data.
     if (pixels)
     {
@@ -212,8 +206,8 @@
 {
     RendererVk *renderer       = contextVk->getRenderer();
     VkDevice device            = renderer->getDevice();
-    const gl::Extents &size    = mRenderTarget.extents;
-    const vk::Format &vkFormat = *mRenderTarget.format;
+    const gl::Extents &size    = mImage.getExtents();
+    const vk::Format &vkFormat = mImage.getFormat();
 
     vk::ImageHelper stagingImage;
     ANGLE_TRY(stagingImage.init2DStaging(device, renderer->getMemoryProperties(), vkFormat, size,
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
index e70aa77..2ef6faa 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
@@ -232,29 +232,30 @@
     memcpy(this, &other, sizeof(RenderPassDesc));
 }
 
-void RenderPassDesc::packAttachment(uint32_t index, const vk::Format &format, GLsizei samples)
+void RenderPassDesc::packAttachment(uint32_t index, const ImageHelper &imageHelper)
 {
     PackedAttachmentDesc &desc = mAttachmentDescs[index];
 
     // TODO(jmadill): We would only need this flag for duplicated attachments.
     desc.flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT;
-    ASSERT(desc.samples < std::numeric_limits<uint8_t>::max());
-    desc.samples = static_cast<uint8_t>(samples);
+    ASSERT(imageHelper.getSamples() < std::numeric_limits<uint8_t>::max());
+    desc.samples         = static_cast<uint8_t>(imageHelper.getSamples());
+    const Format &format = imageHelper.getFormat();
     ASSERT(format.vkTextureFormat < std::numeric_limits<uint16_t>::max());
     desc.format = static_cast<uint16_t>(format.vkTextureFormat);
 }
 
-void RenderPassDesc::packColorAttachment(const vk::Format &format, GLsizei samples)
+void RenderPassDesc::packColorAttachment(const ImageHelper &imageHelper)
 {
     ASSERT(mDepthStencilAttachmentCount == 0);
     ASSERT(mColorAttachmentCount < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS);
-    packAttachment(mColorAttachmentCount++, format, samples);
+    packAttachment(mColorAttachmentCount++, imageHelper);
 }
 
-void RenderPassDesc::packDepthStencilAttachment(const vk::Format &format, GLsizei samples)
+void RenderPassDesc::packDepthStencilAttachment(const ImageHelper &imageHelper)
 {
     ASSERT(mDepthStencilAttachmentCount == 0);
-    packAttachment(mColorAttachmentCount + mDepthStencilAttachmentCount++, format, samples);
+    packAttachment(mColorAttachmentCount + mDepthStencilAttachmentCount++, imageHelper);
 }
 
 RenderPassDesc &RenderPassDesc::operator=(const RenderPassDesc &other)
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.h b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
index 501cc47..1f8227a 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
@@ -54,8 +54,8 @@
     RenderPassDesc &operator=(const RenderPassDesc &other);
 
     // Depth stencil attachments must be packed after color attachments.
-    void packColorAttachment(const Format &format, GLsizei samples);
-    void packDepthStencilAttachment(const Format &format, GLsizei samples);
+    void packColorAttachment(const ImageHelper &imageHelper);
+    void packDepthStencilAttachment(const ImageHelper &imageHelper);
 
     size_t hash() const;
 
@@ -65,7 +65,7 @@
     const PackedAttachmentDesc &operator[](size_t index) const;
 
   private:
-    void packAttachment(uint32_t index, const vk::Format &format, GLsizei samples);
+    void packAttachment(uint32_t index, const ImageHelper &imageHelper);
 
     uint32_t mColorAttachmentCount;
     uint32_t mDepthStencilAttachmentCount;
diff --git a/src/libANGLE/renderer/vulkan/vk_utils.cpp b/src/libANGLE/renderer/vulkan/vk_utils.cpp
index d0f63dd..60a147f 100644
--- a/src/libANGLE/renderer/vulkan/vk_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_utils.cpp
@@ -1425,6 +1425,16 @@
 {
 }
 
+ImageHelper::ImageHelper(ImageHelper &&other)
+    : mImage(std::move(other.mImage)),
+      mDeviceMemory(std::move(other.mDeviceMemory)),
+      mExtents(other.mExtents),
+      mFormat(other.mFormat),
+      mSamples(other.mSamples),
+      mAllocatedMemorySize(other.mAllocatedMemorySize)
+{
+}
+
 ImageHelper::~ImageHelper()
 {
     ASSERT(!valid());
@@ -1480,6 +1490,7 @@
                               const MemoryProperties &memoryProperties,
                               VkMemoryPropertyFlags flags)
 {
+    // TODO(jmadill): Memory sub-allocation. http://anglebug.com/2162
     ANGLE_TRY(AllocateBufferOrImageMemory(device, memoryProperties, flags, &mImage, &mDeviceMemory,
                                           &mAllocatedMemorySize));
     return NoError();
@@ -1517,6 +1528,20 @@
     mDeviceMemory.destroy(device);
 }
 
+void ImageHelper::init2DWeakReference(VkImage handle,
+                                      const gl::Extents &extents,
+                                      const Format &format,
+                                      GLint samples)
+{
+    ASSERT(!valid());
+
+    mExtents = extents;
+    mFormat  = &format;
+    mSamples = samples;
+
+    mImage.setHandle(handle);
+}
+
 Error ImageHelper::init2DStaging(VkDevice device,
                                  const MemoryProperties &memoryProperties,
                                  const Format &format,
diff --git a/src/libANGLE/renderer/vulkan/vk_utils.h b/src/libANGLE/renderer/vulkan/vk_utils.h
index 224a161..26010fb 100644
--- a/src/libANGLE/renderer/vulkan/vk_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_utils.h
@@ -707,6 +707,7 @@
 {
   public:
     ImageHelper();
+    ImageHelper(ImageHelper &&other);
     ~ImageHelper();
 
     bool valid() const;
@@ -716,6 +717,10 @@
                  const Format &format,
                  GLint samples,
                  VkImageUsageFlags usage);
+    void init2DWeakReference(VkImage handle,
+                             const gl::Extents &extents,
+                             const Format &format,
+                             GLint samples);
     Error initMemory(VkDevice device,
                      const MemoryProperties &memoryProperties,
                      VkMemoryPropertyFlags flags);