mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-20 17:59:08 +02:00
- Fixes #21739 With the DarkStalkers hack, SoftGPU skips the game's stretch blit and stretches the source image to the screen itself. Libretro always presents software rendering at 480x272, so that stretch happened at 1x and the frontend then scaled up a blurry image. Now libretro gets the 384x224 source as is, with the aspect the game would have stretched it to, and switches back to the full frame when the game stops using the stretch. GL and D3D11 read the frame from a corner of their larger framebuffer. RetroArch's Vulkan driver samples the whole image the core hands it, so the Vulkan presentation images are resized to the frame. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
84 lines
3.4 KiB
C++
84 lines
3.4 KiB
C++
#pragma once
|
|
|
|
#include <atomic>
|
|
#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;
|
|
bool NeedsRecreate() const override;
|
|
bool Recreate(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;
|
|
void BeginPresent() override;
|
|
void EndPresent() 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();
|
|
|
|
// RetroArch samples the whole image, whatever frame size we report, so the images have to be the
|
|
// frame's size. They're recreated at the start of a later frame.
|
|
void RequestExtent(VkExtent2D extent) {
|
|
requestedExtent_ = PackExtent(extent);
|
|
}
|
|
|
|
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_;
|
|
std::atomic<uint32_t> requestedExtent_;
|
|
bool dedicatedAllocation_ = false;
|
|
uint32_t syncIndexMask_ = 0;
|
|
|
|
std::vector<Image> images_;
|
|
|
|
std::mutex mutex_;
|
|
std::condition_variable condVar_;
|
|
bool presentPending_ = false;
|
|
|
|
static uint32_t PackExtent(VkExtent2D extent) { return (extent.width << 16) | extent.height; }
|
|
static uint32_t ImageCountFromMask(uint32_t mask);
|
|
bool IsValidImageIndex(uint32_t imageIndex) const;
|
|
};
|