#pragma once #include #include #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/Data/Collections/Hashmaps.h" #if PPSSPP_PLATFORM(IOS) // iOS doesn't support a large number of timestamp queries natively. // We don't want MoltenVK to fall back to emulation. enum { MAX_TIMESTAMP_QUERIES = 32, }; #else enum { MAX_TIMESTAMP_QUERIES = 128, }; #endif enum class VKRRunType { SUBMIT, PRESENT, SYNC, EXIT, }; struct QueueProfileContext { bool enabled = false; bool timestampsEnabled = false; VkQueryPool queryPool; std::vector timestampDescriptions; std::string profileSummary; double cpuStartTime; double cpuEndTime; double descWriteTime; int descriptorsWritten; int descriptorsDeduped; #ifdef _DEBUG int commandCounts[11]; #endif }; class VKRFramebuffer; struct ReadbackKey { const VKRFramebuffer *framebuf; int width; int height; }; struct CachedReadback { VkBuffer buffer; VmaAllocation allocation; VkDeviceSize bufferSize; bool isCoherent; // frameId of the frame that created it; holds no data until that frame comes round again. uint64_t createdFrame; void Destroy(VulkanContext *vulkan); }; // Swap chain management struct SwapchainImageData { VkImage image; VkImageView view; VkSemaphore renderingCompleteSemaphore = VK_NULL_HANDLE; }; struct FrameDataShared { // For synchronous readbacks. VkFence readbackFence = VK_NULL_HANDLE; bool useMultiThreading = false; bool measurePresentTime = false; std::vector swapchainImages_; uint32_t swapchainImageCount_ = 0; void Init(VulkanContext *vulkan, bool useMultiThreading, bool measurePresentTime); void Destroy(VulkanContext *vulkan); }; enum class FrameSubmitType { Pending, Sync, FinishFrame, }; // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { bool skipSwap = false; // With the render thread: set once it has submitted this frame (its fence is then in flight), // cleared by BeginFrame before it waits on the fence. retro_atomic_int_t readyForFence{ 1 }; VkFence fence = VK_NULL_HANDLE; VkSemaphore acquireSemaphore = VK_NULL_HANDLE; // These are on different threads so need separate pools. VkCommandPool cmdPoolInit = VK_NULL_HANDLE; // Written to from main thread VkCommandPool cmdPoolMain = VK_NULL_HANDLE; // Written to from render thread, which also submits VkCommandBuffer initCmd = VK_NULL_HANDLE; VkCommandBuffer mainCmd = VK_NULL_HANDLE; VkCommandBuffer presentCmd = VK_NULL_HANDLE; bool hasInitCommands = false; bool hasMainCommands = false; bool hasPresentCommands = false; bool hasFencePending = false; bool hasAcquired = false; // Set by the render thread once a sync submit has finished, cleared by FlushSync. retro_atomic_int_t syncDone{ 0 }; // Swapchain. uint32_t curSwapchainImage = -1; // Frames need unique IDs to wait for present on, let's keep them here. // Also used for indexing into the frame timing history buffer. uint64_t frameId = 0; // Profiling. QueueProfileContext profile{}; // Async readback cache. DenseHashMap readbacks_; FrameData() : readbacks_(8) {} void Init(VulkanContext *vulkan, int index); void Destroy(VulkanContext *vulkan); void AcquireNextImage(VulkanContext *vulkan); VkResult QueuePresent(VulkanContext *vulkan, FrameDataShared &shared); // Generally called from the main thread, unlike most of the rest. VkCommandBuffer GetInitCmd(VulkanContext *vulkan); // Submits pending command buffers. void Submit(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &shared); private: // Metadata for logging etc int index = -1; };