mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-01 02:05:21 +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.
68 lines
2.7 KiB
C++
68 lines
2.7 KiB
C++
#pragma once
|
|
|
|
#include <vector>
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
|
|
// Must come before <libretro_vulkan.h>: this is what defines VK_USE_PLATFORM_WIN32_KHR (etc.) before
|
|
// the first inclusion of ext/vulkan/vulkan.h - since that header is include-guarded, whichever include
|
|
// reaches it first determines whether the platform-specific declarations (e.g.
|
|
// PFN_vkCreateWin32SurfaceKHR) exist for the rest of this translation unit.
|
|
#include "Common/GPU/Vulkan/VulkanPresentation.h"
|
|
|
|
#define VK_NO_PROTOTYPES
|
|
#include <libretro_vulkan.h>
|
|
|
|
// Implements VulkanContext's pluggable presentation backend (see VulkanPresentation.h) against
|
|
// libretro's retro_hw_render_interface_vulkan: instead of a real VK_KHR_swapchain (which doesn't exist in
|
|
// this model - RetroArch owns the real presentation surface itself), PPSSPP renders into its own rotating
|
|
// set of VkImages, sized to match RetroArch's sync index mask, and hands each finished frame back via
|
|
// set_image() rather than vkQueuePresentKHR.
|
|
class LibretroVulkanPresentation : public VulkanPresentation {
|
|
public:
|
|
// vulkan must remain valid for the lifetime of this object - it's owned by RetroArch, not us.
|
|
LibretroVulkanPresentation(retro_hw_render_interface_vulkan *vulkan, VkFormat format, VkExtent2D extent);
|
|
|
|
bool Create(VulkanContext *context);
|
|
void Destroy(VulkanContext *context) override;
|
|
|
|
VkResult AcquireNextImage(VulkanContext *vulkan, VkSemaphore signalSemaphore, uint32_t *imageIndex) override;
|
|
VkResult QueuePresent(VulkanContext *vulkan, VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore) override;
|
|
|
|
uint32_t GetImageCount() const override { return (uint32_t)images_.size(); }
|
|
VkImage GetImage(uint32_t index) const override { return images_[index].image; }
|
|
VkExtent2D GetExtent() const override { return extent_; }
|
|
VkFormat GetFormat() const override { return format_; }
|
|
|
|
VkImageLayout GetPresentLayout() const override { return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; }
|
|
|
|
void LockQueue() override;
|
|
void UnlockQueue() override;
|
|
// RetroArch's shared VkQueue model doesn't let semaphores cross the frontend/core boundary
|
|
// meaningfully - strip them before the real vkQueueSubmit.
|
|
void PrepareSubmit(VkSubmitInfo &submitInfo) override;
|
|
|
|
// Called from LibretroVulkanContext::SwapBuffers(), mirroring the old free-function
|
|
// vk_libretro_wait_for_presentation().
|
|
void WaitForPresentation();
|
|
|
|
private:
|
|
struct Image {
|
|
VkImage image = VK_NULL_HANDLE;
|
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
|
retro_vulkan_image retroImage{};
|
|
};
|
|
|
|
retro_hw_render_interface_vulkan *vulkan_;
|
|
VkFormat format_;
|
|
VkExtent2D extent_;
|
|
bool dedicatedAllocation_ = false;
|
|
|
|
std::vector<Image> images_;
|
|
|
|
std::mutex mutex_;
|
|
std::condition_variable condVar_;
|
|
int currentIndex_ = -1;
|
|
bool everPresented_ = false;
|
|
};
|