GPU Vulkan: Fix recursive Submit calls causing defrag to fail (#12718)

---------

Co-authored-by: Sam Lantinga <slouken@libsdl.org>
This commit is contained in:
Evan Hemsley
2025-04-03 15:00:22 -07:00
committed by GitHub
parent b1919783c5
commit b53e7b4478

View File

@@ -1195,7 +1195,7 @@ struct VulkanRenderer
// Forward declarations // Forward declarations
static bool VULKAN_INTERNAL_DefragmentMemory(VulkanRenderer *renderer); static bool VULKAN_INTERNAL_DefragmentMemory(VulkanRenderer *renderer, VulkanCommandBuffer *commandBuffer);
static bool VULKAN_INTERNAL_BeginCommandBuffer(VulkanRenderer *renderer, VulkanCommandBuffer *commandBuffer); static bool VULKAN_INTERNAL_BeginCommandBuffer(VulkanRenderer *renderer, VulkanCommandBuffer *commandBuffer);
static void VULKAN_ReleaseWindow(SDL_GPURenderer *driverData, SDL_Window *window); static void VULKAN_ReleaseWindow(SDL_GPURenderer *driverData, SDL_Window *window);
static bool VULKAN_Wait(SDL_GPURenderer *driverData); static bool VULKAN_Wait(SDL_GPURenderer *driverData);
@@ -5578,6 +5578,7 @@ static void VULKAN_PopDebugGroup(
static VulkanTexture *VULKAN_INTERNAL_CreateTexture( static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
VulkanRenderer *renderer, VulkanRenderer *renderer,
bool transitionToDefaultLayout,
const SDL_GPUTextureCreateInfo *createinfo) const SDL_GPUTextureCreateInfo *createinfo)
{ {
VkResult vulkanResult; VkResult vulkanResult;
@@ -5805,6 +5806,7 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
&nameInfo); &nameInfo);
} }
if (transitionToDefaultLayout) {
// Let's transition to the default barrier state, because for some reason Vulkan doesn't let us do that with initialLayout. // Let's transition to the default barrier state, because for some reason Vulkan doesn't let us do that with initialLayout.
VulkanCommandBuffer *barrierCommandBuffer = (VulkanCommandBuffer *)VULKAN_AcquireCommandBuffer((SDL_GPURenderer *)renderer); VulkanCommandBuffer *barrierCommandBuffer = (VulkanCommandBuffer *)VULKAN_AcquireCommandBuffer((SDL_GPURenderer *)renderer);
VULKAN_INTERNAL_TextureTransitionToDefaultUsage( VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
@@ -5814,6 +5816,7 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
texture); texture);
VULKAN_INTERNAL_TrackTexture(barrierCommandBuffer, texture); VULKAN_INTERNAL_TrackTexture(barrierCommandBuffer, texture);
VULKAN_Submit((SDL_GPUCommandBuffer *)barrierCommandBuffer); VULKAN_Submit((SDL_GPUCommandBuffer *)barrierCommandBuffer);
}
return texture; return texture;
} }
@@ -5863,6 +5866,7 @@ static void VULKAN_INTERNAL_CycleActiveBuffer(
static void VULKAN_INTERNAL_CycleActiveTexture( static void VULKAN_INTERNAL_CycleActiveTexture(
VulkanRenderer *renderer, VulkanRenderer *renderer,
VulkanCommandBuffer *commandBuffer,
VulkanTextureContainer *container) VulkanTextureContainer *container)
{ {
VulkanTexture *texture; VulkanTexture *texture;
@@ -5880,8 +5884,15 @@ static void VULKAN_INTERNAL_CycleActiveTexture(
// No texture is available, generate a new one. // No texture is available, generate a new one.
texture = VULKAN_INTERNAL_CreateTexture( texture = VULKAN_INTERNAL_CreateTexture(
renderer, renderer,
false,
&container->header.info); &container->header.info);
VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
renderer,
commandBuffer,
VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED,
texture);
if (!texture) { if (!texture) {
return; return;
} }
@@ -5945,6 +5956,7 @@ static VulkanTextureSubresource *VULKAN_INTERNAL_PrepareTextureSubresourceForWri
SDL_GetAtomicInt(&textureContainer->activeTexture->referenceCount) > 0) { SDL_GetAtomicInt(&textureContainer->activeTexture->referenceCount) > 0) {
VULKAN_INTERNAL_CycleActiveTexture( VULKAN_INTERNAL_CycleActiveTexture(
renderer, renderer,
commandBuffer,
textureContainer); textureContainer);
textureSubresource = VULKAN_INTERNAL_FetchTextureSubresource( textureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
@@ -6726,6 +6738,7 @@ static SDL_GPUTexture *VULKAN_CreateTexture(
texture = VULKAN_INTERNAL_CreateTexture( texture = VULKAN_INTERNAL_CreateTexture(
renderer, renderer,
true,
createinfo); createinfo);
if (texture == NULL) { if (texture == NULL) {
@@ -6900,7 +6913,7 @@ static void VULKAN_INTERNAL_ReleaseBuffer(
renderer->buffersToDestroy[renderer->buffersToDestroyCount] = vulkanBuffer; renderer->buffersToDestroy[renderer->buffersToDestroyCount] = vulkanBuffer;
renderer->buffersToDestroyCount += 1; renderer->buffersToDestroyCount += 1;
vulkanBuffer->markedForDestroy = 1; vulkanBuffer->markedForDestroy = true;
vulkanBuffer->container = NULL; vulkanBuffer->container = NULL;
SDL_UnlockMutex(renderer->disposeLock); SDL_UnlockMutex(renderer->disposeLock);
@@ -10417,7 +10430,7 @@ static bool VULKAN_Submit(
Uint32 swapchainImageIndex; Uint32 swapchainImageIndex;
VulkanTextureSubresource *swapchainTextureSubresource; VulkanTextureSubresource *swapchainTextureSubresource;
VulkanMemorySubAllocator *allocator; VulkanMemorySubAllocator *allocator;
bool presenting = false; bool presenting = (vulkanCommandBuffer->presentDataCount > 0);
SDL_LockMutex(renderer->submitLock); SDL_LockMutex(renderer->submitLock);
@@ -10440,6 +10453,15 @@ static bool VULKAN_Submit(
swapchainTextureSubresource); swapchainTextureSubresource);
} }
if (presenting &&
renderer->allocationsToDefragCount > 0 &&
!renderer->defragInProgress) {
if (!VULKAN_INTERNAL_DefragmentMemory(renderer, vulkanCommandBuffer))
{
SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", "Failed to defragment memory, likely OOM!");
}
}
if (!VULKAN_INTERNAL_EndCommandBuffer(renderer, vulkanCommandBuffer)) { if (!VULKAN_INTERNAL_EndCommandBuffer(renderer, vulkanCommandBuffer)) {
SDL_UnlockMutex(renderer->submitLock); SDL_UnlockMutex(renderer->submitLock);
return false; return false;
@@ -10477,11 +10499,7 @@ static bool VULKAN_Submit(
} }
// Present, if applicable // Present, if applicable
bool result = true;
for (Uint32 j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) { for (Uint32 j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) {
presenting = true;
presentData = &vulkanCommandBuffer->presentDatas[j]; presentData = &vulkanCommandBuffer->presentDatas[j];
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
@@ -10519,8 +10537,8 @@ static bool VULKAN_Submit(
(presentData->windowData->frameCounter + 1) % renderer->allowedFramesInFlight; (presentData->windowData->frameCounter + 1) % renderer->allowedFramesInFlight;
} }
// Check if we can perform any cleanups // If presenting, check if we can perform any cleanups
if (presenting) {
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) { for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
vulkanResult = renderer->vkGetFenceStatus( vulkanResult = renderer->vkGetFenceStatus(
renderer->logicalDevice, renderer->logicalDevice,
@@ -10555,24 +10573,15 @@ static bool VULKAN_Submit(
SDL_UnlockMutex(renderer->allocatorLock); SDL_UnlockMutex(renderer->allocatorLock);
} }
// Check pending destroys
VULKAN_INTERNAL_PerformPendingDestroys(renderer); VULKAN_INTERNAL_PerformPendingDestroys(renderer);
// Defrag!
if (
presenting &&
renderer->allocationsToDefragCount > 0 &&
!renderer->defragInProgress) {
result = VULKAN_INTERNAL_DefragmentMemory(renderer);
} }
// Mark command buffer as submitted // Mark command buffer as submitted
// This must happen after defrag, because it will try to acquire new command buffers.
VULKAN_INTERNAL_ReleaseCommandBuffer(vulkanCommandBuffer); VULKAN_INTERNAL_ReleaseCommandBuffer(vulkanCommandBuffer);
SDL_UnlockMutex(renderer->submitLock); SDL_UnlockMutex(renderer->submitLock);
return result; return true;
} }
static bool VULKAN_Cancel( static bool VULKAN_Cancel(
@@ -10599,43 +10608,28 @@ static bool VULKAN_Cancel(
} }
static bool VULKAN_INTERNAL_DefragmentMemory( static bool VULKAN_INTERNAL_DefragmentMemory(
VulkanRenderer *renderer) VulkanRenderer *renderer,
VulkanCommandBuffer *commandBuffer)
{ {
VulkanMemoryAllocation *allocation;
VulkanMemoryUsedRegion *currentRegion;
VulkanBuffer *newBuffer;
VulkanTexture *newTexture;
VkBufferCopy bufferCopy;
VkImageCopy imageCopy;
VulkanCommandBuffer *commandBuffer;
VulkanTextureSubresource *srcSubresource;
VulkanTextureSubresource *dstSubresource;
Uint32 i, subresourceIndex;
renderer->defragInProgress = 1; renderer->defragInProgress = 1;
commandBuffer = (VulkanCommandBuffer *)VULKAN_AcquireCommandBuffer((SDL_GPURenderer *)renderer);
if (commandBuffer == NULL) {
return false;
}
commandBuffer->isDefrag = 1; commandBuffer->isDefrag = 1;
SDL_LockMutex(renderer->allocatorLock); SDL_LockMutex(renderer->allocatorLock);
allocation = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1]; VulkanMemoryAllocation *allocation = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1];
renderer->allocationsToDefragCount -= 1; renderer->allocationsToDefragCount -= 1;
/* For each used region in the allocation /* For each used region in the allocation
* create a new resource, copy the data * create a new resource, copy the data
* and re-point the resource containers * and re-point the resource containers
*/ */
for (i = 0; i < allocation->usedRegionCount; i += 1) { for (Uint32 i = 0; i < allocation->usedRegionCount; i += 1) {
currentRegion = allocation->usedRegions[i]; VulkanMemoryUsedRegion *currentRegion = allocation->usedRegions[i];
if (currentRegion->isBuffer && !currentRegion->vulkanBuffer->markedForDestroy) { if (currentRegion->isBuffer && !currentRegion->vulkanBuffer->markedForDestroy) {
currentRegion->vulkanBuffer->usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; currentRegion->vulkanBuffer->usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
newBuffer = VULKAN_INTERNAL_CreateBuffer( VulkanBuffer *newBuffer = VULKAN_INTERNAL_CreateBuffer(
renderer, renderer,
currentRegion->vulkanBuffer->size, currentRegion->vulkanBuffer->size,
currentRegion->vulkanBuffer->usage, currentRegion->vulkanBuffer->usage,
@@ -10645,6 +10639,7 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
if (newBuffer == NULL) { if (newBuffer == NULL) {
SDL_UnlockMutex(renderer->allocatorLock); SDL_UnlockMutex(renderer->allocatorLock);
SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", "Failed to allocate defrag buffer!");
return false; return false;
} }
@@ -10663,6 +10658,7 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION, VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
newBuffer); newBuffer);
VkBufferCopy bufferCopy;
bufferCopy.srcOffset = 0; bufferCopy.srcOffset = 0;
bufferCopy.dstOffset = 0; bufferCopy.dstOffset = 0;
bufferCopy.size = currentRegion->resourceSize; bufferCopy.size = currentRegion->resourceSize;
@@ -10702,20 +10698,22 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
VULKAN_INTERNAL_ReleaseBuffer(renderer, currentRegion->vulkanBuffer); VULKAN_INTERNAL_ReleaseBuffer(renderer, currentRegion->vulkanBuffer);
} else if (!currentRegion->isBuffer && !currentRegion->vulkanTexture->markedForDestroy) { } else if (!currentRegion->isBuffer && !currentRegion->vulkanTexture->markedForDestroy) {
newTexture = VULKAN_INTERNAL_CreateTexture( VulkanTexture *newTexture = VULKAN_INTERNAL_CreateTexture(
renderer, renderer,
false,
&currentRegion->vulkanTexture->container->header.info); &currentRegion->vulkanTexture->container->header.info);
if (newTexture == NULL) { if (newTexture == NULL) {
SDL_UnlockMutex(renderer->allocatorLock); SDL_UnlockMutex(renderer->allocatorLock);
SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", "Failed to allocate defrag buffer!");
return false; return false;
} }
SDL_GPUTextureCreateInfo info = currentRegion->vulkanTexture->container->header.info; SDL_GPUTextureCreateInfo info = currentRegion->vulkanTexture->container->header.info;
for (subresourceIndex = 0; subresourceIndex < currentRegion->vulkanTexture->subresourceCount; subresourceIndex += 1) { for (Uint32 subresourceIndex = 0; subresourceIndex < currentRegion->vulkanTexture->subresourceCount; subresourceIndex += 1) {
// copy subresource if necessary // copy subresource if necessary
srcSubresource = &currentRegion->vulkanTexture->subresources[subresourceIndex]; VulkanTextureSubresource *srcSubresource = &currentRegion->vulkanTexture->subresources[subresourceIndex];
dstSubresource = &newTexture->subresources[subresourceIndex]; VulkanTextureSubresource *dstSubresource = &newTexture->subresources[subresourceIndex];
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage( VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
renderer, renderer,
@@ -10723,12 +10721,14 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
srcSubresource); srcSubresource);
VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage( VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
renderer, renderer,
commandBuffer, commandBuffer,
VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED,
VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
dstSubresource); dstSubresource);
VkImageCopy imageCopy;
imageCopy.srcOffset.x = 0; imageCopy.srcOffset.x = 0;
imageCopy.srcOffset.y = 0; imageCopy.srcOffset.y = 0;
imageCopy.srcOffset.z = 0; imageCopy.srcOffset.z = 0;
@@ -10780,8 +10780,7 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
SDL_UnlockMutex(renderer->allocatorLock); SDL_UnlockMutex(renderer->allocatorLock);
return VULKAN_Submit( return true;
(SDL_GPUCommandBuffer *)commandBuffer);
} }
// Format Info // Format Info