Commit f750d86a by Tim Van Patten Committed by Commit Bot

Trigger a flush() when the command graph contains too many objects

If an App repeatedly issues GL commands like glTextImage2D without a finish/flush/draw, it's possible for ANGLE to exhaust the available Vulkan memory allocations and exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount. When this occurs, the Vulkan validation layers will trigger an error and cause dEQP tests to fail. This change will query the backend if a flush() should be performed during each of the GL delete calls, and perform it if necessary. This will cause a queue submission and a Serial increment, allowing the allocated memory to be freed, preventing the validation errors. Bug: angleproject:3818 Test: KHR-GLES3.copy_tex_image_conversions.forbidden.renderbuffer_cubemap_* Change-Id: I26d0a47aa7bca10c25bc8141f1523afbab0b3b5b Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1834781 Commit-Queue: Tim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 1f08ab28
...@@ -2909,6 +2909,11 @@ void ContextVk::insertWaitSemaphore(const vk::Semaphore *waitSemaphore) ...@@ -2909,6 +2909,11 @@ void ContextVk::insertWaitSemaphore(const vk::Semaphore *waitSemaphore)
mWaitSemaphores.push_back(waitSemaphore->getHandle()); mWaitSemaphores.push_back(waitSemaphore->getHandle());
} }
bool ContextVk::shouldFlush()
{
return getRenderer()->shouldCleanupGarbage();
}
angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore) angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore)
{ {
if (mCommandGraph.empty() && !signalSemaphore && mWaitSemaphores.empty()) if (mCommandGraph.empty() && !signalSemaphore && mWaitSemaphores.empty())
......
...@@ -324,6 +324,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO ...@@ -324,6 +324,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
void insertWaitSemaphore(const vk::Semaphore *waitSemaphore); void insertWaitSemaphore(const vk::Semaphore *waitSemaphore);
bool shouldFlush();
angle::Result flushImpl(const vk::Semaphore *semaphore); angle::Result flushImpl(const vk::Semaphore *semaphore);
angle::Result finishImpl(); angle::Result finishImpl();
......
...@@ -832,6 +832,10 @@ angle::Result RendererVk::initialize(DisplayVk *displayVk, ...@@ -832,6 +832,10 @@ angle::Result RendererVk::initialize(DisplayVk *displayVk,
ChoosePhysicalDevice(physicalDevices, mEnabledICD, &mPhysicalDevice, ChoosePhysicalDevice(physicalDevices, mEnabledICD, &mPhysicalDevice,
&mPhysicalDeviceProperties); &mPhysicalDeviceProperties);
mGarbageCollectionFlushThreshold =
static_cast<uint32_t>(mPhysicalDeviceProperties.limits.maxMemoryAllocationCount *
kPercentMaxMemoryAllocationCount);
vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysicalDeviceFeatures); vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysicalDeviceFeatures);
// Ensure we can find a graphics queue family. // Ensure we can find a graphics queue family.
......
...@@ -197,6 +197,11 @@ class RendererVk : angle::NonCopyable ...@@ -197,6 +197,11 @@ class RendererVk : angle::NonCopyable
void onCompletedSerial(Serial serial); void onCompletedSerial(Serial serial);
bool shouldCleanupGarbage()
{
return (mSharedGarbage.size() > mGarbageCollectionFlushThreshold);
}
private: private:
angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex); angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex);
void ensureCapsInitialized() const; void ensureCapsInitialized() const;
...@@ -280,6 +285,11 @@ class RendererVk : angle::NonCopyable ...@@ -280,6 +285,11 @@ class RendererVk : angle::NonCopyable
// Latest validation data for debug overlay. // Latest validation data for debug overlay.
std::string mLastValidationMessage; std::string mLastValidationMessage;
uint32_t mValidationMessageCount; uint32_t mValidationMessageCount;
// How close to VkPhysicalDeviceLimits::maxMemoryAllocationCount we allow ourselves to get
static constexpr double kPercentMaxMemoryAllocationCount = 0.3;
// How many objects to garbage collect before issuing a flush()
uint32_t mGarbageCollectionFlushThreshold;
}; };
} // namespace rx } // namespace rx
......
...@@ -1309,6 +1309,14 @@ angle::Result BufferHelper::init(ContextVk *contextVk, ...@@ -1309,6 +1309,14 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
const VkBufferCreateInfo &createInfo, const VkBufferCreateInfo &createInfo,
VkMemoryPropertyFlags memoryPropertyFlags) VkMemoryPropertyFlags memoryPropertyFlags)
{ {
// TODO: Remove with anglebug.com/2162: Vulkan: Implement device memory sub-allocation
// Check if we have too many resources allocated already and need to free some before allocating
// more and (possibly) exceeding the device's limits.
if (contextVk->shouldFlush())
{
ANGLE_TRY(contextVk->flushImpl(nullptr));
}
mSize = createInfo.size; mSize = createInfo.size;
ANGLE_VK_TRY(contextVk, mBuffer.init(contextVk->getDevice(), createInfo)); ANGLE_VK_TRY(contextVk, mBuffer.init(contextVk->getDevice(), createInfo));
return vk::AllocateBufferMemory(contextVk, memoryPropertyFlags, &mMemoryPropertyFlags, nullptr, return vk::AllocateBufferMemory(contextVk, memoryPropertyFlags, &mMemoryPropertyFlags, nullptr,
......
...@@ -341,8 +341,16 @@ void StagingBuffer::destroy(VkDevice device) ...@@ -341,8 +341,16 @@ void StagingBuffer::destroy(VkDevice device)
mSize = 0; mSize = 0;
} }
angle::Result StagingBuffer::init(Context *context, VkDeviceSize size, StagingUsage usage) angle::Result StagingBuffer::init(ContextVk *contextVk, VkDeviceSize size, StagingUsage usage)
{ {
// TODO: Remove with anglebug.com/2162: Vulkan: Implement device memory sub-allocation
// Check if we have too many resources allocated already and need to free some before allocating
// more and (possibly) exceeding the device's limits.
if (contextVk->shouldFlush())
{
ANGLE_TRY(contextVk->flushImpl(nullptr));
}
VkBufferCreateInfo createInfo = {}; VkBufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.flags = 0; createInfo.flags = 0;
...@@ -355,9 +363,9 @@ angle::Result StagingBuffer::init(Context *context, VkDeviceSize size, StagingUs ...@@ -355,9 +363,9 @@ angle::Result StagingBuffer::init(Context *context, VkDeviceSize size, StagingUs
VkMemoryPropertyFlags flags = VkMemoryPropertyFlags flags =
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
ANGLE_VK_TRY(context, mBuffer.init(context->getDevice(), createInfo)); ANGLE_VK_TRY(contextVk, mBuffer.init(contextVk->getDevice(), createInfo));
VkMemoryPropertyFlags flagsOut = 0; VkMemoryPropertyFlags flagsOut = 0;
ANGLE_TRY(AllocateBufferMemory(context, flags, &flagsOut, nullptr, &mBuffer, &mDeviceMemory)); ANGLE_TRY(AllocateBufferMemory(contextVk, flags, &flagsOut, nullptr, &mBuffer, &mDeviceMemory));
mSize = static_cast<size_t>(size); mSize = static_cast<size_t>(size);
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -292,7 +292,7 @@ class StagingBuffer final : angle::NonCopyable ...@@ -292,7 +292,7 @@ class StagingBuffer final : angle::NonCopyable
void release(ContextVk *contextVk); void release(ContextVk *contextVk);
void destroy(VkDevice device); void destroy(VkDevice device);
angle::Result init(Context *context, VkDeviceSize size, StagingUsage usage); angle::Result init(ContextVk *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; }
......
...@@ -95,13 +95,6 @@ ...@@ -95,13 +95,6 @@
3817 VULKAN : KHR-GLES3.copy_tex_image_conversions.required.cubemap_posz_cubemap_posy = FAIL 3817 VULKAN : KHR-GLES3.copy_tex_image_conversions.required.cubemap_posz_cubemap_posy = FAIL
3817 VULKAN : KHR-GLES3.copy_tex_image_conversions.required.cubemap_posz_cubemap_posz = FAIL 3817 VULKAN : KHR-GLES3.copy_tex_image_conversions.required.cubemap_posz_cubemap_posz = FAIL
// Failing due to Vulkan validation error re: "Number of currently valid memory objects is not less than the maximum allowed" error.
3818 VULKAN : KHR-GLES3.copy_tex_image_conversions.forbidden.renderbuffer_cubemap_negy = FAIL
3818 VULKAN : KHR-GLES3.copy_tex_image_conversions.forbidden.renderbuffer_cubemap_negz = FAIL
3818 VULKAN : KHR-GLES3.copy_tex_image_conversions.forbidden.renderbuffer_cubemap_posx = FAIL
3818 VULKAN : KHR-GLES3.copy_tex_image_conversions.forbidden.renderbuffer_cubemap_posy = FAIL
3818 VULKAN : KHR-GLES3.copy_tex_image_conversions.forbidden.renderbuffer_cubemap_posz = FAIL
// glCopyTexImage from 3D textures is failing and getting the following Vulkan Validation errors: // glCopyTexImage from 3D textures is failing and getting the following Vulkan Validation errors:
// //
// ImageView must not be a 2D or 2DArray view of a 3D image. The Vulkan spec states: // ImageView must not be a 2D or 2DArray view of a 3D image. The Vulkan spec states:
......
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