Vulkan: Make Buffer/Image be CommandGraphResources.

Don't make TextureVk/RenderbufferVk/SurfaceVk/BufferVk own the
manipulation of the command graph. Instead put the operations close to
the buffers and images used to render.

This will lead towards implementing implicit barriers on the command
graph resources.

Bug: angleproject:2828
Change-Id: I07b742b6792c60285b280d6454f90e963d667e0e
Reviewed-on: https://chromium-review.googlesource.com/1246983
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/BufferVk.cpp b/src/libANGLE/renderer/vulkan/BufferVk.cpp
index 57aa917..5a4b5e5 100644
--- a/src/libANGLE/renderer/vulkan/BufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/BufferVk.cpp
@@ -18,8 +18,7 @@
 namespace rx
 {
 
-BufferVk::BufferVk(const gl::BufferState &state)
-    : BufferImpl(state), mAllocatedMemoryPropertyFlags(0)
+BufferVk::BufferVk(const gl::BufferState &state) : BufferImpl(state)
 {
 }
 
@@ -37,8 +36,7 @@
 
 void BufferVk::release(RendererVk *renderer)
 {
-    renderer->releaseObject(getStoredQueueSerial(), &mBuffer);
-    renderer->releaseObject(getStoredQueueSerial(), &mBufferMemory);
+    mBuffer.release(renderer);
 }
 
 gl::Error BufferVk::setData(const gl::Context *context,
@@ -54,12 +52,12 @@
         // Release and re-create the memory and buffer.
         release(contextVk->getRenderer());
 
+        // We could potentially use multiple backing buffers for different usages.
+        // For now keep a single buffer with all relevant usage flags.
         const VkImageUsageFlags usageFlags =
             (VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
              VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
 
-        // TODO(jmadill): Proper usage bit implementation. Likely will involve multiple backing
-        // buffers like in D3D11.
         VkBufferCreateInfo createInfo;
         createInfo.sType                 = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
         createInfo.pNext                 = nullptr;
@@ -70,14 +68,11 @@
         createInfo.queueFamilyIndexCount = 0;
         createInfo.pQueueFamilyIndices   = nullptr;
 
-        ANGLE_TRY(mBuffer.init(contextVk, createInfo));
-
         // Assume host vislble/coherent memory available.
         const VkMemoryPropertyFlags memoryPropertyFlags =
             (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
-        ANGLE_TRY(vk::AllocateBufferMemory(contextVk, memoryPropertyFlags,
-                                           &mAllocatedMemoryPropertyFlags, &mBuffer,
-                                           &mBufferMemory));
+
+        ANGLE_TRY(mBuffer.init(contextVk, createInfo, memoryPropertyFlags));
     }
 
     if (data && size > 0)
@@ -94,8 +89,7 @@
                                size_t size,
                                size_t offset)
 {
-    ASSERT(mBuffer.getHandle() != VK_NULL_HANDLE);
-    ASSERT(mBufferMemory.getHandle() != VK_NULL_HANDLE);
+    ASSERT(mBuffer.valid());
 
     ContextVk *contextVk = vk::GetImpl(context);
     ANGLE_TRY(setDataImpl(contextVk, static_cast<const uint8_t *>(data), size, offset));
@@ -115,8 +109,7 @@
 
 gl::Error BufferVk::map(const gl::Context *context, GLenum access, void **mapPtr)
 {
-    ASSERT(mBuffer.getHandle() != VK_NULL_HANDLE);
-    ASSERT(mBufferMemory.getHandle() != VK_NULL_HANDLE);
+    ASSERT(mBuffer.valid());
 
     ContextVk *contextVk = vk::GetImpl(context);
     ANGLE_TRY(mapImpl(contextVk, mapPtr));
@@ -125,8 +118,8 @@
 
 angle::Result BufferVk::mapImpl(ContextVk *contextVk, void **mapPtr)
 {
-    return mBufferMemory.map(contextVk, 0, mState.getSize(), 0,
-                             reinterpret_cast<uint8_t **>(mapPtr));
+    return mBuffer.getDeviceMemory().map(contextVk, 0, mState.getSize(), 0,
+                                         reinterpret_cast<uint8_t **>(mapPtr));
 }
 
 GLint64 BufferVk::getSize()
@@ -140,13 +133,12 @@
                              GLbitfield access,
                              void **mapPtr)
 {
-    ASSERT(mBuffer.getHandle() != VK_NULL_HANDLE);
-    ASSERT(mBufferMemory.getHandle() != VK_NULL_HANDLE);
+    ASSERT(mBuffer.valid());
 
     ContextVk *contextVk = vk::GetImpl(context);
 
-    ANGLE_TRY(
-        mBufferMemory.map(contextVk, offset, length, 0, reinterpret_cast<uint8_t **>(mapPtr)));
+    ANGLE_TRY(mBuffer.getDeviceMemory().map(contextVk, offset, length, 0,
+                                            reinterpret_cast<uint8_t **>(mapPtr)));
 
     return gl::NoError();
 }
@@ -158,10 +150,9 @@
 
 angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
 {
-    ASSERT(mBuffer.getHandle() != VK_NULL_HANDLE);
-    ASSERT(mBufferMemory.getHandle() != VK_NULL_HANDLE);
+    ASSERT(mBuffer.valid());
 
-    mBufferMemory.unmap(contextVk->getDevice());
+    mBuffer.getDeviceMemory().unmap(contextVk->getDevice());
 
     return angle::Result::Continue();
 }
@@ -184,11 +175,12 @@
     const gl::Type &typeInfo = gl::GetTypeInfo(type);
 
     uint8_t *mapPointer = nullptr;
-    ANGLE_TRY(mBufferMemory.map(contextVk, offset, typeInfo.bytes * count, 0, &mapPointer));
+    ANGLE_TRY(
+        mBuffer.getDeviceMemory().map(contextVk, offset, typeInfo.bytes * count, 0, &mapPointer));
 
     *outRange = gl::ComputeIndexRange(type, mapPointer, count, primitiveRestartEnabled);
 
-    mBufferMemory.unmap(contextVk->getDevice());
+    mBuffer.getDeviceMemory().unmap(contextVk->getDevice());
     return gl::NoError();
 }
 
@@ -201,7 +193,7 @@
     VkDevice device      = contextVk->getDevice();
 
     // Use map when available.
-    if (isResourceInUse(renderer))
+    if (mBuffer.isResourceInUse(renderer))
     {
         vk::StagingBuffer stagingBuffer;
         ANGLE_TRY(stagingBuffer.init(contextVk, static_cast<VkDeviceSize>(size),
@@ -218,7 +210,7 @@
         // 'beginWriteResource' will stop any subsequent rendering from using the old buffer data,
         // by marking any current read operations / command buffers as 'finished'.
         vk::CommandBuffer *commandBuffer = nullptr;
-        ANGLE_TRY(recordCommands(contextVk, &commandBuffer));
+        ANGLE_TRY(mBuffer.recordCommands(contextVk, &commandBuffer));
 
         // Insert a barrier to ensure reads from the buffer are complete.
         // TODO(jmadill): Insert minimal barriers.
@@ -229,7 +221,7 @@
         bufferBarrier.dstAccessMask       = VK_ACCESS_TRANSFER_WRITE_BIT;
         bufferBarrier.srcQueueFamilyIndex = 0;
         bufferBarrier.dstQueueFamilyIndex = 0;
-        bufferBarrier.buffer              = mBuffer.getHandle();
+        bufferBarrier.buffer              = mBuffer.getBuffer().getHandle();
         bufferBarrier.offset              = offset;
         bufferBarrier.size                = static_cast<VkDeviceSize>(size);
 
@@ -237,7 +229,7 @@
                                            VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, bufferBarrier);
 
         VkBufferCopy copyRegion = {0, offset, size};
-        commandBuffer->copyBuffer(stagingBuffer.getBuffer(), mBuffer, 1, &copyRegion);
+        commandBuffer->copyBuffer(stagingBuffer.getBuffer(), mBuffer.getBuffer(), 1, &copyRegion);
 
         // Insert a barrier to ensure copy has done.
         // TODO(jie.a.chen@intel.com): Insert minimal barriers.
@@ -253,44 +245,38 @@
 
         bufferBarrier.srcQueueFamilyIndex = 0;
         bufferBarrier.dstQueueFamilyIndex = 0;
-        bufferBarrier.buffer              = mBuffer.getHandle();
+        bufferBarrier.buffer              = mBuffer.getBuffer().getHandle();
         bufferBarrier.offset              = offset;
         bufferBarrier.size                = static_cast<VkDeviceSize>(size);
 
         commandBuffer->singleBufferBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
                                            VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, bufferBarrier);
 
-        // Immediately release staging buffer.
-        // TODO(jmadill): Staging buffer re-use.
-        renderer->releaseObject(getStoredQueueSerial(), &stagingBuffer);
+        // Immediately release staging buffer. We should probably be using a DynamicBuffer here.
+        renderer->releaseObject(renderer->getCurrentQueueSerial(), &stagingBuffer);
     }
     else
     {
         uint8_t *mapPointer = nullptr;
-        ANGLE_TRY(mBufferMemory.map(contextVk, offset, size, 0, &mapPointer));
+        ANGLE_TRY(mBuffer.getDeviceMemory().map(contextVk, offset, size, 0, &mapPointer));
         ASSERT(mapPointer);
 
         memcpy(mapPointer, data, size);
 
-        mBufferMemory.unmap(device);
+        mBuffer.getDeviceMemory().unmap(device);
     }
 
     return angle::Result::Continue();
 }
 
-const vk::Buffer &BufferVk::getVkBuffer() const
-{
-    return mBuffer;
-}
-
 angle::Result BufferVk::copyToBuffer(ContextVk *contextVk,
                                      VkBuffer destbuffer,
                                      uint32_t copyCount,
                                      const VkBufferCopy *copies)
 {
     vk::CommandBuffer *commandBuffer;
-    ANGLE_TRY(recordCommands(contextVk, &commandBuffer));
-    commandBuffer->copyBuffer(mBuffer.getHandle(), destbuffer, copyCount, copies);
+    ANGLE_TRY(mBuffer.recordCommands(contextVk, &commandBuffer));
+    commandBuffer->copyBuffer(mBuffer.getBuffer().getHandle(), destbuffer, copyCount, copies);
     return angle::Result::Continue();
 }
 
diff --git a/src/libANGLE/renderer/vulkan/BufferVk.h b/src/libANGLE/renderer/vulkan/BufferVk.h
index 2d61150..b6c0288 100644
--- a/src/libANGLE/renderer/vulkan/BufferVk.h
+++ b/src/libANGLE/renderer/vulkan/BufferVk.h
@@ -18,7 +18,7 @@
 {
 class RendererVk;
 
-class BufferVk : public BufferImpl, public vk::CommandGraphResource
+class BufferVk : public BufferImpl
 {
   public:
     BufferVk(const gl::BufferState &state);
@@ -56,7 +56,17 @@
                             gl::IndexRange *outRange) override;
     GLint64 getSize();
 
-    const vk::Buffer &getVkBuffer() const;
+    const vk::BufferHelper &getBuffer() const
+    {
+        ASSERT(mBuffer.valid());
+        return mBuffer;
+    }
+
+    vk::BufferHelper &getBuffer()
+    {
+        ASSERT(mBuffer.valid());
+        return mBuffer;
+    }
 
     angle::Result mapImpl(ContextVk *contextVk, void **mapPtr);
     angle::Result unmapImpl(ContextVk *contextVk);
@@ -74,9 +84,7 @@
                               size_t offset);
     void release(RendererVk *renderer);
 
-    vk::Buffer mBuffer;
-    vk::DeviceMemory mBufferMemory;
-    VkMemoryPropertyFlags mAllocatedMemoryPropertyFlags;
+    vk::BufferHelper mBuffer;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/CommandGraph.h b/src/libANGLE/renderer/vulkan/CommandGraph.h
index 2ac17ef..447ddd1 100644
--- a/src/libANGLE/renderer/vulkan/CommandGraph.h
+++ b/src/libANGLE/renderer/vulkan/CommandGraph.h
@@ -122,7 +122,6 @@
     // Sets up dependency relations. 'this' resource is the resource being read.
     void addReadDependency(CommandGraphResource *readingResource);
 
-  protected:
     // Allocates a write node via getNewWriteNode and returns a started command buffer.
     // The started command buffer will render outside of a RenderPass.
     // Will append to an existing command buffer/graph node if possible.
@@ -147,6 +146,7 @@
     // Called when 'this' object changes, but we'd like to start a new command buffer later.
     void finishCurrentCommands(RendererVk *renderer);
 
+  protected:
     // Get the current queue serial for this resource. Only used to release resources.
     Serial getStoredQueueSerial() const;
 
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index 6299e85..23b68ce 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -397,7 +397,7 @@
         // Ensure any writes to the textures are flushed before we read from them.
         TextureVk *textureVk = mActiveTextures[textureIndex];
         ANGLE_TRY(textureVk->ensureImageInitialized(this));
-        textureVk->addReadDependency(mDrawFramebuffer);
+        textureVk->getImage().addReadDependency(mDrawFramebuffer);
     }
 
     if (mProgram->hasTextures())
diff --git a/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp b/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp
index 18dd86d..af826c4 100644
--- a/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp
@@ -15,11 +15,8 @@
 
 namespace rx
 {
-RenderTargetVk::RenderTargetVk(vk::ImageHelper *image,
-                               vk::ImageView *imageView,
-                               vk::CommandGraphResource *resource,
-                               size_t layerIndex)
-    : mImage(image), mImageView(imageView), mResource(resource), mLayerIndex(layerIndex)
+RenderTargetVk::RenderTargetVk(vk::ImageHelper *image, vk::ImageView *imageView, size_t layerIndex)
+    : mImage(image), mImageView(imageView), mLayerIndex(layerIndex)
 {
 }
 
@@ -30,7 +27,6 @@
 RenderTargetVk::RenderTargetVk(RenderTargetVk &&other)
     : mImage(other.mImage),
       mImageView(other.mImageView),
-      mResource(other.mResource),
       mLayerIndex(other.mLayerIndex)
 {
 }
@@ -52,7 +48,7 @@
                                    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, commandBuffer);
 
     // Set up dependencies between the RT resource and the Framebuffer.
-    mResource->addWriteDependency(framebufferVk);
+    mImage->addWriteDependency(framebufferVk);
 }
 
 void RenderTargetVk::onDepthStencilDraw(vk::CommandGraphResource *framebufferVk,
@@ -74,7 +70,7 @@
                                    VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, commandBuffer);
 
     // Set up dependencies between the RT resource and the Framebuffer.
-    mResource->addWriteDependency(framebufferVk);
+    mImage->addWriteDependency(framebufferVk);
 }
 
 const vk::ImageHelper &RenderTargetVk::getImage() const
@@ -89,11 +85,6 @@
     return mImageView;
 }
 
-vk::CommandGraphResource *RenderTargetVk::getResource() const
-{
-    return mResource;
-}
-
 const vk::Format &RenderTargetVk::getImageFormat() const
 {
     ASSERT(mImage && mImage->valid());
@@ -120,7 +111,7 @@
     ASSERT(mImage && mImage->valid());
 
     // TODO(jmadill): Better simultaneous resource access. http://anglebug.com/2679
-    mResource->addWriteDependency(readingResource);
+    mImage->addWriteDependency(readingResource);
 
     mImage->changeLayoutWithStages(mImage->getAspectFlags(), layout,
                                    VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
@@ -132,7 +123,7 @@
 vk::ImageHelper *RenderTargetVk::getImageForWrite(vk::CommandGraphResource *writingResource) const
 {
     ASSERT(mImage && mImage->valid());
-    mResource->addWriteDependency(writingResource);
+    mImage->addWriteDependency(writingResource);
     return mImage;
 }
 
diff --git a/src/libANGLE/renderer/vulkan/RenderTargetVk.h b/src/libANGLE/renderer/vulkan/RenderTargetVk.h
index 4eacad5..6106f5f 100644
--- a/src/libANGLE/renderer/vulkan/RenderTargetVk.h
+++ b/src/libANGLE/renderer/vulkan/RenderTargetVk.h
@@ -35,7 +35,6 @@
   public:
     RenderTargetVk(vk::ImageHelper *image,
                    vk::ImageView *imageView,
-                   vk::CommandGraphResource *resource,
                    size_t layerIndex);
     ~RenderTargetVk();
 
@@ -58,7 +57,6 @@
                                      vk::CommandBuffer *commandBuffer);
     vk::ImageHelper *getImageForWrite(vk::CommandGraphResource *writingResource) const;
     vk::ImageView *getImageView() const;
-    vk::CommandGraphResource *getResource() const;
 
     const vk::Format &getImageFormat() const;
     const gl::Extents &getImageExtents() const;
@@ -71,7 +69,6 @@
   private:
     vk::ImageHelper *mImage;
     vk::ImageView *mImageView;
-    vk::CommandGraphResource *mResource;
     size_t mLayerIndex;
 };
 
diff --git a/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp b/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
index 74bd4b8..40b529e 100644
--- a/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp
@@ -24,7 +24,7 @@
 }  // anonymous namespace
 
 RenderbufferVk::RenderbufferVk(const gl::RenderbufferState &state)
-    : RenderbufferImpl(state), mRenderTarget(&mImage, &mImageView, this, 0)
+    : RenderbufferImpl(state), mRenderTarget(&mImage, &mImageView, 0)
 {
 }
 
@@ -37,8 +37,8 @@
     ContextVk *contextVk = vk::GetImpl(context);
     RendererVk *renderer = contextVk->getRenderer();
 
-    mImage.release(renderer->getCurrentQueueSerial(), renderer);
-    renderer->releaseObject(getStoredQueueSerial(), &mImageView);
+    mImage.release(renderer);
+    renderer->releaseObject(renderer->getCurrentQueueSerial(), &mImageView);
 
     return gl::NoError();
 }
@@ -59,8 +59,8 @@
             static_cast<GLsizei>(width) != mState.getWidth() ||
             static_cast<GLsizei>(height) != mState.getHeight())
         {
-            mImage.release(renderer->getCurrentQueueSerial(), renderer);
-            renderer->releaseObject(getStoredQueueSerial(), &mImageView);
+            mImage.release(renderer);
+            renderer->releaseObject(renderer->getCurrentQueueSerial(), &mImageView);
         }
     }
 
@@ -87,7 +87,7 @@
 
         // TODO(jmadill): Fold this into the RenderPass load/store ops. http://anglebug.com/2361
         vk::CommandBuffer *commandBuffer = nullptr;
-        ANGLE_TRY(recordCommands(contextVk, &commandBuffer));
+        ANGLE_TRY(mImage.recordCommands(contextVk, &commandBuffer));
 
         if (isDepthOrStencilFormat)
         {
diff --git a/src/libANGLE/renderer/vulkan/RenderbufferVk.h b/src/libANGLE/renderer/vulkan/RenderbufferVk.h
index a6453c9..22bda02 100644
--- a/src/libANGLE/renderer/vulkan/RenderbufferVk.h
+++ b/src/libANGLE/renderer/vulkan/RenderbufferVk.h
@@ -17,7 +17,7 @@
 namespace rx
 {
 
-class RenderbufferVk : public RenderbufferImpl, public vk::CommandGraphResource
+class RenderbufferVk : public RenderbufferImpl
 {
   public:
     RenderbufferVk(const gl::RenderbufferState &state);
diff --git a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp
index bd7a94b..5f52202 100644
--- a/src/libANGLE/renderer/vulkan/SurfaceVk.cpp
+++ b/src/libANGLE/renderer/vulkan/SurfaceVk.cpp
@@ -58,8 +58,7 @@
 
 }  // namespace
 
-OffscreenSurfaceVk::AttachmentImage::AttachmentImage(vk::CommandGraphResource *commandGraphResource)
-    : renderTarget(&image, &imageView, commandGraphResource, 0)
+OffscreenSurfaceVk::AttachmentImage::AttachmentImage() : renderTarget(&image, &imageView, 0)
 {
 }
 
@@ -91,24 +90,19 @@
     return angle::Result::Continue();
 }
 
-void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display,
-                                                  Serial storedQueueSerial)
+void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display)
 {
     const DisplayVk *displayVk = vk::GetImpl(display);
     RendererVk *renderer       = displayVk->getRenderer();
 
-    image.release(renderer->getCurrentQueueSerial(), renderer);
-    renderer->releaseObject(storedQueueSerial, &imageView);
+    image.release(renderer);
+    renderer->releaseObject(renderer->getCurrentQueueSerial(), &imageView);
 }
 
 OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState,
                                        EGLint width,
                                        EGLint height)
-    : SurfaceImpl(surfaceState),
-      mWidth(width),
-      mHeight(height),
-      mColorAttachment(this),
-      mDepthStencilAttachment(this)
+    : SurfaceImpl(surfaceState), mWidth(width), mHeight(height)
 {
 }
 
@@ -145,8 +139,8 @@
 
 void OffscreenSurfaceVk::destroy(const egl::Display *display)
 {
-    mColorAttachment.destroy(display, getStoredQueueSerial());
-    mDepthStencilAttachment.destroy(display, getStoredQueueSerial());
+    mColorAttachment.destroy(display);
+    mDepthStencilAttachment.destroy(display);
 }
 
 FramebufferImpl *OffscreenSurfaceVk::createDefaultFramebuffer(const gl::Context *context,
@@ -268,8 +262,8 @@
       mSurface(VK_NULL_HANDLE),
       mInstance(VK_NULL_HANDLE),
       mSwapchain(VK_NULL_HANDLE),
-      mColorRenderTarget(nullptr, nullptr, this, 0),
-      mDepthStencilRenderTarget(&mDepthStencilImage, &mDepthStencilImageView, this, 0),
+      mColorRenderTarget(nullptr, nullptr, 0),
+      mDepthStencilRenderTarget(&mDepthStencilImage, &mDepthStencilImageView, 0),
       mCurrentSwapchainImageIndex(0)
 {
 }
@@ -292,7 +286,7 @@
 
     mAcquireNextImageSemaphore.destroy(device);
 
-    mDepthStencilImage.release(renderer->getCurrentQueueSerial(), renderer);
+    mDepthStencilImage.release(renderer);
     mDepthStencilImageView.destroy(device);
 
     for (SwapchainImage &swapchainImage : mSwapchainImages)
@@ -478,10 +472,6 @@
     ANGLE_VK_TRY(displayVk,
                  vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, swapchainImages.data()));
 
-    // Allocate a command buffer for clearing our images to black.
-    vk::CommandBuffer *commandBuffer = nullptr;
-    ANGLE_TRY(recordCommands(displayVk, &commandBuffer));
-
     VkClearColorValue transparentBlack;
     transparentBlack.float32[0] = 0.0f;
     transparentBlack.float32[1] = 0.0f;
@@ -500,6 +490,10 @@
                                              VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
                                              &member.imageView, 1));
 
+        // Allocate a command buffer for clearing our images to black.
+        vk::CommandBuffer *commandBuffer = nullptr;
+        ANGLE_TRY(member.image.recordCommands(displayVk, &commandBuffer));
+
         // Set transfer dest layout, and clear the image to black.
         member.image.clearColor(transparentBlack, 0, 1, commandBuffer);
 
@@ -525,13 +519,15 @@
         const VkImageAspectFlags aspect = vk::GetDepthStencilAspectFlags(dsFormat.textureFormat());
         VkClearDepthStencilValue depthStencilClearValue = {1.0f, 0};
 
-        // Set transfer dest layout, and clear the image.
+        // Clear the image.
+        vk::CommandBuffer *commandBuffer = nullptr;
+        ANGLE_TRY(mDepthStencilImage.recordCommands(displayVk, &commandBuffer));
         mDepthStencilImage.clearDepthStencil(aspect, depthStencilClearValue, commandBuffer);
 
         ANGLE_TRY(mDepthStencilImage.initImageView(displayVk, gl::TextureType::_2D, aspect,
                                                    gl::SwizzleState(), &mDepthStencilImageView, 1));
 
-        // TODO(jmadill): Figure out how to pass depth/stencil image views to the RenderTargetVk.
+        // We will need to pass depth/stencil image views to the RenderTargetVk in the future.
     }
 
     return angle::Result::Continue();
@@ -565,7 +561,8 @@
     RendererVk *renderer = displayVk->getRenderer();
 
     vk::CommandBuffer *swapCommands = nullptr;
-    ANGLE_TRY(recordCommands(displayVk, &swapCommands));
+    ANGLE_TRY(mSwapchainImages[mCurrentSwapchainImageIndex].image.recordCommands(displayVk,
+                                                                                 &swapCommands));
 
     SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
 
diff --git a/src/libANGLE/renderer/vulkan/SurfaceVk.h b/src/libANGLE/renderer/vulkan/SurfaceVk.h
index abdf07e..73a3cfc 100644
--- a/src/libANGLE/renderer/vulkan/SurfaceVk.h
+++ b/src/libANGLE/renderer/vulkan/SurfaceVk.h
@@ -20,7 +20,7 @@
 {
 class RendererVk;
 
-class OffscreenSurfaceVk : public SurfaceImpl, public vk::CommandGraphResource
+class OffscreenSurfaceVk : public SurfaceImpl
 {
   public:
     OffscreenSurfaceVk(const egl::SurfaceState &surfaceState, EGLint width, EGLint height);
@@ -63,14 +63,14 @@
   private:
     struct AttachmentImage final : angle::NonCopyable
     {
-        AttachmentImage(vk::CommandGraphResource *commandGraphResource);
+        AttachmentImage();
         ~AttachmentImage();
 
         angle::Result initialize(DisplayVk *displayVk,
                                  EGLint width,
                                  EGLint height,
                                  const vk::Format &vkFormat);
-        void destroy(const egl::Display *display, Serial storedQueueSerial);
+        void destroy(const egl::Display *display);
 
         vk::ImageHelper image;
         vk::ImageView imageView;
@@ -86,7 +86,7 @@
     AttachmentImage mDepthStencilAttachment;
 };
 
-class WindowSurfaceVk : public SurfaceImpl, public vk::CommandGraphResource
+class WindowSurfaceVk : public SurfaceImpl
 {
   public:
     WindowSurfaceVk(const egl::SurfaceState &surfaceState,
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
index 696028b..a4446b3 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
@@ -429,15 +429,11 @@
 
 // TextureVk implementation.
 TextureVk::TextureVk(const gl::TextureState &state, RendererVk *renderer)
-    : TextureImpl(state),
-      mRenderTarget(&mImage, &mBaseLevelImageView, this, 0),
-      mPixelBuffer(renderer)
+    : TextureImpl(state), mRenderTarget(&mImage, &mBaseLevelImageView, 0), mPixelBuffer(renderer)
 {
 }
 
-TextureVk::~TextureVk()
-{
-}
+TextureVk::~TextureVk() = default;
 
 gl::Error TextureVk::onDestroy(const gl::Context *context)
 {
@@ -445,7 +441,7 @@
     RendererVk *renderer = contextVk->getRenderer();
 
     releaseImage(context, renderer);
-    renderer->releaseObject(getStoredQueueSerial(), &mSampler);
+    renderer->releaseObject(renderer->getCurrentQueueSerial(), &mSampler);
 
     mPixelBuffer.release(renderer);
     return gl::NoError();
@@ -475,7 +471,7 @@
     }
 
     // Create a new graph node to store image initialization commands.
-    finishCurrentCommands(renderer);
+    mImage.finishCurrentCommands(renderer);
 
     // Handle initial data.
     if (pixels)
@@ -503,7 +499,7 @@
         gl::Offset(area.x, area.y, area.z), formatInfo, unpack, type, pixels));
 
     // Create a new graph node to store image initialization commands.
-    finishCurrentCommands(contextVk->getRenderer());
+    mImage.finishCurrentCommands(contextVk->getRenderer());
 
     return gl::NoError();
 }
@@ -629,8 +625,8 @@
         gl::Extents(clippedSourceArea.width, clippedSourceArea.height, 1), internalFormat,
         framebufferVk));
 
-    finishCurrentCommands(renderer);
-    framebufferVk->addReadDependency(this);
+    mImage.finishCurrentCommands(renderer);
+    framebufferVk->addReadDependency(&mImage);
     return angle::Result::Continue();
 }
 
@@ -679,7 +675,7 @@
                       unpackUnmultiplyAlpha);
 
     // Create a new graph node to store image initialization commands.
-    finishCurrentCommands(contextVk->getRenderer());
+    mImage.finishCurrentCommands(contextVk->getRenderer());
 
     return angle::Result::Continue();
 }
@@ -687,7 +683,7 @@
 angle::Result TextureVk::getCommandBufferForWrite(ContextVk *contextVk,
                                                   vk::CommandBuffer **commandBufferOut)
 {
-    ANGLE_TRY(recordCommands(contextVk, commandBufferOut));
+    ANGLE_TRY(mImage.recordCommands(contextVk, commandBufferOut));
     return angle::Result::Continue();
 }
 
@@ -997,7 +993,7 @@
     }
 
     // We're changing this textureVk content, make sure we let the graph know.
-    finishCurrentCommands(renderer);
+    mImage.finishCurrentCommands(renderer);
 
     return gl::NoError();
 }
@@ -1086,7 +1082,7 @@
         ANGLE_TRY(mImage.initLayerImageView(contextVk, gl::TextureType::CubeMap,
                                             VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
                                             &imageView, 1, cubeMapFaceIndex, 1));
-        mCubeMapRenderTargets.emplace_back(&mImage, &imageView, this, cubeMapFaceIndex);
+        mCubeMapRenderTargets.emplace_back(&mImage, &imageView, cubeMapFaceIndex);
     }
     return angle::Result::Continue();
 }
@@ -1102,7 +1098,7 @@
     if (mSampler.valid())
     {
         RendererVk *renderer = contextVk->getRenderer();
-        renderer->releaseObject(getStoredQueueSerial(), &mSampler);
+        renderer->releaseObject(renderer->getCurrentQueueSerial(), &mSampler);
     }
 
     const gl::SamplerState &samplerState = mState.getSamplerState();
@@ -1149,12 +1145,6 @@
     return gl::NoError();
 }
 
-const vk::ImageHelper &TextureVk::getImage() const
-{
-    ASSERT(mImage.valid());
-    return mImage;
-}
-
 const vk::ImageView &TextureVk::getImageView() const
 {
     ASSERT(mImage.valid());
@@ -1213,13 +1203,16 @@
 
 void TextureVk::releaseImage(const gl::Context *context, RendererVk *renderer)
 {
-    mImage.release(renderer->getCurrentQueueSerial(), renderer);
-    renderer->releaseObject(getStoredQueueSerial(), &mBaseLevelImageView);
-    renderer->releaseObject(getStoredQueueSerial(), &mMipmapImageView);
+    mImage.release(renderer);
+
+    Serial currentSerial = renderer->getCurrentQueueSerial();
+
+    renderer->releaseObject(currentSerial, &mBaseLevelImageView);
+    renderer->releaseObject(currentSerial, &mMipmapImageView);
 
     for (vk::ImageView &imageView : mCubeMapFaceImageViews)
     {
-        renderer->releaseObject(getStoredQueueSerial(), &imageView);
+        renderer->releaseObject(currentSerial, &imageView);
     }
     mCubeMapFaceImageViews.clear();
     mCubeMapRenderTargets.clear();
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.h b/src/libANGLE/renderer/vulkan/TextureVk.h
index 499437e..fbfcf49 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.h
+++ b/src/libANGLE/renderer/vulkan/TextureVk.h
@@ -83,7 +83,7 @@
     std::vector<SubresourceUpdate> mSubresourceUpdates;
 };
 
-class TextureVk : public TextureImpl, public vk::CommandGraphResource
+class TextureVk : public TextureImpl
 {
   public:
     TextureVk(const gl::TextureState &state, RendererVk *renderer);
@@ -192,7 +192,18 @@
     gl::Error initializeContents(const gl::Context *context,
                                  const gl::ImageIndex &imageIndex) override;
 
-    const vk::ImageHelper &getImage() const;
+    const vk::ImageHelper &getImage() const
+    {
+        ASSERT(mImage.valid());
+        return mImage;
+    }
+
+    vk::ImageHelper &getImage()
+    {
+        ASSERT(mImage.valid());
+        return mImage;
+    }
+
     const vk::ImageView &getImageView() const;
     const vk::Sampler &getSampler() const;
 
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index 26f088f..90d9ff2 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -251,8 +251,9 @@
                 if (bufferGL)
                 {
                     BufferVk *bufferVk                 = vk::GetImpl(bufferGL);
-                    mCurrentElementArrayBufferResource = bufferVk;
-                    mCurrentElementArrayBufferHandle   = bufferVk->getVkBuffer().getHandle();
+                    mCurrentElementArrayBufferResource = &bufferVk->getBuffer();
+                    mCurrentElementArrayBufferHandle =
+                        bufferVk->getBuffer().getBuffer().getHandle();
                 }
                 else
                 {
@@ -327,8 +328,9 @@
             }
             else
             {
-                mCurrentArrayBufferResources[attribIndex] = bufferVk;
-                mCurrentArrayBufferHandles[attribIndex]   = bufferVk->getVkBuffer().getHandle();
+                mCurrentArrayBufferResources[attribIndex] = &bufferVk->getBuffer();
+                mCurrentArrayBufferHandles[attribIndex] =
+                    bufferVk->getBuffer().getBuffer().getHandle();
                 mCurrentArrayBufferOffsets[attribIndex]   = binding.getOffset();
                 mCurrentArrayBufferStrides[attribIndex]   = binding.getStride();
             }
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index 4e041a1..32b26d9 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -503,6 +503,28 @@
     commandBuffer->drawIndexed(count + 1, 1, 0, 0, 0);
 }
 
+// BufferHelper implementation.
+BufferHelper::BufferHelper() : mMemoryPropertyFlags{}
+{
+}
+
+BufferHelper::~BufferHelper() = default;
+
+angle::Result BufferHelper::init(ContextVk *contextVk,
+                                 const VkBufferCreateInfo &createInfo,
+                                 VkMemoryPropertyFlags memoryPropertyFlags)
+{
+    ANGLE_TRY(mBuffer.init(contextVk, createInfo));
+    return vk::AllocateBufferMemory(contextVk, memoryPropertyFlags, &mMemoryPropertyFlags, &mBuffer,
+                                    &mDeviceMemory);
+}
+
+void BufferHelper::release(RendererVk *renderer)
+{
+    renderer->releaseObject(getStoredQueueSerial(), &mBuffer);
+    renderer->releaseObject(getStoredQueueSerial(), &mDeviceMemory);
+}
+
 // ImageHelper implementation.
 ImageHelper::ImageHelper()
     : mFormat(nullptr), mSamples(0), mCurrentLayout(VK_IMAGE_LAYOUT_UNDEFINED), mLayerCount(0)
@@ -527,11 +549,6 @@
     ASSERT(!valid());
 }
 
-bool ImageHelper::valid() const
-{
-    return mImage.valid();
-}
-
 angle::Result ImageHelper::init(Context *context,
                                 gl::TextureType textureType,
                                 const gl::Extents &extents,
@@ -572,10 +589,10 @@
     return angle::Result::Continue();
 }
 
-void ImageHelper::release(Serial serial, RendererVk *renderer)
+void ImageHelper::release(RendererVk *renderer)
 {
-    renderer->releaseObject(serial, &mImage);
-    renderer->releaseObject(serial, &mDeviceMemory);
+    renderer->releaseObject(getStoredQueueSerial(), &mImage);
+    renderer->releaseObject(getStoredQueueSerial(), &mDeviceMemory);
 }
 
 void ImageHelper::resetImageWeakReference()
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h
index 858e6fc..ab20e72 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.h
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.h
@@ -166,15 +166,37 @@
     DynamicBuffer mDynamicIndexBuffer;
 };
 
-class ImageHelper final : angle::NonCopyable
+class BufferHelper final : public CommandGraphResource
+{
+  public:
+    BufferHelper();
+    ~BufferHelper();
+
+    angle::Result init(ContextVk *contextVk,
+                       const VkBufferCreateInfo &createInfo,
+                       VkMemoryPropertyFlags memoryPropertyFlags);
+    void release(RendererVk *renderer);
+
+    bool valid() const { return mBuffer.valid(); }
+    const Buffer &getBuffer() const { return mBuffer; }
+    const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; }
+
+  private:
+    // Vulkan objects.
+    Buffer mBuffer;
+    DeviceMemory mDeviceMemory;
+
+    // Cached properties.
+    VkMemoryPropertyFlags mMemoryPropertyFlags;
+};
+
+class ImageHelper final : public CommandGraphResource
 {
   public:
     ImageHelper();
     ImageHelper(ImageHelper &&other);
     ~ImageHelper();
 
-    bool valid() const;
-
     angle::Result init(Context *context,
                        gl::TextureType textureType,
                        const gl::Extents &extents,
@@ -205,8 +227,11 @@
                                 const gl::Extents &extent,
                                 StagingUsage usage);
 
+    void release(RendererVk *renderer);
+
+    bool valid() const { return mImage.valid(); }
+
     VkImageAspectFlags getAspectFlags() const;
-    void release(Serial serial, RendererVk *renderer);
     void destroy(VkDevice device);
     void dumpResources(Serial serial, std::vector<GarbageObject> *garbageQueue);