diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 1a2ce787a47..856da1b51f5 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -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 @@ -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 }; @@ -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); @@ -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; { @@ -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); } diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 3a34b8b85e3..25c6657d749 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -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; @@ -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, @@ -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); @@ -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); @@ -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; @@ -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); } diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 0d9d1970bff..435d79e95b1 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -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; @@ -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 diff --git a/tasks/task_database.c b/tasks/task_database.c index 8128ff17b42..95a4312ba54 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -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;