mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
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.
This commit is contained in:
1 parent
35f558d677
commit
4160414b31
23 files changed
+609
-574
No files matched your search
@@ -5,7 +5,7 @@ set(LIBRETRO_SRCS
|
||||
LibretroGLContext.cpp
|
||||
LibretroGLCoreContext.cpp
|
||||
LibretroVulkanContext.cpp
|
||||
libretro_vulkan.cpp)
|
||||
LibretroVulkanPresentation.cpp)
|
||||
|
||||
if(WIN32)
|
||||
set(LIBRETRO_SRCS ${LIBRETRO_SRCS}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/GPU/Vulkan/VulkanLoader.h"
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
@@ -10,24 +12,28 @@
|
||||
#include "Common/Data/Text/Parsers.h"
|
||||
|
||||
#include "libretro/LibretroVulkanContext.h"
|
||||
#include "libretro/LibretroVulkanPresentation.h"
|
||||
#include <libretro_vulkan.h>
|
||||
#include <GPU/Vulkan/VulkanRenderManager.h>
|
||||
#include "ext/glslang/OGLCompilersDLL/InitializeDll.h"
|
||||
|
||||
#undef fflush
|
||||
|
||||
static VulkanContext *vk;
|
||||
using namespace PPSSPP_VK;
|
||||
|
||||
void vk_libretro_init(VkInstance instance, VkPhysicalDevice gpu, VkSurfaceKHR surface, PFN_vkGetInstanceProcAddr get_instance_proc_addr, const char **required_device_extensions, unsigned num_required_device_extensions, const char **required_device_layers, unsigned num_required_device_layers, const VkPhysicalDeviceFeatures *required_features);
|
||||
void vk_libretro_shutdown();
|
||||
void vk_libretro_set_hwrender_interface(retro_hw_render_interface *hw_render_interface);
|
||||
void vk_libretro_wait_for_presentation();
|
||||
static VulkanContext *vk;
|
||||
// Non-owning: lifetime is tied to vk (owns it via VulkanContext::presentation_).
|
||||
static LibretroVulkanPresentation *presentation;
|
||||
// Fetched in ContextReset(), consumed when CreateDrawContext() constructs the presentation backend.
|
||||
static retro_hw_render_interface_vulkan *hwRenderInterface;
|
||||
|
||||
LibretroVulkanContext::LibretroVulkanContext()
|
||||
: LibretroHWRenderContext(RETRO_HW_CONTEXT_VULKAN, VK_MAKE_VERSION(1, 0, 18)) {}
|
||||
|
||||
void LibretroVulkanContext::SwapBuffers() {
|
||||
vk_libretro_wait_for_presentation();
|
||||
if (presentation) {
|
||||
presentation->WaitForPresentation();
|
||||
}
|
||||
LibretroHWRenderContext::SwapBuffers();
|
||||
}
|
||||
|
||||
@@ -36,37 +42,72 @@ static bool create_device(retro_vulkan_context *context, VkInstance instance, Vk
|
||||
|
||||
vk = new VulkanContext();
|
||||
|
||||
vk_libretro_init(instance, gpu, surface, get_instance_proc_addr, required_device_extensions, num_required_device_extensions, required_device_layers, num_required_device_layers, required_features);
|
||||
|
||||
// TODO: Here we'll inject the instance and all of the stuff into the VulkanContext.
|
||||
|
||||
vk->CreateInstance({});
|
||||
|
||||
int physical_device = 0;
|
||||
while (gpu && vk->GetPhysicalDevice(physical_device) != gpu) {
|
||||
physical_device++;
|
||||
if (!VulkanLoadFromGetInstanceProcAddr(instance, get_instance_proc_addr)) {
|
||||
ERROR_LOG(Log::G3D, "Failed to resolve Vulkan functions from libretro-provided get_instance_proc_addr");
|
||||
delete vk;
|
||||
vk = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gpu) {
|
||||
if (vk->CreateInstanceExternal(instance) != VK_SUCCESS) {
|
||||
ERROR_LOG(Log::G3D, "Failed to adopt libretro-provided Vulkan instance: %s", vk->InitError().c_str());
|
||||
delete vk;
|
||||
vk = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
int physical_device = -1;
|
||||
if (gpu) {
|
||||
for (int i = 0; i < vk->GetNumPhysicalDevices(); i++) {
|
||||
if (vk->GetPhysicalDevice(i) == gpu) {
|
||||
physical_device = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (physical_device < 0) {
|
||||
WARN_LOG(Log::G3D, "libretro-provided VkPhysicalDevice %p not found among our own enumerated physical devices - falling back to best physical device", (void *)gpu);
|
||||
}
|
||||
}
|
||||
if (physical_device < 0) {
|
||||
physical_device = vk->GetBestPhysicalDevice();
|
||||
}
|
||||
|
||||
vk->CreateDevice(physical_device);
|
||||
#ifdef _WIN32
|
||||
vk->InitSurface(WINDOWSYSTEM_WIN32, nullptr, nullptr);
|
||||
#elif defined(__ANDROID__)
|
||||
vk->InitSurface(WINDOWSYSTEM_ANDROID, nullptr, nullptr);
|
||||
#elif defined(VK_USE_PLATFORM_METAL_EXT)
|
||||
vk->InitSurface(WINDOWSYSTEM_METAL_EXT, nullptr, nullptr);
|
||||
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||
vk->InitSurface(WINDOWSYSTEM_XLIB, nullptr, nullptr);
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
vk->InitSurface(WINDOWSYSTEM_XCB, nullptr, nullptr);
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
vk->InitSurface(WINDOWSYSTEM_WAYLAND, nullptr, nullptr);
|
||||
#elif defined(VK_USE_PLATFORM_DISPLAY_KHR)
|
||||
vk->InitSurface(WINDOWSYSTEM_DISPLAY, nullptr, nullptr);
|
||||
#endif
|
||||
std::vector<const char *> extraDeviceExtensions(required_device_extensions, required_device_extensions + num_required_device_extensions);
|
||||
if (vk->CreateDevice(physical_device, extraDeviceExtensions, required_features) != VK_SUCCESS) {
|
||||
ERROR_LOG(Log::G3D, "Failed to create Vulkan device: %s", vk->InitError().c_str());
|
||||
delete vk;
|
||||
vk = nullptr;
|
||||
return false;
|
||||
}
|
||||
// CreateDevice() enables whatever of extraDeviceExtensions is available and silently skips the rest -
|
||||
// if RetroArch's own subsequent Vulkan calls depend on one of these, silently not enabling it could
|
||||
// cause exactly the kind of driver-level crash that's hard to diagnose, so make sure we'd notice.
|
||||
for (const char *extraExtension : extraDeviceExtensions) {
|
||||
bool enabled = false;
|
||||
for (const char *e : vk->GetDeviceExtensionsEnabled()) {
|
||||
if (!strcmp(e, extraExtension)) {
|
||||
enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!enabled) {
|
||||
WARN_LOG(Log::G3D, "libretro required device extension '%s' was not enabled", extraExtension);
|
||||
}
|
||||
}
|
||||
|
||||
// Per the libretro create_device contract, the frontend takes over responsibility for eventually
|
||||
// destroying the VkDevice we just created and handed back to it below.
|
||||
vk->SetDeviceExternallyOwned();
|
||||
|
||||
// Normally the graphics queue is only resolved as a side effect of ReinitSurface()/ChooseQueue(),
|
||||
// since that also needs to check presentation support against a real WSI surface - libretro has no
|
||||
// such surface (RetroArch owns real presentation itself), so pick the queue directly instead.
|
||||
if (!vk->ChooseGraphicsQueueWithoutSurface()) {
|
||||
ERROR_LOG(Log::G3D, "Failed to choose a graphics queue");
|
||||
delete vk;
|
||||
vk = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
context->gpu = vk->GetPhysicalDevice(physical_device);
|
||||
context->device = vk->GetDevice();
|
||||
@@ -74,6 +115,8 @@ static bool create_device(retro_vulkan_context *context, VkInstance instance, Vk
|
||||
context->queue_family_index = vk->GetGraphicsQueueFamilyIndex();
|
||||
context->presentation_queue = context->queue;
|
||||
context->presentation_queue_family_index = context->queue_family_index;
|
||||
INFO_LOG(Log::G3D, "libretro create_device: gpu=%p device=%p queue=%p queue_family_index=%u",
|
||||
(void *)context->gpu, (void *)context->device, (void *)context->queue, context->queue_family_index);
|
||||
#ifdef _DEBUG
|
||||
fflush(stdout);
|
||||
#endif
|
||||
@@ -109,16 +152,16 @@ bool LibretroVulkanContext::InitAPI(void *wnd, std::string *deviceName, std::str
|
||||
}
|
||||
|
||||
void LibretroVulkanContext::ContextReset() {
|
||||
retro_hw_render_interface *vulkan;
|
||||
if (!Libretro::environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void **)&vulkan) || !vulkan) {
|
||||
retro_hw_render_interface *iface;
|
||||
if (!Libretro::environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void **)&iface) || !iface) {
|
||||
ERROR_LOG(Log::G3D, "Failed to get HW rendering interface!\n");
|
||||
return;
|
||||
}
|
||||
if (vulkan->interface_version != RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION) {
|
||||
ERROR_LOG(Log::G3D, "HW render interface mismatch, expected %u, got %u!\n", RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION, vulkan->interface_version);
|
||||
if (iface->interface_version != RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION) {
|
||||
ERROR_LOG(Log::G3D, "HW render interface mismatch, expected %u, got %u!\n", RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION, iface->interface_version);
|
||||
return;
|
||||
}
|
||||
vk_libretro_set_hwrender_interface(vulkan);
|
||||
hwRenderInterface = (retro_hw_render_interface_vulkan *)iface;
|
||||
|
||||
LibretroHWRenderContext::ContextReset();
|
||||
}
|
||||
@@ -132,12 +175,19 @@ void LibretroVulkanContext::ContextDestroy() {
|
||||
}
|
||||
|
||||
void LibretroVulkanContext::CreateDrawContext() {
|
||||
vk->ReinitSurface();
|
||||
int w = g_Config.iInternalResolution * NATIVEWIDTH;
|
||||
int h = g_Config.iInternalResolution * NATIVEHEIGHT;
|
||||
if (g_Config.bDisplayCropTo16x9) {
|
||||
h -= g_Config.iInternalResolution * 2;
|
||||
}
|
||||
|
||||
// TODO: Integrate properly with libretro vulkan context. We currently use a wacky wrapper (libretro_vulkan.cpp)
|
||||
if (!vk->InitSwapchain(VK_PRESENT_MODE_FIFO_KHR)) {
|
||||
auto libretroPresentation = std::make_unique<LibretroVulkanPresentation>(hwRenderInterface, VK_FORMAT_B8G8R8A8_UNORM, VkExtent2D{ (uint32_t)w, (uint32_t)h });
|
||||
if (!libretroPresentation->Create(vk)) {
|
||||
ERROR_LOG(Log::G3D, "Failed to create libretro Vulkan presentation images");
|
||||
return;
|
||||
}
|
||||
presentation = libretroPresentation.get();
|
||||
vk->SetPresentation(std::move(libretroPresentation));
|
||||
|
||||
bool useMultiThreading = g_Config.bRenderMultiThreading;
|
||||
if (g_Config.iInflightFrames == 1) {
|
||||
@@ -161,17 +211,21 @@ void LibretroVulkanContext::ShutdownAPI() {
|
||||
|
||||
vk->WaitUntilQueueIdle();
|
||||
|
||||
vk->DestroySwapchain();
|
||||
vk->DestroySurface();
|
||||
if (presentation) {
|
||||
presentation->Destroy(vk);
|
||||
vk->SetPresentation(nullptr);
|
||||
presentation = nullptr;
|
||||
}
|
||||
|
||||
vk->DestroyDevice();
|
||||
vk->DestroyInstance();
|
||||
|
||||
delete vk;
|
||||
vk = nullptr;
|
||||
hwRenderInterface = nullptr;
|
||||
|
||||
finalize_glslang();
|
||||
glslang::DetachProcess();
|
||||
vk_libretro_shutdown();
|
||||
}
|
||||
|
||||
void *LibretroVulkanContext::GetAPIContext() { return vk; }
|
||||
@@ -0,0 +1,133 @@
|
||||
#include "libretro/LibretroVulkanPresentation.h"
|
||||
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
|
||||
using namespace PPSSPP_VK;
|
||||
|
||||
LibretroVulkanPresentation::LibretroVulkanPresentation(retro_hw_render_interface_vulkan *vulkan, VkFormat format, VkExtent2D extent)
|
||||
: vulkan_(vulkan), format_(format), extent_(extent) {
|
||||
}
|
||||
|
||||
bool LibretroVulkanPresentation::Create(VulkanContext *context) {
|
||||
dedicatedAllocation_ = context->Extensions().KHR_dedicated_allocation;
|
||||
|
||||
uint32_t mask = vulkan_->get_sync_index_mask(vulkan_->handle);
|
||||
uint32_t count = 0;
|
||||
while (mask) {
|
||||
count++;
|
||||
mask >>= 1;
|
||||
}
|
||||
|
||||
VkDevice device = context->GetDevice();
|
||||
images_.resize(count);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
Image &img = images_[i];
|
||||
|
||||
VkImageCreateInfo info{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
info.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
info.imageType = VK_IMAGE_TYPE_2D;
|
||||
info.format = format_;
|
||||
info.extent = { extent_.width, extent_.height, 1 };
|
||||
info.mipLevels = 1;
|
||||
info.arrayLayers = 1;
|
||||
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
if (vkCreateImage(device, &info, nullptr, &img.image) != VK_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VkMemoryRequirements memreq;
|
||||
vkGetImageMemoryRequirements(device, img.image, &memreq);
|
||||
|
||||
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
alloc.allocationSize = memreq.size;
|
||||
|
||||
VkMemoryDedicatedAllocateInfoKHR dedicated{ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
|
||||
if (dedicatedAllocation_) {
|
||||
alloc.pNext = &dedicated;
|
||||
dedicated.image = img.image;
|
||||
}
|
||||
|
||||
if (!context->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex)) {
|
||||
return false;
|
||||
}
|
||||
if (vkAllocateMemory(device, &alloc, nullptr, &img.memory) != VK_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
if (vkBindImageMemory(device, img.image, img.memory, 0) != VK_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
img.retroImage.create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
img.retroImage.create_info.image = img.image;
|
||||
img.retroImage.create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
img.retroImage.create_info.format = format_;
|
||||
img.retroImage.create_info.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
|
||||
img.retroImage.create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
img.retroImage.create_info.subresourceRange.layerCount = 1;
|
||||
img.retroImage.create_info.subresourceRange.levelCount = 1;
|
||||
if (vkCreateImageView(device, &img.retroImage.create_info, nullptr, &img.retroImage.image_view) != VK_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
img.retroImage.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibretroVulkanPresentation::Destroy(VulkanContext *context) {
|
||||
VkDevice device = context->GetDevice();
|
||||
for (Image &img : images_) {
|
||||
if (img.retroImage.image_view) {
|
||||
vkDestroyImageView(device, img.retroImage.image_view, nullptr);
|
||||
}
|
||||
if (img.image) {
|
||||
vkDestroyImage(device, img.image, nullptr);
|
||||
}
|
||||
if (img.memory) {
|
||||
vkFreeMemory(device, img.memory, nullptr);
|
||||
}
|
||||
}
|
||||
images_.clear();
|
||||
}
|
||||
|
||||
VkResult LibretroVulkanPresentation::AcquireNextImage(VulkanContext *vulkan, VkSemaphore signalSemaphore, uint32_t *imageIndex) {
|
||||
// Unlike a real swapchain, RetroArch doesn't signal signalSemaphore for us here - PrepareSubmit()
|
||||
// strips any wait on it before the real vkQueueSubmit, matching the old hack's behavior.
|
||||
vulkan_->wait_sync_index(vulkan_->handle);
|
||||
*imageIndex = vulkan_->get_sync_index(vulkan_->handle);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VkResult LibretroVulkanPresentation::QueuePresent(VulkanContext *vulkan, VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
currentIndex_ = (int)imageIndex;
|
||||
vulkan_->set_image(vulkan_->handle, &images_[imageIndex].retroImage, 0, nullptr, vulkan_->queue_index);
|
||||
everPresented_ = true;
|
||||
condVar_.notify_all();
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
void LibretroVulkanPresentation::LockQueue() {
|
||||
vulkan_->lock_queue(vulkan_->handle);
|
||||
}
|
||||
|
||||
void LibretroVulkanPresentation::UnlockQueue() {
|
||||
vulkan_->unlock_queue(vulkan_->handle);
|
||||
}
|
||||
|
||||
void LibretroVulkanPresentation::PrepareSubmit(VkSubmitInfo &submitInfo) {
|
||||
submitInfo.waitSemaphoreCount = 0;
|
||||
submitInfo.pWaitSemaphores = nullptr;
|
||||
submitInfo.signalSemaphoreCount = 0;
|
||||
submitInfo.pSignalSemaphores = nullptr;
|
||||
}
|
||||
|
||||
void LibretroVulkanPresentation::WaitForPresentation() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
if (everPresented_ && currentIndex_ < 0) {
|
||||
condVar_.wait(lock);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#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;
|
||||
};
|
||||
@@ -1043,7 +1043,7 @@ SOURCES_CXX += \
|
||||
$(GPUDIR)/Vulkan/TextureCacheVulkan.cpp \
|
||||
$(GPUDIR)/Vulkan/VulkanUtil.cpp \
|
||||
$(LIBRETRODIR)/LibretroVulkanContext.cpp \
|
||||
$(LIBRETRODIR)/libretro_vulkan.cpp
|
||||
$(LIBRETRODIR)/LibretroVulkanPresentation.cpp
|
||||
|
||||
|
||||
ifeq ($(PLATFORM_EXT), win32)
|
||||
|
||||
@@ -1171,7 +1171,11 @@ void retro_init(void)
|
||||
{
|
||||
log_cb = log.log;
|
||||
g_logManager.Init(&g_Config.bEnableLogging);
|
||||
g_logManager.SetOutputsEnabled(LogOutput::ExternalCallback);
|
||||
// Also enable LogOutput::DebugString unconditionally (not just via Init()'s IsDebuggerPresent()
|
||||
// auto-detection, which only fires if a debugger was already attached before Init() ran) so the
|
||||
// log always shows up in the debugger's Output window when debugging the core in-process with
|
||||
// RetroArch, regardless of where RetroArch itself routes the ExternalCallback log messages.
|
||||
g_logManager.EnableOutput(LogOutput::ExternalCallback | LogOutput::DebugString);
|
||||
g_logManager.SetExternalLogCallback(&RetroLogCallback, (void *)log_cb);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,478 +0,0 @@
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Common/GPU/Vulkan/VulkanLoader.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Core/Config.h"
|
||||
|
||||
#define VK_NO_PROTOTYPES
|
||||
#include <libretro_vulkan.h>
|
||||
#include "libretro/LibretroGraphicsContext.h"
|
||||
|
||||
using namespace PPSSPP_VK;
|
||||
|
||||
static retro_hw_render_interface_vulkan *vulkan;
|
||||
|
||||
static struct {
|
||||
VkInstance instance;
|
||||
VkPhysicalDevice gpu;
|
||||
VkSurfaceKHR surface;
|
||||
PFN_vkGetInstanceProcAddr get_instance_proc_addr;
|
||||
const char **required_device_extensions;
|
||||
unsigned num_required_device_extensions;
|
||||
const char **required_device_layers;
|
||||
unsigned num_required_device_layers;
|
||||
const VkPhysicalDeviceFeatures *required_features;
|
||||
} vk_init_info;
|
||||
static bool DEDICATED_ALLOCATION;
|
||||
|
||||
struct VkSwapchainImage {
|
||||
VkImage handle;
|
||||
VkDeviceMemory memory;
|
||||
retro_vulkan_image retro_image;
|
||||
};
|
||||
|
||||
struct VkSwapchainKHR_T {
|
||||
uint32_t count;
|
||||
std::vector<VkSwapchainImage> images;
|
||||
std::mutex mutex;
|
||||
std::condition_variable condVar;
|
||||
int current_index;
|
||||
bool ever_presented = false;
|
||||
};
|
||||
static VkSwapchainKHR_T chain;
|
||||
|
||||
#define LIBRETRO_VK_WARP_LIST() \
|
||||
LIBRETRO_VK_WARP_FUNC(vkCreateInstance); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkDestroyInstance); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkCreateDevice); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkDestroyDevice); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkDestroySurfaceKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkCreateSwapchainKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkGetSwapchainImagesKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkAcquireNextImageKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkQueuePresentKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkDestroySwapchainKHR); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkQueueSubmit); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkQueueWaitIdle); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkCmdPipelineBarrier); \
|
||||
LIBRETRO_VK_WARP_FUNC(vkCreateRenderPass);
|
||||
|
||||
#define LIBRETRO_VK_WARP_FUNC(x) \
|
||||
PFN_##x x##_org
|
||||
|
||||
LIBRETRO_VK_WARP_FUNC(vkGetInstanceProcAddr);
|
||||
LIBRETRO_VK_WARP_FUNC(vkGetDeviceProcAddr);
|
||||
LIBRETRO_VK_WARP_LIST();
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance_libretro(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) {
|
||||
*pInstance = vk_init_info.instance;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static void add_name_unique(std::vector<const char *> &list, const char *value) {
|
||||
for (const char *name : list) {
|
||||
if (!strcmp(value, name))
|
||||
return;
|
||||
}
|
||||
|
||||
list.push_back(value);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice_libretro(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) {
|
||||
VkDeviceCreateInfo newInfo = *pCreateInfo;
|
||||
|
||||
// Add our custom layers
|
||||
std::vector<const char *> enabledLayerNames(pCreateInfo->ppEnabledLayerNames, pCreateInfo->ppEnabledLayerNames + pCreateInfo->enabledLayerCount);
|
||||
|
||||
for (uint32_t i = 0; i < vk_init_info.num_required_device_layers; i++) {
|
||||
add_name_unique(enabledLayerNames, vk_init_info.required_device_layers[i]);
|
||||
}
|
||||
|
||||
newInfo.enabledLayerCount = (uint32_t)enabledLayerNames.size();
|
||||
newInfo.ppEnabledLayerNames = newInfo.enabledLayerCount ? enabledLayerNames.data() : nullptr;
|
||||
|
||||
// Add our custom extensions
|
||||
std::vector<const char *> enabledExtensionNames(pCreateInfo->ppEnabledExtensionNames, pCreateInfo->ppEnabledExtensionNames + pCreateInfo->enabledExtensionCount);
|
||||
|
||||
for (uint32_t i = 0; i < vk_init_info.num_required_device_extensions; i++) {
|
||||
add_name_unique(enabledExtensionNames, vk_init_info.required_device_extensions[i]);
|
||||
}
|
||||
|
||||
for (const char *extensionName : enabledExtensionNames) {
|
||||
if (!strcmp(extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME))
|
||||
DEDICATED_ALLOCATION = true;
|
||||
}
|
||||
|
||||
newInfo.enabledExtensionCount = (uint32_t)enabledExtensionNames.size();
|
||||
newInfo.ppEnabledExtensionNames = newInfo.enabledExtensionCount ? enabledExtensionNames.data() : nullptr;
|
||||
|
||||
// Add required features through VkPhysicalDeviceFeatures2 when it is present.
|
||||
// Vulkan requires pEnabledFeatures to be NULL in that case.
|
||||
std::unordered_map<VkPhysicalDeviceFeatures *, VkPhysicalDeviceFeatures> originalFeaturePointers;
|
||||
VkPhysicalDeviceFeatures placeholderEnabledFeatures{};
|
||||
bool has_features2 = false;
|
||||
|
||||
for (const VkBaseOutStructure *next = (const VkBaseOutStructure *)pCreateInfo->pNext; next != nullptr;) {
|
||||
if (next->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) {
|
||||
VkPhysicalDeviceFeatures *enabledFeatures = &((VkPhysicalDeviceFeatures2 *)next)->features;
|
||||
originalFeaturePointers.try_emplace(enabledFeatures, *enabledFeatures);
|
||||
has_features2 = true;
|
||||
}
|
||||
|
||||
next = (const VkBaseOutStructure *)next->pNext;
|
||||
}
|
||||
|
||||
if (!has_features2) {
|
||||
if (newInfo.pEnabledFeatures) {
|
||||
placeholderEnabledFeatures = *newInfo.pEnabledFeatures;
|
||||
}
|
||||
|
||||
newInfo.pEnabledFeatures = &placeholderEnabledFeatures;
|
||||
originalFeaturePointers.try_emplace((VkPhysicalDeviceFeatures *)newInfo.pEnabledFeatures, *newInfo.pEnabledFeatures);
|
||||
} else {
|
||||
newInfo.pEnabledFeatures = nullptr;
|
||||
}
|
||||
|
||||
for (const auto& pair : originalFeaturePointers) {
|
||||
for (uint32_t i = 0; i < sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32); i++) {
|
||||
if (((VkBool32 *)vk_init_info.required_features)[i])
|
||||
((VkBool32 *)pair.first)[i] = VK_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
VkResult res = vkCreateDevice_org(physicalDevice, &newInfo, pAllocator, pDevice);
|
||||
|
||||
// The above code potentially modifies application memory. Restore it to avoid unexpected side effects.
|
||||
for (const auto& pair : originalFeaturePointers) {
|
||||
*pair.first = pair.second;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkCreateLibretroSurfaceKHR(VkInstance instance, const void *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
|
||||
*pSurface = vk_init_info.surface;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilitiesKHR_libretro(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) {
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR_org(physicalDevice, surface, pSurfaceCapabilities);
|
||||
if (res == VK_SUCCESS) {
|
||||
int w = g_Config.iInternalResolution * NATIVEWIDTH;
|
||||
int h = g_Config.iInternalResolution * NATIVEHEIGHT;
|
||||
|
||||
if (g_Config.bDisplayCropTo16x9)
|
||||
h -= g_Config.iInternalResolution * 2;
|
||||
|
||||
pSurfaceCapabilities->minImageExtent.width = w;
|
||||
pSurfaceCapabilities->minImageExtent.height = h;
|
||||
pSurfaceCapabilities->maxImageExtent.width = w;
|
||||
pSurfaceCapabilities->maxImageExtent.height = h;
|
||||
pSurfaceCapabilities->currentExtent.width = w;
|
||||
pSurfaceCapabilities->currentExtent.height = h;
|
||||
// The libretro fake swapchain presents images directly to the frontend via set_image(),
|
||||
// so the real surface's pre-rotation transform is irrelevant. Force identity to prevent
|
||||
// VulkanContext from applying rotation (which would double-rotate on Android).
|
||||
pSurfaceCapabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
pSurfaceCapabilities->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex) {
|
||||
VkPhysicalDeviceMemoryProperties memory_properties;
|
||||
vkGetPhysicalDeviceMemoryProperties(vulkan->gpu, &memory_properties);
|
||||
// Search memtypes to find first index with those properties
|
||||
for (uint32_t i = 0; i < 32; i++) {
|
||||
if ((typeBits & 1) == 1) {
|
||||
// Type is available, does it match user properties?
|
||||
if ((memory_properties.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask) {
|
||||
*typeIndex = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
typeBits >>= 1;
|
||||
}
|
||||
// No memory types matched, return failure
|
||||
return false;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR_libretro(VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) {
|
||||
uint32_t swapchain_mask = vulkan->get_sync_index_mask(vulkan->handle);
|
||||
|
||||
chain.count = 0;
|
||||
while (swapchain_mask) {
|
||||
chain.count++;
|
||||
swapchain_mask >>= 1;
|
||||
}
|
||||
chain.images.resize(chain.count);
|
||||
|
||||
for (uint32_t i = 0; i < chain.count; i++) {
|
||||
{
|
||||
VkImageCreateInfo info{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
info.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
info.imageType = VK_IMAGE_TYPE_2D;
|
||||
info.format = pCreateInfo->imageFormat;
|
||||
info.extent.width = pCreateInfo->imageExtent.width;
|
||||
info.extent.height = pCreateInfo->imageExtent.height;
|
||||
info.extent.depth = 1;
|
||||
info.mipLevels = 1;
|
||||
info.arrayLayers = 1;
|
||||
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
vkCreateImage(device, &info, pAllocator, &chain.images[i].handle);
|
||||
}
|
||||
|
||||
VkMemoryRequirements memreq;
|
||||
vkGetImageMemoryRequirements(device, chain.images[i].handle, &memreq);
|
||||
|
||||
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
alloc.allocationSize = memreq.size;
|
||||
|
||||
VkMemoryDedicatedAllocateInfoKHR dedicated{ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
|
||||
if (DEDICATED_ALLOCATION) {
|
||||
alloc.pNext = &dedicated;
|
||||
dedicated.image = chain.images[i].handle;
|
||||
}
|
||||
|
||||
MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex);
|
||||
VkResult res = vkAllocateMemory(device, &alloc, pAllocator, &chain.images[i].memory);
|
||||
assert(res == VK_SUCCESS);
|
||||
res = vkBindImageMemory(device, chain.images[i].handle, chain.images[i].memory, 0);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
chain.images[i].retro_image.create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
chain.images[i].retro_image.create_info.image = chain.images[i].handle;
|
||||
chain.images[i].retro_image.create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
chain.images[i].retro_image.create_info.format = pCreateInfo->imageFormat;
|
||||
chain.images[i].retro_image.create_info.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
|
||||
chain.images[i].retro_image.create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
chain.images[i].retro_image.create_info.subresourceRange.layerCount = 1;
|
||||
chain.images[i].retro_image.create_info.subresourceRange.levelCount = 1;
|
||||
res = vkCreateImageView(device, &chain.images[i].retro_image.create_info, pAllocator, &chain.images[i].retro_image.image_view);
|
||||
assert(res == VK_SUCCESS);
|
||||
|
||||
chain.images[i].retro_image.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
}
|
||||
|
||||
chain.current_index = -1;
|
||||
chain.ever_presented = false;
|
||||
*pSwapchain = (VkSwapchainKHR)&chain;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainImagesKHR_libretro(VkDevice device, VkSwapchainKHR swapchain_, uint32_t *pSwapchainImageCount, VkImage *pSwapchainImages) {
|
||||
VkSwapchainKHR_T *swapchain = (VkSwapchainKHR_T *)swapchain_;
|
||||
if (pSwapchainImages) {
|
||||
assert(*pSwapchainImageCount <= swapchain->count);
|
||||
for (int i = 0; i < *pSwapchainImageCount; i++)
|
||||
pSwapchainImages[i] = swapchain->images[i].handle;
|
||||
} else
|
||||
*pSwapchainImageCount = swapchain->count;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImageKHR_libretro(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *pImageIndex) {
|
||||
vulkan->wait_sync_index(vulkan->handle);
|
||||
*pImageIndex = vulkan->get_sync_index(vulkan->handle);
|
||||
#if 0
|
||||
vulkan->set_signal_semaphore(vulkan->handle, semaphore);
|
||||
#endif
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR_libretro(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) {
|
||||
VkSwapchainKHR_T *swapchain = (VkSwapchainKHR_T *)pPresentInfo->pSwapchains[0];
|
||||
std::unique_lock<std::mutex> lock(swapchain->mutex);
|
||||
#if 0
|
||||
if(chain.current_index >= 0)
|
||||
chain.condVar.wait(lock);
|
||||
#endif
|
||||
|
||||
chain.current_index = pPresentInfo->pImageIndices[0];
|
||||
#if 0
|
||||
vulkan->set_image(vulkan->handle, &swapchain->images[pPresentInfo->pImageIndices[0]].retro_image, pPresentInfo->waitSemaphoreCount, pPresentInfo->pWaitSemaphores, vulkan->queue_index);
|
||||
#else
|
||||
vulkan->set_image(vulkan->handle, &swapchain->images[pPresentInfo->pImageIndices[0]].retro_image, 0, nullptr, vulkan->queue_index);
|
||||
#endif
|
||||
chain.ever_presented = true;
|
||||
swapchain->condVar.notify_all();
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
void vk_libretro_wait_for_presentation() {
|
||||
std::unique_lock<std::mutex> lock(chain.mutex);
|
||||
if (chain.ever_presented && chain.current_index < 0)
|
||||
chain.condVar.wait(lock);
|
||||
#if 0
|
||||
chain.current_index = -1;
|
||||
chain.condVar.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL vkDestroyInstance_libretro(VkInstance instance, const VkAllocationCallbacks *pAllocator) {}
|
||||
static VKAPI_ATTR void VKAPI_CALL vkDestroyDevice_libretro(VkDevice device, const VkAllocationCallbacks *pAllocator) {}
|
||||
static VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR_libretro(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks *pAllocator) {}
|
||||
static VKAPI_ATTR void VKAPI_CALL vkDestroySwapchainKHR_libretro(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks *pAllocator) {
|
||||
for (int i = 0; i < chain.count; i++) {
|
||||
vkDestroyImage(device, chain.images[i].handle, pAllocator);
|
||||
vkDestroyImageView(device, chain.images[i].retro_image.image_view, pAllocator);
|
||||
vkFreeMemory(device, chain.images[i].memory, pAllocator);
|
||||
}
|
||||
|
||||
chain.images.clear();
|
||||
chain.count = 0;
|
||||
chain.current_index = -1;
|
||||
chain.ever_presented = false;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit_libretro(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) {
|
||||
VkResult res = VK_SUCCESS;
|
||||
|
||||
#if 0
|
||||
for(int i = 0; i < submitCount; i++)
|
||||
vulkan->set_command_buffers(vulkan->handle, pSubmits[i].commandBufferCount, pSubmits[i].pCommandBuffers);
|
||||
#else
|
||||
#if 1
|
||||
for (int i = 0; i < submitCount; i++) {
|
||||
((VkSubmitInfo *)pSubmits)[i].waitSemaphoreCount = 0;
|
||||
((VkSubmitInfo *)pSubmits)[i].pWaitSemaphores = nullptr;
|
||||
((VkSubmitInfo *)pSubmits)[i].signalSemaphoreCount = 0;
|
||||
((VkSubmitInfo *)pSubmits)[i].pSignalSemaphores = nullptr;
|
||||
}
|
||||
#endif
|
||||
vulkan->lock_queue(vulkan->handle);
|
||||
res = vkQueueSubmit_org(queue, submitCount, pSubmits, fence);
|
||||
vulkan->unlock_queue(vulkan->handle);
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkQueueWaitIdle_libretro(VkQueue queue) {
|
||||
vulkan->lock_queue(vulkan->handle);
|
||||
VkResult res = vkQueueWaitIdle_org(queue);
|
||||
vulkan->unlock_queue(vulkan->handle);
|
||||
return res;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier_libretro(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers) {
|
||||
VkImageMemoryBarrier *barriers = (VkImageMemoryBarrier *)pImageMemoryBarriers;
|
||||
for (int i = 0; i < imageMemoryBarrierCount; i++) {
|
||||
if (pImageMemoryBarriers[i].oldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
|
||||
barriers[i].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barriers[i].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
}
|
||||
if (pImageMemoryBarriers[i].newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
|
||||
barriers[i].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barriers[i].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
}
|
||||
}
|
||||
return vkCmdPipelineBarrier_org(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, barriers);
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass_libretro(VkDevice device, const VkRenderPassCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) {
|
||||
if (pCreateInfo->pAttachments[0].finalLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
|
||||
((VkAttachmentDescription *)pCreateInfo->pAttachments)[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
return vkCreateRenderPass_org(device, pCreateInfo, pAllocator, pRenderPass);
|
||||
}
|
||||
|
||||
#undef LIBRETRO_VK_WARP_FUNC
|
||||
#define LIBRETRO_VK_WARP_FUNC(x) \
|
||||
if (!strcmp(pName, #x)) { \
|
||||
x##_org = (PFN_##x)fptr; \
|
||||
return (PFN_vkVoidFunction)x##_libretro; \
|
||||
}
|
||||
|
||||
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr_libretro(VkInstance instance, const char *pName) {
|
||||
if (false
|
||||
#ifdef _WIN32
|
||||
|| !strcmp(pName, "vkCreateWin32SurfaceKHR")
|
||||
#endif
|
||||
#ifdef __ANDROID__
|
||||
|| !strcmp(pName, "vkCreateAndroidSurfaceKHR")
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_METAL_EXT
|
||||
|| !strcmp(pName, "vkCreateMetalSurfaceEXT")
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||
|| !strcmp(pName, "vkCreateXlibSurfaceKHR")
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||
|| !strcmp(pName, "vkCreateXcbSurfaceKHR")
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||
|| !strcmp(pName, "vkCreateWaylandSurfaceKHR")
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_DISPLAY_KHR
|
||||
|| !strcmp(pName, "vkCreateDisplayPlaneSurfaceKHR")
|
||||
#endif
|
||||
) {
|
||||
return (PFN_vkVoidFunction)vkCreateLibretroSurfaceKHR;
|
||||
}
|
||||
|
||||
PFN_vkVoidFunction fptr = vk_init_info.get_instance_proc_addr(instance, pName);
|
||||
if (!fptr) {
|
||||
ERROR_LOG(Log::G3D, "Failed to load VK instance function: %s", pName);
|
||||
return fptr;
|
||||
}
|
||||
|
||||
LIBRETRO_VK_WARP_LIST();
|
||||
|
||||
return fptr;
|
||||
}
|
||||
|
||||
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr_libretro(VkDevice device, const char *pName) {
|
||||
PFN_vkVoidFunction fptr = vkGetDeviceProcAddr_org(device, pName);
|
||||
if (!fptr)
|
||||
return fptr;
|
||||
|
||||
LIBRETRO_VK_WARP_LIST();
|
||||
|
||||
return fptr;
|
||||
}
|
||||
|
||||
void vk_libretro_init(VkInstance instance, VkPhysicalDevice gpu, VkSurfaceKHR surface, PFN_vkGetInstanceProcAddr get_instance_proc_addr, const char **required_device_extensions, unsigned num_required_device_extensions, const char **required_device_layers, unsigned num_required_device_layers, const VkPhysicalDeviceFeatures *required_features) {
|
||||
assert(surface);
|
||||
|
||||
vk_init_info.instance = instance;
|
||||
vk_init_info.gpu = gpu;
|
||||
vk_init_info.surface = surface;
|
||||
vk_init_info.get_instance_proc_addr = get_instance_proc_addr;
|
||||
vk_init_info.required_device_extensions = required_device_extensions;
|
||||
vk_init_info.num_required_device_extensions = num_required_device_extensions;
|
||||
vk_init_info.required_device_layers = required_device_layers;
|
||||
vk_init_info.num_required_device_layers = num_required_device_layers;
|
||||
vk_init_info.required_features = required_features;
|
||||
|
||||
vkGetInstanceProcAddr_org = vkGetInstanceProcAddr;
|
||||
vkGetInstanceProcAddr = vkGetInstanceProcAddr_libretro;
|
||||
vkGetDeviceProcAddr_org = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(instance, "vkGetDeviceProcAddr");;
|
||||
vkGetDeviceProcAddr = vkGetDeviceProcAddr_libretro;
|
||||
vkCreateInstance = vkCreateInstance_libretro;
|
||||
|
||||
vkEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion");
|
||||
vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties");
|
||||
vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");
|
||||
}
|
||||
|
||||
void vk_libretro_set_hwrender_interface(retro_hw_render_interface *hw_render_interface) {
|
||||
vulkan = (retro_hw_render_interface_vulkan *)hw_render_interface;
|
||||
}
|
||||
|
||||
void vk_libretro_shutdown() {
|
||||
memset(&vk_init_info, 0, sizeof(vk_init_info));
|
||||
vulkan = nullptr;
|
||||
DEDICATED_ALLOCATION = false;
|
||||
}
|
||||
Reference in new issue
Block a user