mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-08-31 09:45:24 +02:00
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.
46 lines
2.2 KiB
C++
46 lines
2.2 KiB
C++
#pragma once
|
|
|
|
#include "Common/GPU/Vulkan/VulkanLoader.h"
|
|
|
|
class VulkanContext;
|
|
|
|
// Pluggable replacement for VulkanContext's normal real-swapchain path (InitSwapchain()/GetSwapchain()),
|
|
// for host environments that hand PPSSPP a way to render frames without a real VK_KHR_swapchain - for
|
|
// example a libretro frontend, which owns the actual presentation surface itself and instead wants each
|
|
// finished frame handed back through its own callback.
|
|
//
|
|
// When VulkanContext::GetPresentation() is null (the default), every consumer of this interface falls back
|
|
// to the normal real-swapchain code path unchanged. Setting a VulkanPresentation via
|
|
// VulkanContext::SetPresentation() opts into this interface instead.
|
|
class VulkanPresentation {
|
|
public:
|
|
virtual ~VulkanPresentation() {}
|
|
|
|
virtual void Destroy(VulkanContext *vulkan) = 0;
|
|
|
|
// Mirrors vkAcquireNextImageKHR: waits for the next image to become available, and signals
|
|
// signalSemaphore once it's safe to render into it.
|
|
virtual VkResult AcquireNextImage(VulkanContext *vulkan, VkSemaphore signalSemaphore, uint32_t *imageIndex) = 0;
|
|
// Mirrors vkQueuePresentKHR: hands the finished image back, waiting on waitSemaphore first.
|
|
virtual VkResult QueuePresent(VulkanContext *vulkan, VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore) = 0;
|
|
|
|
virtual uint32_t GetImageCount() const = 0;
|
|
virtual VkImage GetImage(uint32_t index) const = 0;
|
|
virtual VkExtent2D GetExtent() const = 0;
|
|
virtual VkFormat GetFormat() const = 0;
|
|
|
|
// The layout backbuffer images should be left in after rendering, since there's no real presentation
|
|
// engine to hand them to via a VK_IMAGE_LAYOUT_PRESENT_SRC_KHR transition.
|
|
virtual VkImageLayout GetPresentLayout() const = 0;
|
|
|
|
// Some hosts (e.g. libretro) share a single VkQueue between the frontend and the core, and require
|
|
// exclusive access to be taken around every vkQueueSubmit/vkQueueWaitIdle. No-ops by default.
|
|
virtual void LockQueue() {}
|
|
virtual void UnlockQueue() {}
|
|
|
|
// Called right before every real vkQueueSubmit, in case the backend needs to adjust it - for example,
|
|
// a host that doesn't let semaphores cross the frontend/core boundary meaningfully may need to strip
|
|
// them out here. No-op by default.
|
|
virtual void PrepareSubmit(VkSubmitInfo &submitInfo) {}
|
|
};
|