Commit 6e0d718a by Hyunchang Kim Committed by Commit Bot

Vulkan: Implement device memory sub-allocation

Use AMD Vulkan Memory Allocator for device memory sub-allocation. We now have a mempool from which all glBuffer memory is allocated. The CPU overhead involved in repeated IOCTL calls to the kernel is reduced significantly. Bug: angleproject:2162 Change-Id: Id7681ffe2ac3d2853141ebe34c7df7b7fdd0d55e Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2124519Reviewed-by: 's avatarTobin Ehlis <tobine@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Mohan Maiya <m.maiya@samsung.com>
parent d26fb99e
...@@ -294,13 +294,12 @@ angle::Result BufferVk::copySubData(const gl::Context *context, ...@@ -294,13 +294,12 @@ angle::Result BufferVk::copySubData(const gl::Context *context,
// Update the shadow buffer // Update the shadow buffer
uint8_t *srcPtr; uint8_t *srcPtr;
ANGLE_VK_TRY(contextVk, sourceBuffer->getBuffer().getDeviceMemory().map( ANGLE_TRY(sourceBuffer->getBuffer().mapWithOffset(contextVk, &srcPtr, sourceOffset));
contextVk->getDevice(), sourceOffset, size, 0, &srcPtr));
updateShadowBuffer(srcPtr, size, destOffset); updateShadowBuffer(srcPtr, size, destOffset);
// Unmap the source buffer // Unmap the source buffer
sourceBuffer->getBuffer().getDeviceMemory().unmap(contextVk->getDevice()); sourceBuffer->getBuffer().unmap(contextVk->getRenderer());
} }
vk::CommandBuffer *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
...@@ -359,9 +358,8 @@ angle::Result BufferVk::mapRangeImpl(ContextVk *contextVk, ...@@ -359,9 +358,8 @@ angle::Result BufferVk::mapRangeImpl(ContextVk *contextVk,
ANGLE_TRY(mBuffer.waitForIdle(contextVk)); ANGLE_TRY(mBuffer.waitForIdle(contextVk));
} }
ANGLE_VK_TRY(contextVk, ANGLE_TRY(mBuffer.mapWithOffset(contextVk, reinterpret_cast<uint8_t **>(mapPtr),
mBuffer.getDeviceMemory().map(contextVk->getDevice(), offset, length, 0, static_cast<size_t>(offset)));
reinterpret_cast<uint8_t **>(mapPtr)));
} }
else else
{ {
...@@ -394,7 +392,7 @@ angle::Result BufferVk::unmapImpl(ContextVk *contextVk) ...@@ -394,7 +392,7 @@ angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
if (!mShadowBuffer.valid()) if (!mShadowBuffer.valid())
{ {
mBuffer.getDeviceMemory().unmap(contextVk->getDevice()); mBuffer.unmap(contextVk->getRenderer());
mBuffer.onExternalWrite(VK_ACCESS_HOST_WRITE_BIT); mBuffer.onExternalWrite(VK_ACCESS_HOST_WRITE_BIT);
} }
else else
...@@ -450,10 +448,7 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context, ...@@ -450,10 +448,7 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context,
ASSERT(mBuffer.valid()); ASSERT(mBuffer.valid());
const GLuint &typeBytes = gl::GetDrawElementsTypeSize(type); ANGLE_TRY(mBuffer.mapWithOffset(contextVk, &mapPointer, offset));
ANGLE_VK_TRY(contextVk, mBuffer.getDeviceMemory().map(contextVk->getDevice(), offset,
typeBytes * count, 0, &mapPointer));
} }
else else
{ {
...@@ -462,7 +457,7 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context, ...@@ -462,7 +457,7 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context,
*outRange = gl::ComputeIndexRange(type, mapPointer, count, primitiveRestartEnabled); *outRange = gl::ComputeIndexRange(type, mapPointer, count, primitiveRestartEnabled);
mBuffer.getDeviceMemory().unmap(contextVk->getDevice()); mBuffer.unmap(renderer);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -471,15 +466,14 @@ angle::Result BufferVk::directUpdate(ContextVk *contextVk, ...@@ -471,15 +466,14 @@ angle::Result BufferVk::directUpdate(ContextVk *contextVk,
size_t size, size_t size,
size_t offset) size_t offset)
{ {
VkDevice device = contextVk->getDevice();
uint8_t *mapPointer = nullptr; uint8_t *mapPointer = nullptr;
ANGLE_VK_TRY(contextVk, mBuffer.getDeviceMemory().map(device, offset, size, 0, &mapPointer)); ANGLE_TRY(mBuffer.mapWithOffset(contextVk, &mapPointer, offset));
ASSERT(mapPointer); ASSERT(mapPointer);
memcpy(mapPointer, data, size); memcpy(mapPointer, data, size);
mBuffer.getDeviceMemory().unmap(device); mBuffer.unmap(contextVk->getRenderer());
mBuffer.onExternalWrite(VK_ACCESS_HOST_WRITE_BIT); mBuffer.onExternalWrite(VK_ACCESS_HOST_WRITE_BIT);
return angle::Result::Continue; return angle::Result::Continue;
......
...@@ -2132,7 +2132,7 @@ angle::Result ContextVk::drawArraysIndirect(const gl::Context *context, ...@@ -2132,7 +2132,7 @@ angle::Result ContextVk::drawArraysIndirect(const gl::Context *context,
// We have instanced vertex attributes that need to be emulated for Vulkan. // We have instanced vertex attributes that need to be emulated for Vulkan.
// invalidate any cache and map the buffer so that we can read the indirect data. // invalidate any cache and map the buffer so that we can read the indirect data.
// Mapping the buffer will cause a flush. // Mapping the buffer will cause a flush.
ANGLE_TRY(currentIndirectBuf->invalidate(this, 0, sizeof(VkDrawIndirectCommand))); ANGLE_TRY(currentIndirectBuf->invalidate(mRenderer, 0, sizeof(VkDrawIndirectCommand)));
uint8_t *buffPtr; uint8_t *buffPtr;
ANGLE_TRY(currentIndirectBuf->map(this, &buffPtr)); ANGLE_TRY(currentIndirectBuf->map(this, &buffPtr));
const VkDrawIndirectCommand *indirectData = const VkDrawIndirectCommand *indirectData =
...@@ -2141,7 +2141,7 @@ angle::Result ContextVk::drawArraysIndirect(const gl::Context *context, ...@@ -2141,7 +2141,7 @@ angle::Result ContextVk::drawArraysIndirect(const gl::Context *context,
ANGLE_TRY(drawArraysInstanced(context, mode, indirectData->firstVertex, ANGLE_TRY(drawArraysInstanced(context, mode, indirectData->firstVertex,
indirectData->vertexCount, indirectData->instanceCount)); indirectData->vertexCount, indirectData->instanceCount));
currentIndirectBuf->unmap(getDevice()); currentIndirectBuf->unmap(mRenderer);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -2186,7 +2186,8 @@ angle::Result ContextVk::drawElementsIndirect(const gl::Context *context, ...@@ -2186,7 +2186,8 @@ angle::Result ContextVk::drawElementsIndirect(const gl::Context *context,
// We have instanced vertex attributes that need to be emulated for Vulkan. // We have instanced vertex attributes that need to be emulated for Vulkan.
// invalidate any cache and map the buffer so that we can read the indirect data. // invalidate any cache and map the buffer so that we can read the indirect data.
// Mapping the buffer will cause a flush. // Mapping the buffer will cause a flush.
ANGLE_TRY(currentIndirectBuf->invalidate(this, 0, sizeof(VkDrawIndexedIndirectCommand))); ANGLE_TRY(
currentIndirectBuf->invalidate(mRenderer, 0, sizeof(VkDrawIndexedIndirectCommand)));
uint8_t *buffPtr; uint8_t *buffPtr;
ANGLE_TRY(currentIndirectBuf->map(this, &buffPtr)); ANGLE_TRY(currentIndirectBuf->map(this, &buffPtr));
const VkDrawIndexedIndirectCommand *indirectData = const VkDrawIndexedIndirectCommand *indirectData =
...@@ -2195,7 +2196,7 @@ angle::Result ContextVk::drawElementsIndirect(const gl::Context *context, ...@@ -2195,7 +2196,7 @@ angle::Result ContextVk::drawElementsIndirect(const gl::Context *context,
ANGLE_TRY(drawElementsInstanced(context, mode, indirectData->indexCount, type, nullptr, ANGLE_TRY(drawElementsInstanced(context, mode, indirectData->indexCount, type, nullptr,
indirectData->instanceCount)); indirectData->instanceCount));
currentIndirectBuf->unmap(getDevice()); currentIndirectBuf->unmap(mRenderer);
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -102,8 +102,8 @@ angle::Result OverlayVk::createFont(ContextVk *contextVk) ...@@ -102,8 +102,8 @@ angle::Result OverlayVk::createFont(ContextVk *contextVk)
mState.initFontData(fontData); mState.initFontData(fontData);
ANGLE_TRY(fontDataBuffer.get().flush(contextVk, 0, fontDataBuffer.get().getSize())); ANGLE_TRY(fontDataBuffer.get().flush(renderer, 0, fontDataBuffer.get().getSize()));
fontDataBuffer.get().unmap(contextVk->getDevice()); fontDataBuffer.get().unmap(renderer);
fontDataBuffer.get().onExternalWrite(VK_ACCESS_HOST_WRITE_BIT); fontDataBuffer.get().onExternalWrite(VK_ACCESS_HOST_WRITE_BIT);
...@@ -171,8 +171,8 @@ angle::Result OverlayVk::cullWidgets(ContextVk *contextVk) ...@@ -171,8 +171,8 @@ angle::Result OverlayVk::cullWidgets(ContextVk *contextVk)
gl::Extents presentImageExtents(mPresentImageExtent.width, mPresentImageExtent.height, 1); gl::Extents presentImageExtents(mPresentImageExtent.width, mPresentImageExtent.height, 1);
mState.fillEnabledWidgetCoordinates(presentImageExtents, enabledWidgets); mState.fillEnabledWidgetCoordinates(presentImageExtents, enabledWidgets);
ANGLE_TRY(enabledWidgetsBuffer.get().flush(contextVk, 0, enabledWidgetsBuffer.get().getSize())); ANGLE_TRY(enabledWidgetsBuffer.get().flush(renderer, 0, enabledWidgetsBuffer.get().getSize()));
enabledWidgetsBuffer.get().unmap(contextVk->getDevice()); enabledWidgetsBuffer.get().unmap(renderer);
enabledWidgetsBuffer.get().onExternalWrite(VK_ACCESS_HOST_WRITE_BIT); enabledWidgetsBuffer.get().onExternalWrite(VK_ACCESS_HOST_WRITE_BIT);
...@@ -261,10 +261,10 @@ angle::Result OverlayVk::onPresent(ContextVk *contextVk, ...@@ -261,10 +261,10 @@ angle::Result OverlayVk::onPresent(ContextVk *contextVk,
gl::Extents presentImageExtents(mPresentImageExtent.width, mPresentImageExtent.height, 1); gl::Extents presentImageExtents(mPresentImageExtent.width, mPresentImageExtent.height, 1);
mState.fillWidgetData(presentImageExtents, textData, graphData); mState.fillWidgetData(presentImageExtents, textData, graphData);
ANGLE_TRY(textDataBuffer.get().flush(contextVk, 0, textDataBuffer.get().getSize())); ANGLE_TRY(textDataBuffer.get().flush(renderer, 0, textDataBuffer.get().getSize()));
ANGLE_TRY(graphDataBuffer.get().flush(contextVk, 0, graphDataBuffer.get().getSize())); ANGLE_TRY(graphDataBuffer.get().flush(renderer, 0, graphDataBuffer.get().getSize()));
textDataBuffer.get().unmap(contextVk->getDevice()); textDataBuffer.get().unmap(renderer);
graphDataBuffer.get().unmap(contextVk->getDevice()); graphDataBuffer.get().unmap(renderer);
UtilsVk::OverlayDrawParameters params; UtilsVk::OverlayDrawParameters params;
params.subgroupSize[0] = mSubgroupSize[0]; params.subgroupSize[0] = mSubgroupSize[0];
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
#include "libANGLE/renderer/vulkan/VertexArrayVk.h" #include "libANGLE/renderer/vulkan/VertexArrayVk.h"
#include "libANGLE/renderer/vulkan/vk_caps_utils.h" #include "libANGLE/renderer/vulkan/vk_caps_utils.h"
#include "libANGLE/renderer/vulkan/vk_format_utils.h" #include "libANGLE/renderer/vulkan/vk_format_utils.h"
#include "libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.h"
#include "libANGLE/trace.h" #include "libANGLE/trace.h"
#include "platform/Platform.h" #include "platform/Platform.h"
...@@ -623,6 +622,8 @@ void RendererVk::onDestroy() ...@@ -623,6 +622,8 @@ void RendererVk::onDestroy()
mPipelineCache.destroy(mDevice); mPipelineCache.destroy(mDevice);
vma::DestroyAllocator(mAllocator);
if (mGlslangInitialized) if (mGlslangInitialized)
{ {
GlslangRelease(); GlslangRelease();
...@@ -925,6 +926,9 @@ angle::Result RendererVk::initialize(DisplayVk *displayVk, ...@@ -925,6 +926,9 @@ angle::Result RendererVk::initialize(DisplayVk *displayVk,
ANGLE_TRY(initializeDevice(displayVk, firstGraphicsQueueFamily)); ANGLE_TRY(initializeDevice(displayVk, firstGraphicsQueueFamily));
} }
// Create VMA allocator
ANGLE_VK_TRY(displayVk, vma::InitAllocator(mPhysicalDevice, mDevice, mInstance, &mAllocator));
// Store the physical device memory properties so we can find the right memory pools. // Store the physical device memory properties so we can find the right memory pools.
mMemoryProperties.init(mPhysicalDevice); mMemoryProperties.init(mPhysicalDevice);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "libANGLE/renderer/vulkan/vk_format_utils.h" #include "libANGLE/renderer/vulkan/vk_format_utils.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h" #include "libANGLE/renderer/vulkan/vk_helpers.h"
#include "libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h" #include "libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h"
#include "libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.h"
namespace egl namespace egl
{ {
...@@ -100,6 +101,8 @@ class RendererVk : angle::NonCopyable ...@@ -100,6 +101,8 @@ class RendererVk : angle::NonCopyable
} }
VkDevice getDevice() const { return mDevice; } VkDevice getDevice() const { return mDevice; }
const VmaAllocator &getAllocator() const { return mAllocator; }
angle::Result selectPresentQueueForSurface(DisplayVk *displayVk, angle::Result selectPresentQueueForSurface(DisplayVk *displayVk,
VkSurfaceKHR surface, VkSurfaceKHR surface,
uint32_t *presentQueueOut); uint32_t *presentQueueOut);
...@@ -355,6 +358,8 @@ class RendererVk : angle::NonCopyable ...@@ -355,6 +358,8 @@ class RendererVk : angle::NonCopyable
// track whether we initialized (or released) glslang // track whether we initialized (or released) glslang
bool mGlslangInitialized; bool mGlslangInitialized;
VmaAllocator mAllocator;
}; };
} // namespace rx } // namespace rx
......
...@@ -545,7 +545,7 @@ angle::Result DynamicBuffer::allocate(ContextVk *contextVk, ...@@ -545,7 +545,7 @@ angle::Result DynamicBuffer::allocate(ContextVk *contextVk,
if (mBuffer) if (mBuffer)
{ {
ANGLE_TRY(flush(contextVk)); ANGLE_TRY(flush(contextVk));
mBuffer->unmap(contextVk->getDevice()); mBuffer->unmap(contextVk->getRenderer());
mInFlightBuffers.push_back(mBuffer); mInFlightBuffers.push_back(mBuffer);
mBuffer = nullptr; mBuffer = nullptr;
...@@ -617,7 +617,7 @@ angle::Result DynamicBuffer::flush(ContextVk *contextVk) ...@@ -617,7 +617,7 @@ angle::Result DynamicBuffer::flush(ContextVk *contextVk)
if (mHostVisible && (mNextAllocationOffset > mLastFlushOrInvalidateOffset)) if (mHostVisible && (mNextAllocationOffset > mLastFlushOrInvalidateOffset))
{ {
ASSERT(mBuffer != nullptr); ASSERT(mBuffer != nullptr);
ANGLE_TRY(mBuffer->flush(contextVk, mLastFlushOrInvalidateOffset, ANGLE_TRY(mBuffer->flush(contextVk->getRenderer(), mLastFlushOrInvalidateOffset,
mNextAllocationOffset - mLastFlushOrInvalidateOffset)); mNextAllocationOffset - mLastFlushOrInvalidateOffset));
mLastFlushOrInvalidateOffset = mNextAllocationOffset; mLastFlushOrInvalidateOffset = mNextAllocationOffset;
} }
...@@ -629,7 +629,7 @@ angle::Result DynamicBuffer::invalidate(ContextVk *contextVk) ...@@ -629,7 +629,7 @@ angle::Result DynamicBuffer::invalidate(ContextVk *contextVk)
if (mHostVisible && (mNextAllocationOffset > mLastFlushOrInvalidateOffset)) if (mHostVisible && (mNextAllocationOffset > mLastFlushOrInvalidateOffset))
{ {
ASSERT(mBuffer != nullptr); ASSERT(mBuffer != nullptr);
ANGLE_TRY(mBuffer->invalidate(contextVk, mLastFlushOrInvalidateOffset, ANGLE_TRY(mBuffer->invalidate(contextVk->getRenderer(), mLastFlushOrInvalidateOffset,
mNextAllocationOffset - mLastFlushOrInvalidateOffset)); mNextAllocationOffset - mLastFlushOrInvalidateOffset));
mLastFlushOrInvalidateOffset = mNextAllocationOffset; mLastFlushOrInvalidateOffset = mNextAllocationOffset;
} }
...@@ -700,7 +700,7 @@ void DynamicBuffer::destroy(RendererVk *renderer) ...@@ -700,7 +700,7 @@ void DynamicBuffer::destroy(RendererVk *renderer)
if (mBuffer) if (mBuffer)
{ {
mBuffer->unmap(renderer->getDevice()); mBuffer->unmap(renderer);
mBuffer->destroy(renderer); mBuffer->destroy(renderer);
delete mBuffer; delete mBuffer;
mBuffer = nullptr; mBuffer = nullptr;
...@@ -1689,11 +1689,14 @@ angle::Result BufferHelper::init(ContextVk *contextVk, ...@@ -1689,11 +1689,14 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
createInfo = &modifiedCreateInfo; createInfo = &modifiedCreateInfo;
} }
ANGLE_VK_TRY(contextVk, mBuffer.init(contextVk->getDevice(), *createInfo)); VkMemoryPropertyFlags requiredFlags =
(memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
VkMemoryPropertyFlags preferredFlags =
(memoryPropertyFlags & (~VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
mAllocation.createBufferAndMemory(renderer->getAllocator(), createInfo, requiredFlags,
preferredFlags, &mBuffer, &mMemoryPropertyFlags);
VkDeviceSize size;
ANGLE_TRY(AllocateBufferMemory(contextVk, memoryPropertyFlags, &mMemoryPropertyFlags, nullptr,
&mBuffer, &mDeviceMemory, &size));
mCurrentQueueFamilyIndex = contextVk->getRenderer()->getQueueFamilyIndex(); mCurrentQueueFamilyIndex = contextVk->getRenderer()->getQueueFamilyIndex();
if (renderer->getFeatures().allocateNonZeroMemory.enabled) if (renderer->getFeatures().allocateNonZeroMemory.enabled)
...@@ -1701,10 +1704,18 @@ angle::Result BufferHelper::init(ContextVk *contextVk, ...@@ -1701,10 +1704,18 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
// This memory can't be mapped, so the buffer must be marked as a transfer destination so we // This memory can't be mapped, so the buffer must be marked as a transfer destination so we
// can use a staging resource to initialize it to a non-zero value. If the memory is // can use a staging resource to initialize it to a non-zero value. If the memory is
// mappable we do the initialization in AllocateBufferMemory. // mappable we do the initialization in AllocateBufferMemory.
if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0 && if ((mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0 &&
(requestedCreateInfo.usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT) != 0) (requestedCreateInfo.usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT) != 0)
{ {
ANGLE_TRY(initializeNonZeroMemory(contextVk, size)); ANGLE_TRY(initializeNonZeroMemory(contextVk, createInfo->size));
}
else if ((mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
{
// Can map the memory.
// Pick an arbitrary value to initialize non-zero memory for sanitization.
constexpr int kNonZeroInitValue = 55;
ANGLE_TRY(InitMappableAllocation(renderer->getAllocator(), &mAllocation, mSize,
kNonZeroInitValue, mMemoryPropertyFlags));
} }
} }
...@@ -1745,23 +1756,22 @@ angle::Result BufferHelper::initializeNonZeroMemory(Context *context, VkDeviceSi ...@@ -1745,23 +1756,22 @@ angle::Result BufferHelper::initializeNonZeroMemory(Context *context, VkDeviceSi
void BufferHelper::destroy(RendererVk *renderer) void BufferHelper::destroy(RendererVk *renderer)
{ {
VkDevice device = renderer->getDevice(); VkDevice device = renderer->getDevice();
unmap(renderer);
unmap(device);
mSize = 0; mSize = 0;
mViewFormat = nullptr; mViewFormat = nullptr;
mBuffer.destroy(device); mBuffer.destroy(device);
mBufferView.destroy(device); mBufferView.destroy(device);
mDeviceMemory.destroy(device); mAllocation.destroy(renderer->getAllocator());
} }
void BufferHelper::release(RendererVk *renderer) void BufferHelper::release(RendererVk *renderer)
{ {
unmap(renderer->getDevice()); unmap(renderer);
mSize = 0; mSize = 0;
mViewFormat = nullptr; mViewFormat = nullptr;
renderer->collectGarbageAndReinit(&mUse, &mBuffer, &mBufferView, &mDeviceMemory); renderer->collectGarbageAndReinit(&mUse, &mBuffer, &mBufferView, &mAllocation);
} }
bool BufferHelper::needsOnWriteBarrier(VkAccessFlags writeAccessType, bool BufferHelper::needsOnWriteBarrier(VkAccessFlags writeAccessType,
...@@ -1821,47 +1831,39 @@ angle::Result BufferHelper::initBufferView(ContextVk *contextVk, const Format &f ...@@ -1821,47 +1831,39 @@ angle::Result BufferHelper::initBufferView(ContextVk *contextVk, const Format &f
angle::Result BufferHelper::mapImpl(ContextVk *contextVk) angle::Result BufferHelper::mapImpl(ContextVk *contextVk)
{ {
ANGLE_VK_TRY(contextVk, mDeviceMemory.map(contextVk->getDevice(), 0, mSize, 0, &mMappedMemory)); ANGLE_VK_TRY(contextVk,
mAllocation.map(contextVk->getRenderer()->getAllocator(), &mMappedMemory));
return angle::Result::Continue; return angle::Result::Continue;
} }
void BufferHelper::unmap(VkDevice device) void BufferHelper::unmap(RendererVk *renderer)
{ {
if (mMappedMemory) if (mMappedMemory)
{ {
mDeviceMemory.unmap(device); mAllocation.unmap(renderer->getAllocator());
mMappedMemory = nullptr; mMappedMemory = nullptr;
} }
} }
angle::Result BufferHelper::flush(ContextVk *contextVk, VkDeviceSize offset, VkDeviceSize size) angle::Result BufferHelper::flush(RendererVk *renderer, VkDeviceSize offset, VkDeviceSize size)
{ {
bool hostVisible = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; bool hostVisible = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
bool hostCoherent = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; bool hostCoherent = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (hostVisible && !hostCoherent) if (hostVisible && !hostCoherent)
{ {
VkMappedMemoryRange range = {}; mAllocation.flush(renderer->getAllocator(), offset, size);
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
range.memory = mDeviceMemory.getHandle();
range.offset = offset;
range.size = size;
ANGLE_VK_TRY(contextVk, vkFlushMappedMemoryRanges(contextVk->getDevice(), 1, &range));
} }
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result BufferHelper::invalidate(ContextVk *contextVk, VkDeviceSize offset, VkDeviceSize size) angle::Result BufferHelper::invalidate(RendererVk *renderer, VkDeviceSize offset, VkDeviceSize size)
{ {
bool hostVisible = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; bool hostVisible = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
bool hostCoherent = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; bool hostCoherent = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (hostVisible && !hostCoherent) if (hostVisible && !hostCoherent)
{ {
VkMappedMemoryRange range = {}; mAllocation.invalidate(renderer->getAllocator(), offset, size);
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
range.memory = mDeviceMemory.getHandle();
range.offset = offset;
range.size = size;
ANGLE_VK_TRY(contextVk, vkInvalidateMappedMemoryRanges(contextVk->getDevice(), 1, &range));
} }
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -553,7 +553,6 @@ class BufferHelper final : public Resource ...@@ -553,7 +553,6 @@ class BufferHelper final : public Resource
bool valid() const { return mBuffer.valid(); } bool valid() const { return mBuffer.valid(); }
const Buffer &getBuffer() const { return mBuffer; } const Buffer &getBuffer() const { return mBuffer; }
const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; }
VkDeviceSize getSize() const { return mSize; } VkDeviceSize getSize() const { return mSize; }
bool isHostVisible() const bool isHostVisible() const
{ {
...@@ -595,13 +594,22 @@ class BufferHelper final : public Resource ...@@ -595,13 +594,22 @@ class BufferHelper final : public Resource
*ptrOut = mMappedMemory; *ptrOut = mMappedMemory;
return angle::Result::Continue; return angle::Result::Continue;
} }
void unmap(VkDevice device);
angle::Result mapWithOffset(ContextVk *contextVk, uint8_t **ptrOut, size_t offset)
{
uint8_t *mapBufPointer;
ANGLE_TRY(map(contextVk, &mapBufPointer));
*ptrOut = mapBufPointer + offset;
return angle::Result::Continue;
}
void unmap(RendererVk *renderer);
// After a sequence of writes, call flush to ensure the data is visible to the device. // After a sequence of writes, call flush to ensure the data is visible to the device.
angle::Result flush(ContextVk *contextVk, VkDeviceSize offset, VkDeviceSize size); angle::Result flush(RendererVk *renderer, VkDeviceSize offset, VkDeviceSize size);
// After a sequence of writes, call invalidate to ensure the data is visible to the host. // After a sequence of writes, call invalidate to ensure the data is visible to the host.
angle::Result invalidate(ContextVk *contextVk, VkDeviceSize offset, VkDeviceSize size); angle::Result invalidate(RendererVk *renderer, VkDeviceSize offset, VkDeviceSize size);
void changeQueue(uint32_t newQueueFamilyIndex, CommandBuffer *commandBuffer); void changeQueue(uint32_t newQueueFamilyIndex, CommandBuffer *commandBuffer);
...@@ -641,7 +649,7 @@ class BufferHelper final : public Resource ...@@ -641,7 +649,7 @@ class BufferHelper final : public Resource
// Vulkan objects. // Vulkan objects.
Buffer mBuffer; Buffer mBuffer;
BufferView mBufferView; BufferView mBufferView;
DeviceMemory mDeviceMemory; Allocation mAllocation;
// Cached properties. // Cached properties.
VkMemoryPropertyFlags mMemoryPropertyFlags; VkMemoryPropertyFlags mMemoryPropertyFlags;
......
...@@ -14,30 +14,70 @@ ...@@ -14,30 +14,70 @@
namespace vma namespace vma
{ {
VkResult InitAllocator(VkPhysicalDevice physicalDevice,
VkDevice device,
VkInstance instance,
VmaAllocator *pAllocator)
{
VmaVulkanFunctions funcs;
funcs.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties;
funcs.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties;
funcs.vkAllocateMemory = vkAllocateMemory;
funcs.vkFreeMemory = vkFreeMemory;
funcs.vkMapMemory = vkMapMemory;
funcs.vkUnmapMemory = vkUnmapMemory;
funcs.vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges;
funcs.vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges;
funcs.vkBindBufferMemory = vkBindBufferMemory;
funcs.vkBindImageMemory = vkBindImageMemory;
funcs.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements;
funcs.vkGetImageMemoryRequirements = vkGetImageMemoryRequirements;
funcs.vkCreateBuffer = vkCreateBuffer;
funcs.vkDestroyBuffer = vkDestroyBuffer;
funcs.vkCreateImage = vkCreateImage;
funcs.vkDestroyImage = vkDestroyImage;
funcs.vkCmdCopyBuffer = vkCmdCopyBuffer;
funcs.vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2KHR;
funcs.vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2KHR;
VmaAllocatorCreateInfo allocatorInfo = {};
allocatorInfo.physicalDevice = physicalDevice;
allocatorInfo.device = device;
allocatorInfo.instance = instance;
allocatorInfo.pVulkanFunctions = &funcs;
return vmaCreateAllocator(&allocatorInfo, pAllocator);
}
void DestroyAllocator(VmaAllocator allocator)
{
vmaDestroyAllocator(allocator);
}
void FreeMemory(VmaAllocator allocator, VmaAllocation allocation)
{
vmaFreeMemory(allocator, allocation);
}
VkResult CreateBuffer(VmaAllocator allocator, VkResult CreateBuffer(VmaAllocator allocator,
const VkBufferCreateInfo *pBufferCreateInfo, const VkBufferCreateInfo *pBufferCreateInfo,
VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags requiredFlags,
VkMemoryPropertyFlags preferredFlags, VkMemoryPropertyFlags preferredFlags,
uint32_t *pMemoryTypeIndexOut,
VkBuffer *pBuffer, VkBuffer *pBuffer,
VmaAllocation *pAllocation) VmaAllocation *pAllocation)
{ {
VkResult result;
VmaAllocationCreateInfo allocationCreateInfo = {}; VmaAllocationCreateInfo allocationCreateInfo = {};
allocationCreateInfo.requiredFlags = requiredFlags; allocationCreateInfo.requiredFlags = requiredFlags;
allocationCreateInfo.preferredFlags = preferredFlags; allocationCreateInfo.preferredFlags = preferredFlags;
VmaAllocationInfo allocationInfo = {}; VmaAllocationInfo allocationInfo = {};
return vmaCreateBuffer(allocator, pBufferCreateInfo, &allocationCreateInfo, pBuffer,
pAllocation, &allocationInfo);
}
void DestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation) result = vmaCreateBuffer(allocator, pBufferCreateInfo, &allocationCreateInfo, pBuffer,
{ pAllocation, &allocationInfo);
vmaDestroyBuffer(allocator, buffer, allocation); *pMemoryTypeIndexOut = allocationInfo.memoryType;
}
void GetMemoryProperties(VmaAllocator allocator, return result;
const VkPhysicalDeviceMemoryProperties **ppPhysicalDeviceMemoryProperties)
{
return vmaGetMemoryProperties(allocator, ppPhysicalDeviceMemoryProperties);
} }
void GetMemoryTypeProperties(VmaAllocator allocator, void GetMemoryTypeProperties(VmaAllocator allocator,
......
...@@ -13,27 +13,27 @@ ...@@ -13,27 +13,27 @@
#include <volk.h> #include <volk.h>
VK_DEFINE_HANDLE(VmaAllocator) VK_DEFINE_HANDLE(VmaAllocator)
VK_DEFINE_HANDLE(VmaPool)
VK_DEFINE_HANDLE(VmaAllocation) VK_DEFINE_HANDLE(VmaAllocation)
VK_DEFINE_HANDLE(VmaDefragmentationContext)
namespace vma namespace vma
{ {
VkResult InitAllocator(VkPhysicalDevice physicalDevice,
VkDevice device,
VkInstance instance,
VmaAllocator *pAllocator);
// Please add additional functions or parameters here as needed. void DestroyAllocator(VmaAllocator allocator);
void FreeMemory(VmaAllocator allocator, VmaAllocation allocation);
VkResult CreateBuffer(VmaAllocator allocator, VkResult CreateBuffer(VmaAllocator allocator,
const VkBufferCreateInfo *pBufferCreateInfo, const VkBufferCreateInfo *pBufferCreateInfo,
VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags requiredFlags,
VkMemoryPropertyFlags preferredFlags, VkMemoryPropertyFlags preferredFlags,
uint32_t *pMemoryTypeIndexOut,
VkBuffer *pBuffer, VkBuffer *pBuffer,
VmaAllocation *pAllocation); VmaAllocation *pAllocation);
void DestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation);
void GetMemoryProperties(VmaAllocator allocator,
const VkPhysicalDeviceMemoryProperties **ppPhysicalDeviceMemoryProperties);
void GetMemoryTypeProperties(VmaAllocator allocator, void GetMemoryTypeProperties(VmaAllocator allocator,
uint32_t memoryTypeIndex, uint32_t memoryTypeIndex,
VkMemoryPropertyFlags *pFlags); VkMemoryPropertyFlags *pFlags);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "libANGLE/renderer/vulkan/DisplayVk.h" #include "libANGLE/renderer/vulkan/DisplayVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h" #include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/ResourceVk.h" #include "libANGLE/renderer/vulkan/ResourceVk.h"
#include "libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.h"
namespace angle namespace angle
{ {
...@@ -383,10 +384,11 @@ angle::Result MemoryProperties::findCompatibleMemoryIndex( ...@@ -383,10 +384,11 @@ angle::Result MemoryProperties::findCompatibleMemoryIndex(
// StagingBuffer implementation. // StagingBuffer implementation.
StagingBuffer::StagingBuffer() : mSize(0) {} StagingBuffer::StagingBuffer() : mSize(0) {}
void StagingBuffer::destroy(VkDevice device) void StagingBuffer::destroy(RendererVk *renderer)
{ {
VkDevice device = renderer->getDevice();
mBuffer.destroy(device); mBuffer.destroy(device);
mDeviceMemory.destroy(device); mAllocation.destroy(renderer->getAllocator());
mSize = 0; mSize = 0;
} }
...@@ -401,14 +403,15 @@ angle::Result StagingBuffer::init(Context *context, VkDeviceSize size, StagingUs ...@@ -401,14 +403,15 @@ angle::Result StagingBuffer::init(Context *context, VkDeviceSize size, StagingUs
createInfo.queueFamilyIndexCount = 0; createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr; createInfo.pQueueFamilyIndices = nullptr;
VkMemoryPropertyFlags flags = VkMemoryPropertyFlags memoryPropertyOutFlags;
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VkMemoryPropertyFlags preferredFlags = 0;
VkMemoryPropertyFlags requiredFlags =
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
mAllocation.createBufferAndMemory(context->getRenderer()->getAllocator(), &createInfo,
requiredFlags, preferredFlags, &mBuffer,
&memoryPropertyOutFlags);
ANGLE_VK_TRY(context, mBuffer.init(context->getDevice(), createInfo));
VkMemoryPropertyFlags flagsOut = 0;
VkDeviceSize sizeIgnored;
ANGLE_TRY(AllocateBufferMemory(context, flags, &flagsOut, nullptr, &mBuffer, &mDeviceMemory,
&sizeIgnored));
mSize = static_cast<size_t>(size); mSize = static_cast<size_t>(size);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -416,14 +419,14 @@ angle::Result StagingBuffer::init(Context *context, VkDeviceSize size, StagingUs ...@@ -416,14 +419,14 @@ angle::Result StagingBuffer::init(Context *context, VkDeviceSize size, StagingUs
void StagingBuffer::release(ContextVk *contextVk) void StagingBuffer::release(ContextVk *contextVk)
{ {
contextVk->addGarbage(&mBuffer); contextVk->addGarbage(&mBuffer);
contextVk->addGarbage(&mDeviceMemory); contextVk->addGarbage(&mAllocation);
} }
void StagingBuffer::collectGarbage(RendererVk *renderer, Serial serial) void StagingBuffer::collectGarbage(RendererVk *renderer, Serial serial)
{ {
vk::GarbageList garbageList; vk::GarbageList garbageList;
garbageList.emplace_back(vk::GetGarbage(&mBuffer)); garbageList.emplace_back(vk::GetGarbage(&mBuffer));
garbageList.emplace_back(vk::GetGarbage(&mDeviceMemory)); garbageList.emplace_back(vk::GetGarbage(&mAllocation));
vk::SharedResourceUse sharedUse; vk::SharedResourceUse sharedUse;
sharedUse.init(); sharedUse.init();
...@@ -431,6 +434,26 @@ void StagingBuffer::collectGarbage(RendererVk *renderer, Serial serial) ...@@ -431,6 +434,26 @@ void StagingBuffer::collectGarbage(RendererVk *renderer, Serial serial)
renderer->collectGarbage(std::move(sharedUse), std::move(garbageList)); renderer->collectGarbage(std::move(sharedUse), std::move(garbageList));
} }
angle::Result InitMappableAllocation(VmaAllocator allocator,
Allocation *allocation,
VkDeviceSize size,
int value,
VkMemoryPropertyFlags memoryPropertyFlags)
{
uint8_t *mapPointer;
allocation->map(allocator, &mapPointer);
memset(mapPointer, value, static_cast<size_t>(size));
if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
{
allocation->flush(allocator, 0, size);
}
allocation->unmap(allocator);
return angle::Result::Continue;
}
angle::Result InitMappableDeviceMemory(Context *context, angle::Result InitMappableDeviceMemory(Context *context,
DeviceMemory *deviceMemory, DeviceMemory *deviceMemory,
VkDeviceSize size, VkDeviceSize size,
...@@ -622,6 +645,9 @@ void GarbageObject::destroy(RendererVk *renderer) ...@@ -622,6 +645,9 @@ void GarbageObject::destroy(RendererVk *renderer)
case HandleType::QueryPool: case HandleType::QueryPool:
vkDestroyQueryPool(device, (VkQueryPool)mHandle, nullptr); vkDestroyQueryPool(device, (VkQueryPool)mHandle, nullptr);
break; break;
case HandleType::Allocation:
vma::FreeMemory(renderer->getAllocator(), (VmaAllocation)mHandle);
break;
default: default:
UNREACHABLE(); UNREACHABLE();
break; break;
......
...@@ -314,22 +314,26 @@ class StagingBuffer final : angle::NonCopyable ...@@ -314,22 +314,26 @@ class StagingBuffer final : angle::NonCopyable
StagingBuffer(); StagingBuffer();
void release(ContextVk *contextVk); void release(ContextVk *contextVk);
void collectGarbage(RendererVk *renderer, Serial serial); void collectGarbage(RendererVk *renderer, Serial serial);
void destroy(VkDevice device); void destroy(RendererVk *renderer);
angle::Result init(Context *context, VkDeviceSize size, StagingUsage usage); angle::Result init(Context *context, VkDeviceSize size, StagingUsage usage);
Buffer &getBuffer() { return mBuffer; } Buffer &getBuffer() { return mBuffer; }
const Buffer &getBuffer() const { return mBuffer; } const Buffer &getBuffer() const { return mBuffer; }
DeviceMemory &getDeviceMemory() { return mDeviceMemory; }
const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; }
size_t getSize() const { return mSize; } size_t getSize() const { return mSize; }
private: private:
Buffer mBuffer; Buffer mBuffer;
DeviceMemory mDeviceMemory; Allocation mAllocation;
size_t mSize; size_t mSize;
}; };
angle::Result InitMappableAllocation(VmaAllocator allocator,
Allocation *allcation,
VkDeviceSize size,
int value,
VkMemoryPropertyFlags memoryPropertyFlags);
angle::Result InitMappableDeviceMemory(vk::Context *context, angle::Result InitMappableDeviceMemory(vk::Context *context,
vk::DeviceMemory *deviceMemory, vk::DeviceMemory *deviceMemory,
VkDeviceSize size, VkDeviceSize size,
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "volk.h" #include "volk.h"
#include "libANGLE/renderer/renderer_utils.h" #include "libANGLE/renderer/renderer_utils.h"
#include "libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.h"
namespace rx namespace rx
{ {
...@@ -46,7 +47,8 @@ namespace vk ...@@ -46,7 +47,8 @@ namespace vk
FUNC(RenderPass) \ FUNC(RenderPass) \
FUNC(Sampler) \ FUNC(Sampler) \
FUNC(Semaphore) \ FUNC(Semaphore) \
FUNC(ShaderModule) FUNC(ShaderModule) \
FUNC(Allocation)
#define ANGLE_COMMA_SEP_FUNC(TYPE) TYPE, #define ANGLE_COMMA_SEP_FUNC(TYPE) TYPE,
...@@ -93,6 +95,7 @@ class WrappedObject : angle::NonCopyable ...@@ -93,6 +95,7 @@ class WrappedObject : angle::NonCopyable
{ {
public: public:
HandleT getHandle() const { return mHandle; } HandleT getHandle() const { return mHandle; }
void setHandle(HandleT handle) { mHandle = handle; }
bool valid() const { return (mHandle != VK_NULL_HANDLE); } bool valid() const { return (mHandle != VK_NULL_HANDLE); }
const HandleT *ptr() const { return &mHandle; } const HandleT *ptr() const { return &mHandle; }
...@@ -453,6 +456,24 @@ class DeviceMemory final : public WrappedObject<DeviceMemory, VkDeviceMemory> ...@@ -453,6 +456,24 @@ class DeviceMemory final : public WrappedObject<DeviceMemory, VkDeviceMemory>
void unmap(VkDevice device) const; void unmap(VkDevice device) const;
}; };
class Allocation final : public WrappedObject<Allocation, VmaAllocation>
{
public:
Allocation() = default;
void destroy(VmaAllocator allocator);
VkResult createBufferAndMemory(VmaAllocator allocator,
const VkBufferCreateInfo *pBufferCreateInfo,
VkMemoryPropertyFlags requiredFlags,
VkMemoryPropertyFlags preferredFlags,
Buffer *buffer,
VkMemoryPropertyFlags *pMemPropertyOut);
VkResult map(VmaAllocator allocator, uint8_t **mapPointer) const;
void unmap(VmaAllocator allocator) const;
void flush(VmaAllocator allocator, VkDeviceSize offset, VkDeviceSize size);
void invalidate(VmaAllocator allocator, VkDeviceSize offset, VkDeviceSize size);
};
class RenderPass final : public WrappedObject<RenderPass, VkRenderPass> class RenderPass final : public WrappedObject<RenderPass, VkRenderPass>
{ {
public: public:
...@@ -1294,6 +1315,61 @@ ANGLE_INLINE void DeviceMemory::unmap(VkDevice device) const ...@@ -1294,6 +1315,61 @@ ANGLE_INLINE void DeviceMemory::unmap(VkDevice device) const
vkUnmapMemory(device, mHandle); vkUnmapMemory(device, mHandle);
} }
// Allocation implementation.
ANGLE_INLINE void Allocation::destroy(VmaAllocator allocator)
{
if (valid())
{
vma::FreeMemory(allocator, mHandle);
mHandle = VK_NULL_HANDLE;
}
}
ANGLE_INLINE VkResult Allocation::createBufferAndMemory(VmaAllocator allocator,
const VkBufferCreateInfo *pBufferCreateInfo,
VkMemoryPropertyFlags requiredFlags,
VkMemoryPropertyFlags preferredFlags,
Buffer *buffer,
VkMemoryPropertyFlags *pMemPropertyOut)
{
ASSERT(!valid());
VkResult result;
uint32_t memoryTypeIndex;
VkBuffer bufferHandle;
result = vma::CreateBuffer(allocator, pBufferCreateInfo, requiredFlags, preferredFlags,
&memoryTypeIndex, &bufferHandle, &mHandle);
vma::GetMemoryTypeProperties(allocator, memoryTypeIndex, pMemPropertyOut);
buffer->setHandle(bufferHandle);
return result;
}
ANGLE_INLINE VkResult Allocation::map(VmaAllocator allocator, uint8_t **mapPointer) const
{
ASSERT(valid());
return vma::MapMemory(allocator, mHandle, (void **)mapPointer);
}
ANGLE_INLINE void Allocation::unmap(VmaAllocator allocator) const
{
ASSERT(valid());
vma::UnmapMemory(allocator, mHandle);
}
ANGLE_INLINE void Allocation::flush(VmaAllocator allocator, VkDeviceSize offset, VkDeviceSize size)
{
ASSERT(valid());
vma::FlushAllocation(allocator, mHandle, offset, size);
}
ANGLE_INLINE void Allocation::invalidate(VmaAllocator allocator,
VkDeviceSize offset,
VkDeviceSize size)
{
ASSERT(valid());
vma::InvalidateAllocation(allocator, mHandle, offset, size);
}
// RenderPass implementation. // RenderPass implementation.
ANGLE_INLINE void RenderPass::destroy(VkDevice device) ANGLE_INLINE void RenderPass::destroy(VkDevice device)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment