Commit Graph
140 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 9f1278b5d6 iOS: Link MoltenVK statically in sideload builds too, raise target to 15.0
Sideload builds used to dlopen a bundled libMoltenVK.dylib, which was
only worth having while they could still fall back to OpenGL. Now both
variants link the static MoltenVK from ios/MoltenVK and call Vulkan
directly. PPSSPP_VK_STATIC replaces the IOS_APP_STORE checks in the
Vulkan loader (the iOS libretro core still loads at runtime).

The static MoltenVK needs iOS 15.0, so that's now the deployment target
for all iOS builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:28 +02:00
Henrik RydgårdandClaude Opus 5.5 1fe8071875 Vulkan: Apply the texture LOD bias in the shader where samplers ignore it
MoltenVK doesn't support a sampler mipLodBias (the portability subset's
samplerMipLodBias is false), so the PSP's LOD bias did nothing on Mac and
iOS, and biased textures came out a mip level blurrier than on OpenGL and
the PSP: MLB's signs and crowd, Burnout Legends, MediEvil, Ridge Racer 2.
Query the portability subset, and where the bias is missing, pass the auto
mode bias to texture() from a uniform instead.

VK_ENABLE_BETA_EXTENSIONS, which declares the portability subset, is now
defined for App Store iOS builds too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:04:23 +02:00
Henrik RydgårdandClaude Fable 5.1 7f751c7ba1 Vulkan: Don't recreate the swapchain over a pre-transform we chose ourselves
InitSwapchain deliberately refuses the 270 degree pre-transform. The driver
then reports VK_SUBOPTIMAL_KHR on every present, which we counted towards
recreating the swapchain - and the new one got the same treatment, so in
reverse landscape we rebuilt it every single frame.

Remember that the mismatch is ours and stop treating SUBOPTIMAL as a reason
to recreate when it is. Any other cause still counts.

Split out of the Android rotation work, where it was found: it's a
platform-independent Vulkan fix and stands on its own.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
2026-10-08 17:15:09 +02:00
Henrik Rydgård d3ab52b4b3 Buildfix 2026-09-28 17:05:44 -06:00
Henrik RydgårdandClaude Opus 5.5 8b4e93021e Vulkan: Cache compiled SPIR-V so shaders skip glslang on later runs
GLSLtoSPV takes an optional SPIRVCache, keyed on a 32-bit hash of the
source, stage and variant, plus the source length. A changed shader
simply misses. thin3d's shaders and the other fixed ones use a global
cache in PSP/SYSTEM/CACHE/vulkan_spirv.cache, loaded on first use and
saved after graphics init, when a game's cache is saved, and at
shutdown; it's flushed once it reaches 32 entries, about twice what a
session compiles, so outdated ones don't pile up. Game shaders keep
theirs in the .vkshadercache, ahead of the shader IDs so that the
compiles on load find it (version 60), and only what the session used
is saved.

A cold glslang costs about 40ms before its first shader here, and
0.3-0.9ms per shader after that.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:03:52 -06:00
Henrik Rydgård de0dc2d7d4 Remove the leftover geometry shader scaffolding
Nothing has generated or used a geometry shader since the GS paths were removed
- GeometryShaderGenerator is gone, and ShaderWriter's BeginGSMain/EndGSMain had
no callers at all. Removes ShaderStage::Geometry and everything hanging off it:
the GS preambles and GSMain helpers in ShaderWriter, the stage mappings in all
three thin3d backends, the D3D11 geometry shader plumbing (curGS_, the pipeline
and module members, gs_4_0 compilation), CreateGeometryShaderD3D11, the unused
PipelineFlags::USES_GEOMETRY_SHADER and PipelineManagerVulkan's
UsesGeometryShader().

Also stop enabling the Vulkan geometryShader device feature, since we no longer
have any use for it.

Kept on purpose: the device feature is still listed in the feature dumps (like
other capabilities we don't use), and the Vulkan shader cache header keeps its
now-always-zero geometry shader count so the on-disk format stays compatible.
2026-09-03 11:24:03 -06:00
Henrik Rydgård f7c2c55e83 Vulkan: Report surface init failures instead of asserting later
VulkanGraphicsContext::InitSurface() threw away VulkanContext::InitSurface()'s
VkResult and carried on, so a failed surface init surfaced as
_dbg_assert_(GetAvailablePresentModes().size() > 0) in the VKContext
constructor rather than as a graphics error with the usual backend fallback.
The vkCreate*SurfaceKHR failure path in ReinitSurface() didn't log anything
either, so the assert was the only trace of it.

Now ReinitSurface() logs and sets init_error_ for all three ways it can bail
(surface creation, ChooseQueue, present mode enumeration), InitSurface()
checks the result, and MainThreadFunc() passes the message back out instead of
writing it to a local it then drops - Windows/main.cpp was reporting
"Failed to initialize main thread function." to the user.

Also deletes the Application on that failure path, which was leaked.
2026-08-30 01:18:37 +02:00
Henrik RydgårdandClaude Opus 5 14ce62af9e Vulkan: Four small correctness fixes found while reviewing Common/GPU
* TransitionDepthStencilImageAuto set dstAccessMask to TRANSFER_READ_BIT for
  TRANSFER_DST_OPTIMAL. The color path and this function's own source-side switch
  both use TRANSFER_WRITE_BIT - it's a copy-paste from the TRANSFER_SRC case two
  lines up. Every depth copy and blit went through it.

* VulkanMayBeAvailable's per-device loop did anyGood = !blacklisted, overwriting
  the verdict from earlier devices, so a blacklisted GPU enumerated after a good
  one hid the Vulkan backend entirely. Hybrid-GPU machines are exactly what the
  blacklist targets.

* The instance extension scan stopped as soon as it found the platform surface
  extension, so a driver reporting that before VK_KHR_surface made us give up
  with "Platform surface extension not found". Enumeration order isn't specified.

* CreateDevice only logged when vkCreateDevice failed, then carried on to report
  success, call VulkanSetAvailable(true) and build a VMA allocator on a null
  device behind an assert that's live in release builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vd8ntC2brCUtCrDJMqLbs8
2026-08-28 23:27:13 +02:00
Henrik RydgårdandClaude Opus 5 f9315e9bc1 Vulkan: Keep draining the delete list until a lap comes up empty
The previous commit moved everything out of the list before running callbacks, to
avoid appending to a vector being iterated. That regressed device teardown: a
callback can queue more deletes (~VKFramebuffer does, via ~VKRFramebuffer, which
queues image views, image allocations and framebuffers), and those land back on a
list that used to be picked up by the object loops later in the same pass.

That's harmless for the per-frame lists, since callbacks queue onto the global
list and a later frame drains it. But PerformPendingDeletes() drains the global
list itself, and DestroyDevice() calls it immediately before vmaDestroyAllocator
and vkDestroyDevice - so the re-queued objects were never destroyed at all.

Loop instead. In the per-frame case that's one extra empty lap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vd8ntC2brCUtCrDJMqLbs8
2026-08-28 22:56:18 +02:00
Henrik RydgårdandClaude Opus 5 6862bc1721 Vulkan: Fix threading issues around pipeline layouts and the delete list
pipelineLayouts_ was mutated from the main thread (CreatePipelineLayout, and the
deferred callback queued by DestroyPipelineLayout) while the render thread walked
it every frame in FlushDescriptors. Exiting a game in Vulkan mode hits this
reliably: ~GPU_Vulkan stops the render thread and destroys the draw engine's
layout, but the destruction is deferred onto the delete list and doesn't actually
run until a BeginFrame two frames later, with the render thread running again.
Guard the list, and the lifetime of the layouts in it, with a mutex.

The global delete list had the same problem - VulkanDescSetPool::Recreate queues
the old pool from FlushDescSets on the render thread, which happens for real once
a game goes past the initial 1024 descriptors, while the main thread moves the
list into the current frame's list in EndFrame(). Lock the queueing functions and
Take's source list.

While in there:
* Take() didn't move queryPools_, so query pools queued for deletion sat on the
  global list until device teardown instead of being deleted a few frames later.
* PerformDeletes now drains into a local list before destroying anything. A
  callback is allowed to queue further deletes (~VKFramebuffer's does, via
  ~VKRFramebuffer), which used to append to the very vector being iterated.
  They now get the normal deferral instead of running in the same pass.
* Missing semicolon in BeginFrame that only compiles because VLOG is empty.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vd8ntC2brCUtCrDJMqLbs8
2026-08-28 22:43:58 +02:00
Henrik Rydgård ed1956b5fb Merge pull request #22035 from hrydgard/libretro-vulkan-cleanup
Vulkan: remove libretro's global function-pointer wrapper hack - by Claude AI
2026-08-06 18:50:20 +02:00
Henrik Rydgård 4160414b31 Vulkan: remove libretro's global function-pointer wrapper hack
libretro/libretro_vulkan.cpp got PPSSPP's libretro core working with
RetroArch's Vulkan integration by globally monkey-patching PPSSPP's Vulkan
loader function pointers (vkCreateInstance, vkCreateDevice,
vkCreateSwapchainKHR, vkAcquireNextImageKHR, vkQueuePresentKHR,
vkQueueSubmit, etc.) so the unmodified VulkanContext class would end up
wrapping RetroArch's already-existing VkInstance/VkDevice instead of
creating its own, and so a fake VkSwapchainKHR (a self-managed array of
images synced against RetroArch's retro_hw_render_interface_vulkan
callbacks) could stand in for the real swapchain that libretro's Vulkan
model doesn't have. Flagged in-code as "a wacky wrapper".

Replaces that with first-class support in VulkanContext for the two things
libretro actually needs:

- Adopting an externally-created instance/device instead of faking
  vkCreateInstance/vkCreateDevice: VulkanContext::CreateInstanceExternal()
  adopts RetroArch's VkInstance; CreateDevice() gained optional
  extraDeviceExtensions/extraRequiredFeatures params so RetroArch's
  requirements get merged into a real vkCreateDevice() call;
  ownsInstance_/ownsDevice_ flags (the latter set via
  SetDeviceExternallyOwned()) mean DestroyInstance()/DestroyDevice() skip
  the real vkDestroy* calls when something else owns the object, without
  needing to intercept anything. VulkanLoader gained
  VulkanLoadFromGetInstanceProcAddr() for bootstrapping from a
  host-supplied proc-addr getter instead of dlopen/dlsym-ing the loader
  ourselves - vkGetDeviceProcAddr is resolved via the real instance handle
  (not NULL), since per the Vulkan spec it's not one of the handful of
  commands queryable with a NULL instance.
- A pluggable presentation backend (Common/GPU/Vulkan/VulkanPresentation.h)
  for hosts with no real VK_KHR_swapchain, replacing the fake-swapchain-
  handle trick. VulkanContext::GetPresentation() is null by default, so
  every existing platform's real-swapchain code path is untouched;
  libretro/LibretroVulkanPresentation implements this interface directly
  against retro_hw_render_interface_vulkan, as real class state instead of
  file-scope globals. Several pieces of state that are normally only
  populated as a side effect of ReinitSurface()/InitSwapchain() - the
  graphics queue/queue family index (ChooseQueue() is entangled with
  real-surface presentation-support checks), the swapchain format, and
  the available present modes - needed presentation-aware fallbacks since
  libretro never calls that real-surface path at all.

libretro/LibretroVulkanContext.cpp now drives VulkanContext's real, public
API directly - no more hijacked function pointers, no more fake surface or
swapchain. libretro/libretro_vulkan.cpp is deleted.

Verified with a full build+run in RetroArch (not just compile-time
checks): the libretro Makefile doesn't track header dependencies
(cl.exe doesn't support -MMD/-MP, and Makefile.common never sets up an
equivalent), so a `make clean` full rebuild is required after any header
change to avoid linking stale object code from before the change - several
of the fixes above were initially masked by exactly that.
2026-08-06 17:07:05 +02:00
Henrik Rydgård c490d4ef36 Headless SDL: Add support for Vulkan rendering 2026-08-06 11:06:03 +02:00
Katharine Chui c39205496c bring back VulkanContext::SetCbGetDrawSize for linux wayland
It was removed during https://github.com/hrydgard/ppsspp/pull/21982

On Linux Wayland however this can be observed without:
34:25:550 Vulkan/VulkanContext.cpp:1401 I[G3D]: surfCapabilities_.current: -1x-1
34:25:550 Vulkan/VulkanContext.cpp:1406 I[G3D]: surfCapabilities_.current after clamp: -1x-1 min: 1x1 max: 32768x32768 computed: 1x1 cbdraw
2026-08-06 00:29:21 +02:00
Henrik Rydgård 1c40facc4c Vulkan SDL: Remove an old workaround 2026-07-26 19:10:46 +02:00
Henrik Rydgård 7f218acc53 Change the GraphicsContext interface to be more consistent 2026-07-26 10:22:00 +02:00
Henrik Rydgård c1ceea1405 Correct Vulkan initialization for devices that don't support Vulkan 1.1. 2026-07-15 11:57:27 +02:00
Henrik Rydgård 7b3405ab86 Fix a Vulkan lifetime issue causing invalid descriptors on shutdown
Also simplifies the code a little
2026-06-25 21:56:33 +02:00
Henrik Rydgård 928cdea420 Fix vulkan validation error after #21616
See #21616
2026-06-05 10:09:23 +02:00
jasaaved 5e25118043 Fix the exclusive fullscreen config guard and switching the setting on/off
Previously the exclusive fullscreen option in the settings screen was
guarded by a compile-time #ifdef VK_EXT_full_screen_exclusive, which is
always true on Windows. It now uses a runtime fullScreenExclusiveSupported
cap derived from whether the extension is actually enabled by the driver.

Switching between borderless and exclusive fullscreen currently requires a
restart. This is because once DWM has claimed the window (any frames
presented), ALLOWED_EXT cannot re-engage exclusive mode on an existing
swapchain. A new process gets a fresh HWND that DWM has not claimed yet,
so ALLOWED_EXT works correctly at startup.

The proper fix is to use VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT
with vkAcquireFullScreenExclusiveModeEXT/vkReleaseFullScreenExclusiveModeEXT
to explicitly negotiate exclusive mode with DWM mid-session without needing
a restart. To be done in a follow-up.
2026-06-03 17:15:13 -07:00
jasaaved 0c23cdaf3d Windows Vulkan: default to borderless fullscreen, add exclusive option
Vulkan swapchain now explicitly sets VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT by default, enabling proper borderless fullscreen behavior with benefits including better Alt+Tab, VRR/G-Sync/FreeSync, and Auto HDR support.
2026-06-03 17:15:13 -07:00
Henrik Rydgård 85ddf03c4b Vulkan: Remove deprecated use of device layers 2026-05-13 23:09:04 +02:00
Henrik Rydgård 4c2be7f8af More work. Some initialization order problem. 2026-04-23 13:31:51 +02:00
Henrik Rydgård 317f811ca2 Slight improvement to our terrible GPU performance heuristic 2026-04-07 11:39:39 -06:00
Henrik Rydgård 845f9f3d55 Add missing check to Vulkan init. Disable validation on android-x86-64
The latter is broken in the Android SDK emulator.
2026-02-27 23:57:50 +01:00
Henrik Rydgård 236a0acf71 Minor tweaks 2026-02-06 11:37:09 +01:00
Henrik Rydgård 68a8ba856f Warning fix, delete unused code 2026-01-24 10:43:48 +01:00
Henrik Rydgård 042cf87248 Rework the present mode settings, refactor. 2025-10-20 21:28:38 +02:00
Henrik Rydgård b4f7635719 Vulkan: Check the available present modes right after initializing the surface 2025-10-20 19:52:24 +02:00
Henrik Rydgård 80bd32325c Move the present mode from the CreateInfo to InitSwapchain 2025-10-20 16:33:34 +02:00
Henrik Rydgård 6338832015 Vulkan: Smoothly recreate the swapchain using oldSwapchain on "resize" on Windows 2025-10-20 15:42:34 +02:00
Henrik Rydgård 92f5db7445 Remove further references to the fixed ui_atlas. CMakeLists.txt improvements. 2025-09-17 09:56:17 -06:00
Henrik Rydgård 2cf0ffea73 Allow choosing the frame presentation mode in detail in Vulkan mode 2025-08-21 01:01:16 +02:00
Henrik Rydgård c0b7ecc686 Refactor: Unify the Vulkan init info logic 2025-08-21 01:01:16 +02:00
Henrik Rydgård 5e05a6d0ac Get rid of some more backward Common/Core dependencies 2025-08-06 00:16:35 +02:00
Henrik Rydgård 9137eedb7b Windows/Vulkan: Correctly handle minimizing and restoring the window
We can't create a swapchain in this case but we still want emulation to
keep running. So we ditch the last renderpass in VulkanQueueRunner but
run all the rest (if PauseWhenMinimized isn't set).

Now works without any validation errors or hangs.
2025-07-18 20:01:48 +02:00
Henrik Rydgård a36d3cf18d Logging improvements / despam 2025-05-21 21:42:08 +02:00
Henrik Rydgård 7792e01545 Vulkan: RenderCompleteSemaphores are now per swapchain image, instead of per-buffered-frame. 2025-05-20 11:39:08 +02:00
Henrik Rydgård 50b604e010 Log fixes 2025-04-15 14:29:40 +02:00
Henrik Rydgård 6eefe8c982 Vulkan init: Slight logging reduction 2025-04-13 15:21:29 +02:00
Henrik Rydgård 67dfbfe97e Add developer setting to prevent loading overlays (and other 'implicit' Vulkan layers) 2025-04-10 18:56:05 +02:00
Henrik Rydgård 670ff21529 Make the Vulkan init flags a proper enum class 2025-04-10 18:16:05 +02:00
Henrik Rydgård 85304d58a8 Warning fixes 2025-03-27 14:45:42 +01:00
Henrik Rydgård 948c38a034 Some more minor fixes 2025-02-04 10:24:55 -06:00
Henrik Rydgård 6e0cafccb9 Request Vulkan 1.4 if available. 2025-01-17 14:27:08 +01:00
Nukem d3aeff861e libretro: Restore Vulkan MSAA video option 2025-01-10 16:13:16 -05:00
Henrik Rydgård f5a4669cb4 Remove obsolete ifdef 2024-11-07 10:17:56 +01:00
Henrik Rydgård 8c8b34aac4 Vulkan: Separate the instance and device API versions, for extension loading purposes 2024-09-25 18:38:31 +02:00
Henrik Rydgård b2d9ac54dd Make InfoField an "enum class", extract function FormatAPIVersion 2024-09-25 16:34:33 +02:00
Henrik Rydgård 144b3a8a98 Vulkan validation error cleanups 2024-07-28 08:08:48 -06:00