Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 6 additions & 62 deletions gfx/drivers/d3d12.c
Original file line number Diff line number Diff line change
Expand Up @@ -440,22 +440,6 @@ typedef struct
#endif
uint32_t flags;
int8_t wait_for_vblank;

/* Deferred texture deletion. Instead of Signal + WaitFor-
* SingleObject on every texture unload (which drains the GPU
* pipeline), textures are queued here and freed at the start
* of the next frame after the frame fence confirms all prior
* GPU work has completed.
*
* Single flat list: D3D12's per-frame fence is a full drain
* (Signal + Wait), so after it completes, all previously
* submitted work is done. No per-slot tracking needed. */
struct
{
#define D3D12_DEFERRED_MAX 64
d3d12_texture_t entries[D3D12_DEFERRED_MAX];
unsigned count;
} deferred;
} d3d12_video_t;

#define D3D12_ROLLING_SCANLINE_SIMULATION
Expand Down Expand Up @@ -684,21 +668,6 @@ static void d3d12_release_texture(d3d12_texture_t* texture)
Release(texture->upload_buffer);
}

/* Flush deferred texture deletions.
* Called at the start of d3d12_gfx_frame after the fence wait
* confirms all prior GPU work has completed, and from
* d3d12_gfx_free after the final fence drain. */
static void d3d12_flush_deferred_deletes(d3d12_video_t *d3d12)
{
unsigned i;
unsigned count = d3d12->deferred.count;

for (i = 0; i < count; i++)
d3d12_release_texture(&d3d12->deferred.entries[i]);

d3d12->deferred.count = 0;
}

static DXGI_FORMAT d3d12_get_closest_match(D3D12Device device, D3D12_FEATURE_DATA_FORMAT_SUPPORT* desired)
{
DXGI_FORMAT default_list[] = { desired->Format, DXGI_FORMAT_UNKNOWN };
Expand Down Expand Up @@ -3555,10 +3524,6 @@ static void d3d12_gfx_free(void* data)
}
}

/* Flush all deferred texture deletions now that the
* GPU is fully idle. */
d3d12_flush_deferred_deletes(d3d12);

if (d3d12->flags & D3D12_ST_FLAG_WAITABLE_SWAPCHAINS)
CloseHandle(d3d12->chain.frameLatencyWaitableObject);

Expand Down Expand Up @@ -4772,11 +4737,6 @@ static bool d3d12_gfx_frame(
}
}

/* Flush deferred texture deletions. The fence wait above
* guarantees all prior GPU work has completed, so every
* texture in the deferred list is safe to release. */
d3d12_flush_deferred_deletes(d3d12);

#ifdef HAVE_DXGI_HDR
d3d12_hdr_enable = (d3d12->flags & D3D12_ST_FLAG_HDR_ENABLE) ? true : false;
{
Expand Down Expand Up @@ -6273,32 +6233,16 @@ static void d3d12_gfx_unload_texture_internal(

if (d3d12)
{
unsigned count = d3d12->deferred.count;

if (count < D3D12_DEFERRED_MAX)
{
/* Copy the D3D12 resource handles into the deferred
* list. They will be released at the start of the
* next frame after the fence confirms the GPU is done. */
d3d12->deferred.entries[count] = *texture;
d3d12->deferred.count = count + 1;
}
else
D3D12Fence fence = d3d12->queue.fence;
d3d12->queue.handle->lpVtbl->Signal(d3d12->queue.handle, fence, ++d3d12->queue.fenceValue);
if (fence->lpVtbl->GetCompletedValue(fence) < d3d12->queue.fenceValue)
{
/* Overflow: fall back to synchronous drain.
* Extremely rare — requires 64+ texture unloads
* between two consecutive frames. */
D3D12Fence fence = d3d12->queue.fence;
d3d12->queue.handle->lpVtbl->Signal(d3d12->queue.handle, fence, ++d3d12->queue.fenceValue);
if (fence->lpVtbl->GetCompletedValue(fence) < d3d12->queue.fenceValue)
{
fence->lpVtbl->SetEventOnCompletion(fence, d3d12->queue.fenceValue, d3d12->queue.fenceEvent);
WaitForSingleObject(d3d12->queue.fenceEvent, INFINITE);
}
d3d12_release_texture(texture);
fence->lpVtbl->SetEventOnCompletion(fence, d3d12->queue.fenceValue, d3d12->queue.fenceEvent);
WaitForSingleObject(d3d12->queue.fenceEvent, INFINITE);
}
}

d3d12_release_texture(texture);
free(texture);
}

Expand Down
99 changes: 11 additions & 88 deletions gfx/drivers/vulkan.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,21 +339,6 @@ typedef struct vk
bool dirty[VULKAN_MAX_SWAPCHAIN_IMAGES];
} menu;

/* Deferred texture deletion. Instead of calling vkQueueWaitIdle
* on every texture unload (which stalls the entire GPU pipeline),
* we tag textures for deletion and free them when the frame fence
* confirms the GPU has finished the frame that was using them.
*
* Per-slot lists: slot index = current_frame_index at the time
* of unload. Flushed at the start of vulkan_frame after the
* fence wait for that slot. */
struct
{
#define VK_DEFERRED_MAX 64
struct vk_texture entries[VULKAN_MAX_SWAPCHAIN_IMAGES][VK_DEFERRED_MAX];
unsigned count[VULKAN_MAX_SWAPCHAIN_IMAGES];
} deferred;

struct
{
VkSampler linear;
Expand Down Expand Up @@ -1023,24 +1008,6 @@ static void vulkan_destroy_texture(
tex->layout = VK_IMAGE_LAYOUT_UNDEFINED;
}

/* Flush deferred texture deletions for a given frame slot.
* Called at the start of vulkan_frame after the fence wait
* guarantees all GPU work from the previous use of this slot
* has completed. Also called from deinit after vkDeviceWaitIdle. */
static void vulkan_flush_deferred_deletes(vk_t *vk, unsigned slot)
{
unsigned i;
unsigned count = vk->deferred.count[slot];

for (i = 0; i < count; i++)
{
vulkan_destroy_texture(
vk->context->device,
&vk->deferred.entries[slot][i]);
}
vk->deferred.count[slot] = 0;
}

static struct vk_texture vulkan_create_texture(vk_t *vk,
struct vk_texture *old,
unsigned width, unsigned height,
Expand Down Expand Up @@ -4647,15 +4614,6 @@ static void vulkan_free(void *data)
#ifdef HAVE_THREADS
slock_unlock(vk->context->queue_lock);
#endif

/* Flush all deferred texture deletions now that the
* GPU is fully idle. */
{
unsigned s;
for (s = 0; s < VULKAN_MAX_SWAPCHAIN_IMAGES; s++)
vulkan_flush_deferred_deletes(vk, s);
}

vulkan_deinit_pipelines(vk);
vulkan_deinit_framebuffers(vk);
vulkan_deinit_descriptor_pool(vk);
Expand Down Expand Up @@ -5162,15 +5120,6 @@ static void vulkan_check_swapchain(vk_t *vk)
#ifdef HAVE_THREADS
slock_unlock(vk->context->queue_lock);
#endif

/* Flush all deferred texture deletions before
* tearing down pipelines and framebuffers. */
{
unsigned s;
for (s = 0; s < VULKAN_MAX_SWAPCHAIN_IMAGES; s++)
vulkan_flush_deferred_deletes(vk, s);
}

vulkan_deinit_pipelines(vk);
vulkan_deinit_framebuffers(vk);
vulkan_deinit_descriptor_pool(vk);
Expand Down Expand Up @@ -6303,12 +6252,6 @@ static bool vulkan_frame(void *data, const void *frame,
vk->context->current_swapchain_index;
bool overlay_behind_menu = video_info->overlay_behind_menu;

/* Flush deferred texture deletions for this frame slot.
* The fence for this slot was already waited on in
* vulkan_acquire_wait_fences, so all GPU work from the
* previous use of this slot has completed. */
vulkan_flush_deferred_deletes(vk, frame_index);

/* Fast toggle shader filter chain logic */
filter_chain = vk->filter_chain;

Expand Down Expand Up @@ -7604,41 +7547,21 @@ static void vulkan_unload_texture_internal(vk_t *vk, uintptr_t handle)
if (!texture || !vk || !vk->context)
return;

{
unsigned slot = vk->context->current_frame_index;
unsigned count = vk->deferred.count[slot];

if (count < VK_DEFERRED_MAX)
{
/* Copy the Vulkan resource handles into the deferred
* slot. They will be destroyed at the start of the
* frame that reuses this slot, after the fence confirms
* the GPU is done. */
vk->deferred.entries[slot][count] = *texture;
vk->deferred.count[slot] = count + 1;
}
else
{
/* Overflow: fall back to synchronous wait.
* This should be extremely rare — it requires more
* than VK_DEFERRED_MAX texture unloads in a single
* frame. */
/* TODO: We really want to defer this deletion instead,
* but this will do for now. */
#ifdef HAVE_THREADS
if (vk->context->queue_lock)
slock_lock(vk->context->queue_lock);
if (vk->context->queue_lock)
slock_lock(vk->context->queue_lock);
#endif
if (vk->context->queue)
vkQueueWaitIdle(vk->context->queue);
if (vk->context->queue)
vkQueueWaitIdle(vk->context->queue);
#ifdef HAVE_THREADS
if (vk->context->queue_lock)
slock_unlock(vk->context->queue_lock);
if (vk->context->queue_lock)
slock_unlock(vk->context->queue_lock);
#endif
if (vk->context->device)
vulkan_destroy_texture(
vk->context->device, texture);
}
}

if (vk->context->device)
vulkan_destroy_texture(
vk->context->device, texture);
free(texture);
}

Expand Down
28 changes: 16 additions & 12 deletions menu/menu_displaylist.c
Original file line number Diff line number Diff line change
Expand Up @@ -8189,8 +8189,7 @@ unsigned menu_displaylist_build_list(
switch (build_list[i].enum_idx)
{
case MENU_ENUM_LABEL_MENU_ALLOW_TABS_BACK:
if (!strcmp(menu_driver, "rgui"))
build_list[i].checked = false;
build_list[i].checked = (!strcmp(menu_driver, "rgui"));
break;
default:
break;
Expand Down Expand Up @@ -9684,17 +9683,22 @@ unsigned menu_displaylist_build_list(
#if defined(HAVE_CHEEVOS) && defined(HAVE_GFX_WIDGETS)
for (i = 0; i < ARRAY_SIZE(build_list); i++)
{
if (!gfx_widgets)
build_list[i].checked = false;
else if (!cheevos_autopad)
switch (build_list[i].enum_idx)
{
if (build_list[i].enum_idx == MENU_ENUM_LABEL_CHEEVOS_APPEARANCE_PADDING_V)
build_list[i].checked = true;

if (build_list[i].enum_idx == MENU_ENUM_LABEL_CHEEVOS_APPEARANCE_PADDING_H &&
!(cheevos_anchor == CHEEVOS_APPEARANCE_ANCHOR_TOPCENTER ||
cheevos_anchor == CHEEVOS_APPEARANCE_ANCHOR_BOTTOMCENTER))
build_list[i].checked = true;
case MENU_ENUM_LABEL_CHEEVOS_APPEARANCE_ANCHOR:
case MENU_ENUM_LABEL_CHEEVOS_APPEARANCE_PADDING_AUTO:
build_list[i].checked = gfx_widgets;
break;
case MENU_ENUM_LABEL_CHEEVOS_APPEARANCE_PADDING_V:
build_list[i].checked = (gfx_widgets && !cheevos_autopad);
break;
case MENU_ENUM_LABEL_CHEEVOS_APPEARANCE_PADDING_H:
build_list[i].checked = ((gfx_widgets && !cheevos_autopad) &&
!(cheevos_anchor == CHEEVOS_APPEARANCE_ANCHOR_TOPCENTER ||
cheevos_anchor == CHEEVOS_APPEARANCE_ANCHOR_BOTTOMCENTER));
break;
default:
break;
}
}
#endif
Expand Down
3 changes: 2 additions & 1 deletion tasks/task_database.c
Original file line number Diff line number Diff line change
Expand Up @@ -1984,7 +1984,8 @@ static void task_manual_content_scan_handler(retro_task_t *task)
}

/* Open playlist */
if (!(manual_scan->playlist =
if (manual_scan->task_config->target_is_single_determined_playlist &&
!(manual_scan->playlist =
playlist_init(&manual_scan->playlist_config)))
goto task_finished;

Expand Down
Loading