mirror of
				https://git.h3cjp.net/H3cJP/citra.git
				synced 2025-10-31 15:04:53 +00:00 
			
		
		
		
	Merge pull request #4674 from ReinUsesLisp/timeline-semaphores
renderer_vulkan: Make unconditional use of VK_KHR_timeline_semaphore
This commit is contained in:
		
						commit
						d66b897a6d
					
				|  | @ -190,6 +190,8 @@ if (ENABLE_VULKAN) | |||
|         renderer_vulkan/vk_blit_screen.h | ||||
|         renderer_vulkan/vk_buffer_cache.cpp | ||||
|         renderer_vulkan/vk_buffer_cache.h | ||||
|         renderer_vulkan/vk_command_pool.cpp | ||||
|         renderer_vulkan/vk_command_pool.h | ||||
|         renderer_vulkan/vk_compute_pass.cpp | ||||
|         renderer_vulkan/vk_compute_pass.h | ||||
|         renderer_vulkan/vk_compute_pipeline.cpp | ||||
|  | @ -204,6 +206,8 @@ if (ENABLE_VULKAN) | |||
|         renderer_vulkan/vk_graphics_pipeline.h | ||||
|         renderer_vulkan/vk_image.cpp | ||||
|         renderer_vulkan/vk_image.h | ||||
|         renderer_vulkan/vk_master_semaphore.cpp | ||||
|         renderer_vulkan/vk_master_semaphore.h | ||||
|         renderer_vulkan/vk_memory_manager.cpp | ||||
|         renderer_vulkan/vk_memory_manager.h | ||||
|         renderer_vulkan/vk_pipeline_cache.cpp | ||||
|  | @ -214,8 +218,8 @@ if (ENABLE_VULKAN) | |||
|         renderer_vulkan/vk_rasterizer.h | ||||
|         renderer_vulkan/vk_renderpass_cache.cpp | ||||
|         renderer_vulkan/vk_renderpass_cache.h | ||||
|         renderer_vulkan/vk_resource_manager.cpp | ||||
|         renderer_vulkan/vk_resource_manager.h | ||||
|         renderer_vulkan/vk_resource_pool.cpp | ||||
|         renderer_vulkan/vk_resource_pool.h | ||||
|         renderer_vulkan/vk_sampler_cache.cpp | ||||
|         renderer_vulkan/vk_sampler_cache.h | ||||
|         renderer_vulkan/vk_scheduler.cpp | ||||
|  |  | |||
|  | @ -91,8 +91,7 @@ private: | |||
|     std::shared_ptr<HostCounter> last; | ||||
| }; | ||||
| 
 | ||||
| template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter, | ||||
|           class QueryPool> | ||||
| template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter> | ||||
| class QueryCacheBase { | ||||
| public: | ||||
|     explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_, | ||||
|  | @ -206,9 +205,6 @@ public: | |||
|         committed_flushes.pop_front(); | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     std::array<QueryPool, VideoCore::NumQueryTypes> query_pools; | ||||
| 
 | ||||
| private: | ||||
|     /// Flushes a memory range to guest memory and removes it from the cache.
 | ||||
|     void FlushAndRemoveRegion(VAddr addr, std::size_t size) { | ||||
|  |  | |||
|  | @ -32,10 +32,8 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) { | |||
| 
 | ||||
| QueryCache::QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d, | ||||
|                        Tegra::MemoryManager& gpu_memory) | ||||
|     : VideoCommon::QueryCacheBase< | ||||
|           QueryCache, CachedQuery, CounterStream, HostCounter, | ||||
|           std::vector<OGLQuery>>{static_cast<VideoCore::RasterizerInterface&>(rasterizer), | ||||
|                                  maxwell3d, gpu_memory}, | ||||
|     : VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter>( | ||||
|           rasterizer, maxwell3d, gpu_memory), | ||||
|       gl_rasterizer{rasterizer} {} | ||||
| 
 | ||||
| QueryCache::~QueryCache() = default; | ||||
|  | @ -91,6 +89,8 @@ u64 HostCounter::BlockingQuery() const { | |||
| CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr) | ||||
|     : VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {} | ||||
| 
 | ||||
| CachedQuery::~CachedQuery() = default; | ||||
| 
 | ||||
| CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept | ||||
|     : VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {} | ||||
| 
 | ||||
|  |  | |||
|  | @ -26,8 +26,8 @@ class RasterizerOpenGL; | |||
| 
 | ||||
| using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>; | ||||
| 
 | ||||
| class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, | ||||
|                                                             HostCounter, std::vector<OGLQuery>> { | ||||
| class QueryCache final | ||||
|     : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { | ||||
| public: | ||||
|     explicit QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d, | ||||
|                         Tegra::MemoryManager& gpu_memory); | ||||
|  | @ -41,6 +41,7 @@ public: | |||
| 
 | ||||
| private: | ||||
|     RasterizerOpenGL& gl_rasterizer; | ||||
|     std::array<std::vector<OGLQuery>, VideoCore::NumQueryTypes> query_pools; | ||||
| }; | ||||
| 
 | ||||
| class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> { | ||||
|  | @ -63,10 +64,12 @@ class CachedQuery final : public VideoCommon::CachedQueryBase<HostCounter> { | |||
| public: | ||||
|     explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, | ||||
|                          u8* host_ptr); | ||||
|     CachedQuery(CachedQuery&& rhs) noexcept; | ||||
|     CachedQuery(const CachedQuery&) = delete; | ||||
|     ~CachedQuery() override; | ||||
| 
 | ||||
|     CachedQuery(CachedQuery&& rhs) noexcept; | ||||
|     CachedQuery& operator=(CachedQuery&& rhs) noexcept; | ||||
| 
 | ||||
|     CachedQuery(const CachedQuery&) = delete; | ||||
|     CachedQuery& operator=(const CachedQuery&) = delete; | ||||
| 
 | ||||
|     void Flush() override; | ||||
|  |  | |||
|  | @ -25,9 +25,9 @@ | |||
| #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||||
| #include "video_core/renderer_vulkan/vk_blit_screen.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_master_semaphore.h" | ||||
| #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_rasterizer.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_state_tracker.h" | ||||
| #include "video_core/renderer_vulkan/vk_swapchain.h" | ||||
|  | @ -56,7 +56,7 @@ VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, | |||
|                        VkDebugUtilsMessageTypeFlagsEXT type, | ||||
|                        const VkDebugUtilsMessengerCallbackDataEXT* data, | ||||
|                        [[maybe_unused]] void* user_data) { | ||||
|     const char* message{data->pMessage}; | ||||
|     const char* const message{data->pMessage}; | ||||
| 
 | ||||
|     if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { | ||||
|         LOG_CRITICAL(Render_Vulkan, "{}", message); | ||||
|  | @ -269,11 +269,11 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
|         scheduler->WaitWorker(); | ||||
| 
 | ||||
|         swapchain->AcquireNextImage(); | ||||
|         const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated); | ||||
|         const VkSemaphore render_semaphore = blit_screen->Draw(*framebuffer, use_accelerated); | ||||
| 
 | ||||
|         scheduler->Flush(false, render_semaphore); | ||||
|         scheduler->Flush(render_semaphore); | ||||
| 
 | ||||
|         if (swapchain->Present(render_semaphore, fence)) { | ||||
|         if (swapchain->Present(render_semaphore)) { | ||||
|             blit_screen->Recreate(); | ||||
|         } | ||||
| 
 | ||||
|  | @ -295,23 +295,21 @@ bool RendererVulkan::Init() { | |||
| 
 | ||||
|     memory_manager = std::make_unique<VKMemoryManager>(*device); | ||||
| 
 | ||||
|     resource_manager = std::make_unique<VKResourceManager>(*device); | ||||
| 
 | ||||
|     const auto& framebuffer = render_window.GetFramebufferLayout(); | ||||
|     swapchain = std::make_unique<VKSwapchain>(*surface, *device); | ||||
|     swapchain->Create(framebuffer.width, framebuffer.height, false); | ||||
| 
 | ||||
|     state_tracker = std::make_unique<StateTracker>(gpu); | ||||
| 
 | ||||
|     scheduler = std::make_unique<VKScheduler>(*device, *resource_manager, *state_tracker); | ||||
|     scheduler = std::make_unique<VKScheduler>(*device, *state_tracker); | ||||
| 
 | ||||
|     rasterizer = std::make_unique<RasterizerVulkan>( | ||||
|         render_window, gpu, gpu.MemoryManager(), cpu_memory, screen_info, *device, | ||||
|         *resource_manager, *memory_manager, *state_tracker, *scheduler); | ||||
|     const auto& framebuffer = render_window.GetFramebufferLayout(); | ||||
|     swapchain = std::make_unique<VKSwapchain>(*surface, *device, *scheduler); | ||||
|     swapchain->Create(framebuffer.width, framebuffer.height, false); | ||||
| 
 | ||||
|     blit_screen = std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device, | ||||
|                                                  *resource_manager, *memory_manager, *swapchain, | ||||
|                                                  *scheduler, screen_info); | ||||
|     rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(), | ||||
|                                                     cpu_memory, screen_info, *device, | ||||
|                                                     *memory_manager, *state_tracker, *scheduler); | ||||
| 
 | ||||
|     blit_screen = | ||||
|         std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device, | ||||
|                                        *memory_manager, *swapchain, *scheduler, screen_info); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
|  | @ -329,7 +327,6 @@ void RendererVulkan::ShutDown() { | |||
|     scheduler.reset(); | ||||
|     swapchain.reset(); | ||||
|     memory_manager.reset(); | ||||
|     resource_manager.reset(); | ||||
|     device.reset(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,9 +30,7 @@ namespace Vulkan { | |||
| class StateTracker; | ||||
| class VKBlitScreen; | ||||
| class VKDevice; | ||||
| class VKFence; | ||||
| class VKMemoryManager; | ||||
| class VKResourceManager; | ||||
| class VKSwapchain; | ||||
| class VKScheduler; | ||||
| class VKImage; | ||||
|  | @ -81,11 +79,10 @@ private: | |||
| 
 | ||||
|     vk::DebugCallback debug_callback; | ||||
|     std::unique_ptr<VKDevice> device; | ||||
|     std::unique_ptr<VKSwapchain> swapchain; | ||||
|     std::unique_ptr<VKMemoryManager> memory_manager; | ||||
|     std::unique_ptr<VKResourceManager> resource_manager; | ||||
|     std::unique_ptr<StateTracker> state_tracker; | ||||
|     std::unique_ptr<VKScheduler> scheduler; | ||||
|     std::unique_ptr<VKSwapchain> swapchain; | ||||
|     std::unique_ptr<VKBlitScreen> blit_screen; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,11 +12,9 @@ | |||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/math_util.h" | ||||
| 
 | ||||
| #include "core/core.h" | ||||
| #include "core/frontend/emu_window.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| #include "video_core/gpu.h" | ||||
| #include "video_core/morton.h" | ||||
| #include "video_core/rasterizer_interface.h" | ||||
|  | @ -24,8 +22,8 @@ | |||
| #include "video_core/renderer_vulkan/vk_blit_screen.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_image.h" | ||||
| #include "video_core/renderer_vulkan/vk_master_semaphore.h" | ||||
| #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_shader_util.h" | ||||
| #include "video_core/renderer_vulkan/vk_swapchain.h" | ||||
|  | @ -213,16 +211,12 @@ struct VKBlitScreen::BufferData { | |||
| VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_, | ||||
|                            Core::Frontend::EmuWindow& render_window_, | ||||
|                            VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_, | ||||
|                            VKResourceManager& resource_manager_, VKMemoryManager& memory_manager_, | ||||
|                            VKSwapchain& swapchain_, VKScheduler& scheduler_, | ||||
|                            const VKScreenInfo& screen_info_) | ||||
|     : cpu_memory{cpu_memory_}, render_window{render_window_}, | ||||
|       rasterizer{rasterizer_}, device{device_}, resource_manager{resource_manager_}, | ||||
|       memory_manager{memory_manager_}, swapchain{swapchain_}, scheduler{scheduler_}, | ||||
|       image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { | ||||
|     watches.resize(image_count); | ||||
|     std::generate(watches.begin(), watches.end(), | ||||
|                   []() { return std::make_unique<VKFenceWatch>(); }); | ||||
|                            VKMemoryManager& memory_manager_, VKSwapchain& swapchain_, | ||||
|                            VKScheduler& scheduler_, const VKScreenInfo& screen_info_) | ||||
|     : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_}, | ||||
|       device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_}, | ||||
|       scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { | ||||
|     resource_ticks.resize(image_count); | ||||
| 
 | ||||
|     CreateStaticResources(); | ||||
|     CreateDynamicResources(); | ||||
|  | @ -234,15 +228,16 @@ void VKBlitScreen::Recreate() { | |||
|     CreateDynamicResources(); | ||||
| } | ||||
| 
 | ||||
| std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, | ||||
|                                                      bool use_accelerated) { | ||||
| VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated) { | ||||
|     RefreshResources(framebuffer); | ||||
| 
 | ||||
|     // Finish any pending renderpass
 | ||||
|     scheduler.RequestOutsideRenderPassOperationContext(); | ||||
| 
 | ||||
|     const std::size_t image_index = swapchain.GetImageIndex(); | ||||
|     watches[image_index]->Watch(scheduler.GetFence()); | ||||
| 
 | ||||
|     scheduler.Wait(resource_ticks[image_index]); | ||||
|     resource_ticks[image_index] = scheduler.CurrentTick(); | ||||
| 
 | ||||
|     VKImage* blit_image = use_accelerated ? screen_info.image : raw_images[image_index].get(); | ||||
| 
 | ||||
|  | @ -345,7 +340,7 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon | |||
|         cmdbuf.EndRenderPass(); | ||||
|     }); | ||||
| 
 | ||||
|     return {scheduler.GetFence(), *semaphores[image_index]}; | ||||
|     return *semaphores[image_index]; | ||||
| } | ||||
| 
 | ||||
| void VKBlitScreen::CreateStaticResources() { | ||||
|  | @ -713,7 +708,7 @@ void VKBlitScreen::CreateFramebuffers() { | |||
| 
 | ||||
| void VKBlitScreen::ReleaseRawImages() { | ||||
|     for (std::size_t i = 0; i < raw_images.size(); ++i) { | ||||
|         watches[i]->Wait(); | ||||
|         scheduler.Wait(resource_ticks.at(i)); | ||||
|     } | ||||
|     raw_images.clear(); | ||||
|     raw_buffer_commits.clear(); | ||||
|  |  | |||
|  | @ -5,10 +5,8 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <tuple> | ||||
| 
 | ||||
| #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Core { | ||||
|  | @ -34,9 +32,9 @@ class RasterizerInterface; | |||
| namespace Vulkan { | ||||
| 
 | ||||
| struct ScreenInfo; | ||||
| 
 | ||||
| class RasterizerVulkan; | ||||
| class VKDevice; | ||||
| class VKFence; | ||||
| class VKImage; | ||||
| class VKScheduler; | ||||
| class VKSwapchain; | ||||
|  | @ -46,14 +44,13 @@ public: | |||
|     explicit VKBlitScreen(Core::Memory::Memory& cpu_memory, | ||||
|                           Core::Frontend::EmuWindow& render_window, | ||||
|                           VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, | ||||
|                           VKResourceManager& resource_manager, VKMemoryManager& memory_manager, | ||||
|                           VKSwapchain& swapchain, VKScheduler& scheduler, | ||||
|                           const VKScreenInfo& screen_info); | ||||
|                           VKMemoryManager& memory_manager, VKSwapchain& swapchain, | ||||
|                           VKScheduler& scheduler, const VKScreenInfo& screen_info); | ||||
|     ~VKBlitScreen(); | ||||
| 
 | ||||
|     void Recreate(); | ||||
| 
 | ||||
|     std::tuple<VKFence&, VkSemaphore> Draw(const Tegra::FramebufferConfig& framebuffer, | ||||
|     [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer, | ||||
|                                    bool use_accelerated); | ||||
| 
 | ||||
| private: | ||||
|  | @ -90,7 +87,6 @@ private: | |||
|     Core::Frontend::EmuWindow& render_window; | ||||
|     VideoCore::RasterizerInterface& rasterizer; | ||||
|     const VKDevice& device; | ||||
|     VKResourceManager& resource_manager; | ||||
|     VKMemoryManager& memory_manager; | ||||
|     VKSwapchain& swapchain; | ||||
|     VKScheduler& scheduler; | ||||
|  | @ -111,7 +107,7 @@ private: | |||
|     vk::Buffer buffer; | ||||
|     VKMemoryCommit buffer_commit; | ||||
| 
 | ||||
|     std::vector<std::unique_ptr<VKFenceWatch>> watches; | ||||
|     std::vector<u64> resource_ticks; | ||||
| 
 | ||||
|     std::vector<vk::Semaphore> semaphores; | ||||
|     std::vector<std::unique_ptr<VKImage>> raw_images; | ||||
|  |  | |||
							
								
								
									
										41
									
								
								src/video_core/renderer_vulkan/vk_command_pool.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/video_core/renderer_vulkan/vk_command_pool.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <cstddef> | ||||
| 
 | ||||
| #include "video_core/renderer_vulkan/vk_command_pool.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| constexpr size_t COMMAND_BUFFER_POOL_SIZE = 0x1000; | ||||
| 
 | ||||
| CommandPool::CommandPool(MasterSemaphore& master_semaphore, const VKDevice& device) | ||||
|     : ResourcePool(master_semaphore, COMMAND_BUFFER_POOL_SIZE), device{device} {} | ||||
| 
 | ||||
| CommandPool::~CommandPool() = default; | ||||
| 
 | ||||
| void CommandPool::Allocate(size_t begin, size_t end) { | ||||
|     // Command buffers are going to be commited, recorded, executed every single usage cycle.
 | ||||
|     // They are also going to be reseted when commited.
 | ||||
|     Pool& pool = pools.emplace_back(); | ||||
|     pool.handle = device.GetLogical().CreateCommandPool({ | ||||
|         .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = | ||||
|             VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, | ||||
|         .queueFamilyIndex = device.GetGraphicsFamily(), | ||||
|     }); | ||||
|     pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE); | ||||
| } | ||||
| 
 | ||||
| VkCommandBuffer CommandPool::Commit() { | ||||
|     const size_t index = CommitResource(); | ||||
|     const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE; | ||||
|     const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE; | ||||
|     return pools[pool_index].cmdbufs[sub_index]; | ||||
| } | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
							
								
								
									
										35
									
								
								src/video_core/renderer_vulkan/vk_command_pool.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/video_core/renderer_vulkan/vk_command_pool.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <cstddef> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "video_core/renderer_vulkan/vk_resource_pool.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| class MasterSemaphore; | ||||
| class VKDevice; | ||||
| 
 | ||||
| class CommandPool final : public ResourcePool { | ||||
| public: | ||||
|     explicit CommandPool(MasterSemaphore& master_semaphore, const VKDevice& device); | ||||
|     virtual ~CommandPool(); | ||||
| 
 | ||||
|     void Allocate(size_t begin, size_t end) override; | ||||
| 
 | ||||
|     VkCommandBuffer Commit(); | ||||
| 
 | ||||
| private: | ||||
|     struct Pool { | ||||
|         vk::CommandPool handle; | ||||
|         vk::CommandBuffers cmdbufs; | ||||
|     }; | ||||
| 
 | ||||
|     const VKDevice& device; | ||||
|     std::vector<Pool> pools; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
|  | @ -112,7 +112,8 @@ constexpr u8 quad_array[] = { | |||
|     0xf9, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, | ||||
|     0xf9, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4e, 0x00, 0x00, 0x00, | ||||
|     0xf9, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00, | ||||
|     0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00}; | ||||
|     0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, | ||||
| }; | ||||
| 
 | ||||
| VkDescriptorSetLayoutBinding BuildQuadArrayPassDescriptorSetLayoutBinding() { | ||||
|     return { | ||||
|  | @ -218,7 +219,8 @@ constexpr u8 uint8_pass[] = { | |||
|     0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, | ||||
|     0x24, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, | ||||
|     0xf9, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, | ||||
|     0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00}; | ||||
|     0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, | ||||
| }; | ||||
| 
 | ||||
| // Quad indexed SPIR-V module. Generated from the "shaders/" directory.
 | ||||
| constexpr u8 QUAD_INDEXED_SPV[] = { | ||||
|  | @ -341,7 +343,8 @@ constexpr u8 QUAD_INDEXED_SPV[] = { | |||
|     0xf9, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00, | ||||
|     0xf9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, 0x00, | ||||
|     0xf9, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, | ||||
|     0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00}; | ||||
|     0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, | ||||
| }; | ||||
| 
 | ||||
| std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBindings() { | ||||
|     return {{ | ||||
|  | @ -448,12 +451,12 @@ VKComputePass::VKComputePass(const VKDevice& device, VKDescriptorPool& descripto | |||
| 
 | ||||
| VKComputePass::~VKComputePass() = default; | ||||
| 
 | ||||
| VkDescriptorSet VKComputePass::CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue, | ||||
|                                                    VKFence& fence) { | ||||
| VkDescriptorSet VKComputePass::CommitDescriptorSet( | ||||
|     VKUpdateDescriptorQueue& update_descriptor_queue) { | ||||
|     if (!descriptor_template) { | ||||
|         return nullptr; | ||||
|     } | ||||
|     const auto set = descriptor_allocator->Commit(fence); | ||||
|     const VkDescriptorSet set = descriptor_allocator->Commit(); | ||||
|     update_descriptor_queue.Send(*descriptor_template, set); | ||||
|     return set; | ||||
| } | ||||
|  | @ -477,7 +480,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 | |||
| 
 | ||||
|     update_descriptor_queue.Acquire(); | ||||
|     update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); | ||||
|     const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence()); | ||||
|     const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); | ||||
| 
 | ||||
|     scheduler.RequestOutsideRenderPassOperationContext(); | ||||
| 
 | ||||
|  | @ -520,13 +523,13 @@ Uint8Pass::~Uint8Pass() = default; | |||
| 
 | ||||
| std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer, | ||||
|                                              u64 src_offset) { | ||||
|     const auto staging_size = static_cast<u32>(num_vertices * sizeof(u16)); | ||||
|     const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16)); | ||||
|     auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); | ||||
| 
 | ||||
|     update_descriptor_queue.Acquire(); | ||||
|     update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); | ||||
|     update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); | ||||
|     const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence()); | ||||
|     const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); | ||||
| 
 | ||||
|     scheduler.RequestOutsideRenderPassOperationContext(); | ||||
|     scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, | ||||
|  | @ -589,7 +592,7 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble( | |||
|     update_descriptor_queue.Acquire(); | ||||
|     update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); | ||||
|     update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); | ||||
|     const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence()); | ||||
|     const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); | ||||
| 
 | ||||
|     scheduler.RequestOutsideRenderPassOperationContext(); | ||||
|     scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, | ||||
|  |  | |||
|  | @ -15,7 +15,6 @@ | |||
| namespace Vulkan { | ||||
| 
 | ||||
| class VKDevice; | ||||
| class VKFence; | ||||
| class VKScheduler; | ||||
| class VKStagingBufferPool; | ||||
| class VKUpdateDescriptorQueue; | ||||
|  | @ -30,8 +29,7 @@ public: | |||
|     ~VKComputePass(); | ||||
| 
 | ||||
| protected: | ||||
|     VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue, | ||||
|                                         VKFence& fence); | ||||
|     VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue); | ||||
| 
 | ||||
|     vk::DescriptorUpdateTemplateKHR descriptor_template; | ||||
|     vk::PipelineLayout layout; | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ VkDescriptorSet VKComputePipeline::CommitDescriptorSet() { | |||
|     if (!descriptor_template) { | ||||
|         return {}; | ||||
|     } | ||||
|     const auto set = descriptor_allocator.Commit(scheduler.GetFence()); | ||||
|     const VkDescriptorSet set = descriptor_allocator.Commit(); | ||||
|     update_descriptor_queue.Send(*descriptor_template, set); | ||||
|     return set; | ||||
| } | ||||
|  |  | |||
|  | @ -7,7 +7,8 @@ | |||
| #include "common/common_types.h" | ||||
| #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_pool.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
|  | @ -15,14 +16,15 @@ namespace Vulkan { | |||
| // Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines.
 | ||||
| constexpr std::size_t SETS_GROW_RATE = 0x20; | ||||
| 
 | ||||
| DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool, | ||||
|                                          VkDescriptorSetLayout layout) | ||||
|     : VKFencedPool{SETS_GROW_RATE}, descriptor_pool{descriptor_pool}, layout{layout} {} | ||||
| DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool_, | ||||
|                                          VkDescriptorSetLayout layout_) | ||||
|     : ResourcePool(descriptor_pool_.master_semaphore, SETS_GROW_RATE), | ||||
|       descriptor_pool{descriptor_pool_}, layout{layout_} {} | ||||
| 
 | ||||
| DescriptorAllocator::~DescriptorAllocator() = default; | ||||
| 
 | ||||
| VkDescriptorSet DescriptorAllocator::Commit(VKFence& fence) { | ||||
|     const std::size_t index = CommitResource(fence); | ||||
| VkDescriptorSet DescriptorAllocator::Commit() { | ||||
|     const std::size_t index = CommitResource(); | ||||
|     return descriptors_allocations[index / SETS_GROW_RATE][index % SETS_GROW_RATE]; | ||||
| } | ||||
| 
 | ||||
|  | @ -30,8 +32,9 @@ void DescriptorAllocator::Allocate(std::size_t begin, std::size_t end) { | |||
|     descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin)); | ||||
| } | ||||
| 
 | ||||
| VKDescriptorPool::VKDescriptorPool(const VKDevice& device) | ||||
|     : device{device}, active_pool{AllocateNewPool()} {} | ||||
| VKDescriptorPool::VKDescriptorPool(const VKDevice& device_, VKScheduler& scheduler) | ||||
|     : device{device_}, master_semaphore{scheduler.GetMasterSemaphore()}, active_pool{ | ||||
|                                                                              AllocateNewPool()} {} | ||||
| 
 | ||||
| VKDescriptorPool::~VKDescriptorPool() = default; | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,21 +6,24 @@ | |||
| 
 | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_pool.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| class VKDevice; | ||||
| class VKDescriptorPool; | ||||
| class VKScheduler; | ||||
| 
 | ||||
| class DescriptorAllocator final : public VKFencedPool { | ||||
| class DescriptorAllocator final : public ResourcePool { | ||||
| public: | ||||
|     explicit DescriptorAllocator(VKDescriptorPool& descriptor_pool, VkDescriptorSetLayout layout); | ||||
|     ~DescriptorAllocator() override; | ||||
| 
 | ||||
|     DescriptorAllocator& operator=(const DescriptorAllocator&) = delete; | ||||
|     DescriptorAllocator(const DescriptorAllocator&) = delete; | ||||
| 
 | ||||
|     VkDescriptorSet Commit(VKFence& fence); | ||||
|     VkDescriptorSet Commit(); | ||||
| 
 | ||||
| protected: | ||||
|     void Allocate(std::size_t begin, std::size_t end) override; | ||||
|  | @ -36,15 +39,19 @@ class VKDescriptorPool final { | |||
|     friend DescriptorAllocator; | ||||
| 
 | ||||
| public: | ||||
|     explicit VKDescriptorPool(const VKDevice& device); | ||||
|     explicit VKDescriptorPool(const VKDevice& device, VKScheduler& scheduler); | ||||
|     ~VKDescriptorPool(); | ||||
| 
 | ||||
|     VKDescriptorPool(const VKDescriptorPool&) = delete; | ||||
|     VKDescriptorPool& operator=(const VKDescriptorPool&) = delete; | ||||
| 
 | ||||
| private: | ||||
|     vk::DescriptorPool* AllocateNewPool(); | ||||
| 
 | ||||
|     vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count); | ||||
| 
 | ||||
|     const VKDevice& device; | ||||
|     MasterSemaphore& master_semaphore; | ||||
| 
 | ||||
|     std::vector<vk::DescriptorPool> pools; | ||||
|     vk::DescriptorPool* active_pool; | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ constexpr std::array REQUIRED_EXTENSIONS{ | |||
|     VK_KHR_8BIT_STORAGE_EXTENSION_NAME, | ||||
|     VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, | ||||
|     VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, | ||||
|     VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, | ||||
|     VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, | ||||
|     VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, | ||||
|     VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, | ||||
|  | @ -250,6 +251,13 @@ bool VKDevice::Create() { | |||
|         .inheritedQueries = false, | ||||
|     }; | ||||
| 
 | ||||
|     VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR, | ||||
|         .pNext = nullptr, | ||||
|         .timelineSemaphore = true, | ||||
|     }; | ||||
|     SetNext(next, timeline_semaphore); | ||||
| 
 | ||||
|     VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, | ||||
|         .pNext = nullptr, | ||||
|  |  | |||
|  | @ -29,8 +29,8 @@ void InnerFence::Queue() { | |||
|     } | ||||
|     ASSERT(!event); | ||||
| 
 | ||||
|     event = device.GetLogical().CreateNewEvent(); | ||||
|     ticks = scheduler.Ticks(); | ||||
|     event = device.GetLogical().CreateEvent(); | ||||
|     ticks = scheduler.CurrentTick(); | ||||
| 
 | ||||
|     scheduler.RequestOutsideRenderPassOperationContext(); | ||||
|     scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) { | ||||
|  | @ -52,7 +52,7 @@ void InnerFence::Wait() { | |||
|     } | ||||
|     ASSERT(event); | ||||
| 
 | ||||
|     if (ticks >= scheduler.Ticks()) { | ||||
|     if (ticks >= scheduler.CurrentTick()) { | ||||
|         scheduler.Flush(); | ||||
|     } | ||||
|     while (!IsEventSignalled()) { | ||||
|  |  | |||
|  | @ -93,7 +93,7 @@ VkDescriptorSet VKGraphicsPipeline::CommitDescriptorSet() { | |||
|     if (!descriptor_template) { | ||||
|         return {}; | ||||
|     } | ||||
|     const auto set = descriptor_allocator.Commit(scheduler.GetFence()); | ||||
|     const VkDescriptorSet set = descriptor_allocator.Commit(); | ||||
|     update_descriptor_queue.Send(*descriptor_template, set); | ||||
|     return set; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										56
									
								
								src/video_core/renderer_vulkan/vk_master_semaphore.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/video_core/renderer_vulkan/vk_master_semaphore.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | |||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <atomic> | ||||
| #include <chrono> | ||||
| 
 | ||||
| #include "core/settings.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_master_semaphore.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| using namespace std::chrono_literals; | ||||
| 
 | ||||
| MasterSemaphore::MasterSemaphore(const VKDevice& device) { | ||||
|     static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, | ||||
|         .pNext = nullptr, | ||||
|         .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR, | ||||
|         .initialValue = 0, | ||||
|     }; | ||||
|     static constexpr VkSemaphoreCreateInfo semaphore_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, | ||||
|         .pNext = &semaphore_type_ci, | ||||
|         .flags = 0, | ||||
|     }; | ||||
|     semaphore = device.GetLogical().CreateSemaphore(semaphore_ci); | ||||
| 
 | ||||
|     if (!Settings::values.renderer_debug) { | ||||
|         return; | ||||
|     } | ||||
|     // Validation layers have a bug where they fail to track resource usage when using timeline
 | ||||
|     // semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have
 | ||||
|     // a separate thread waiting for each timeline semaphore value.
 | ||||
|     debug_thread = std::thread([this] { | ||||
|         u64 counter = 0; | ||||
|         while (!shutdown) { | ||||
|             if (semaphore.Wait(counter, 10'000'000)) { | ||||
|                 ++counter; | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| MasterSemaphore::~MasterSemaphore() { | ||||
|     shutdown = true; | ||||
| 
 | ||||
|     // This thread might not be started
 | ||||
|     if (debug_thread.joinable()) { | ||||
|         debug_thread.join(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
							
								
								
									
										70
									
								
								src/video_core/renderer_vulkan/vk_master_semaphore.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/video_core/renderer_vulkan/vk_master_semaphore.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | |||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <atomic> | ||||
| #include <thread> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| class VKDevice; | ||||
| 
 | ||||
| class MasterSemaphore { | ||||
| public: | ||||
|     explicit MasterSemaphore(const VKDevice& device); | ||||
|     ~MasterSemaphore(); | ||||
| 
 | ||||
|     /// Returns the current logical tick.
 | ||||
|     [[nodiscard]] u64 CurrentTick() const noexcept { | ||||
|         return current_tick; | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the timeline semaphore handle.
 | ||||
|     [[nodiscard]] VkSemaphore Handle() const noexcept { | ||||
|         return *semaphore; | ||||
|     } | ||||
| 
 | ||||
|     /// Returns true when a tick has been hit by the GPU.
 | ||||
|     [[nodiscard]] bool IsFree(u64 tick) { | ||||
|         return gpu_tick >= tick; | ||||
|     } | ||||
| 
 | ||||
|     /// Advance to the logical tick.
 | ||||
|     void NextTick() noexcept { | ||||
|         ++current_tick; | ||||
|     } | ||||
| 
 | ||||
|     /// Refresh the known GPU tick
 | ||||
|     void Refresh() { | ||||
|         gpu_tick = semaphore.GetCounter(); | ||||
|     } | ||||
| 
 | ||||
|     /// Waits for a tick to be hit on the GPU
 | ||||
|     void Wait(u64 tick) { | ||||
|         // No need to wait if the GPU is ahead of the tick
 | ||||
|         if (IsFree(tick)) { | ||||
|             return; | ||||
|         } | ||||
|         // Update the GPU tick and try again
 | ||||
|         Refresh(); | ||||
|         if (IsFree(tick)) { | ||||
|             return; | ||||
|         } | ||||
|         // If none of the above is hit, fallback to a regular wait
 | ||||
|         semaphore.Wait(tick); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     vk::Semaphore semaphore;           ///< Timeline semaphore.
 | ||||
|     std::atomic<u64> gpu_tick{0};      ///< Current known GPU tick.
 | ||||
|     std::atomic<u64> current_tick{1};  ///< Current logical tick.
 | ||||
|     std::atomic<bool> shutdown{false}; ///< True when the object is being destroyed.
 | ||||
|     std::thread debug_thread;          ///< Debug thread to workaround validation layer bugs.
 | ||||
| }; | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
|  | @ -38,7 +38,6 @@ class RasterizerVulkan; | |||
| class VKComputePipeline; | ||||
| class VKDescriptorPool; | ||||
| class VKDevice; | ||||
| class VKFence; | ||||
| class VKScheduler; | ||||
| class VKUpdateDescriptorQueue; | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,35 +9,33 @@ | |||
| 
 | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_query_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_pool.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| using VideoCore::QueryType; | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| constexpr std::array QUERY_TARGETS = {VK_QUERY_TYPE_OCCLUSION}; | ||||
| 
 | ||||
| constexpr VkQueryType GetTarget(VideoCore::QueryType type) { | ||||
| constexpr VkQueryType GetTarget(QueryType type) { | ||||
|     return QUERY_TARGETS[static_cast<std::size_t>(type)]; | ||||
| } | ||||
| 
 | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| QueryPool::QueryPool() : VKFencedPool{GROW_STEP} {} | ||||
| QueryPool::QueryPool(const VKDevice& device_, VKScheduler& scheduler, QueryType type_) | ||||
|     : ResourcePool{scheduler.GetMasterSemaphore(), GROW_STEP}, device{device_}, type{type_} {} | ||||
| 
 | ||||
| QueryPool::~QueryPool() = default; | ||||
| 
 | ||||
| void QueryPool::Initialize(const VKDevice& device_, VideoCore::QueryType type_) { | ||||
|     device = &device_; | ||||
|     type = type_; | ||||
| } | ||||
| 
 | ||||
| std::pair<VkQueryPool, u32> QueryPool::Commit(VKFence& fence) { | ||||
| std::pair<VkQueryPool, u32> QueryPool::Commit() { | ||||
|     std::size_t index; | ||||
|     do { | ||||
|         index = CommitResource(fence); | ||||
|         index = CommitResource(); | ||||
|     } while (usage[index]); | ||||
|     usage[index] = true; | ||||
| 
 | ||||
|  | @ -47,7 +45,7 @@ std::pair<VkQueryPool, u32> QueryPool::Commit(VKFence& fence) { | |||
| void QueryPool::Allocate(std::size_t begin, std::size_t end) { | ||||
|     usage.resize(end); | ||||
| 
 | ||||
|     pools.push_back(device->GetLogical().CreateQueryPool({ | ||||
|     pools.push_back(device.GetLogical().CreateQueryPool({ | ||||
|         .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|  | @ -71,28 +69,36 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) { | |||
| VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer, | ||||
|                            Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory, | ||||
|                            const VKDevice& device, VKScheduler& scheduler) | ||||
|     : VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter, | ||||
|                                   QueryPool>{rasterizer, maxwell3d, gpu_memory}, | ||||
|       device{device}, scheduler{scheduler} { | ||||
|     for (std::size_t i = 0; i < static_cast<std::size_t>(VideoCore::NumQueryTypes); ++i) { | ||||
|         query_pools[i].Initialize(device, static_cast<VideoCore::QueryType>(i)); | ||||
|     : VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, | ||||
|                                   HostCounter>{rasterizer, maxwell3d, gpu_memory}, | ||||
|       device{device}, scheduler{scheduler}, query_pools{ | ||||
|                                                 QueryPool{device, scheduler, | ||||
|                                                           QueryType::SamplesPassed}, | ||||
|                                             } {} | ||||
| 
 | ||||
| VKQueryCache::~VKQueryCache() { | ||||
|     // TODO(Rodrigo): This is a hack to destroy all HostCounter instances before the base class
 | ||||
|     // destructor is called. The query cache should be redesigned to have a proper ownership model
 | ||||
|     // instead of using shared pointers.
 | ||||
|     for (size_t query_type = 0; query_type < VideoCore::NumQueryTypes; ++query_type) { | ||||
|         auto& stream = Stream(static_cast<QueryType>(query_type)); | ||||
|         stream.Update(false); | ||||
|         stream.Reset(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| VKQueryCache::~VKQueryCache() = default; | ||||
| 
 | ||||
| std::pair<VkQueryPool, u32> VKQueryCache::AllocateQuery(VideoCore::QueryType type) { | ||||
|     return query_pools[static_cast<std::size_t>(type)].Commit(scheduler.GetFence()); | ||||
| std::pair<VkQueryPool, u32> VKQueryCache::AllocateQuery(QueryType type) { | ||||
|     return query_pools[static_cast<std::size_t>(type)].Commit(); | ||||
| } | ||||
| 
 | ||||
| void VKQueryCache::Reserve(VideoCore::QueryType type, std::pair<VkQueryPool, u32> query) { | ||||
| void VKQueryCache::Reserve(QueryType type, std::pair<VkQueryPool, u32> query) { | ||||
|     query_pools[static_cast<std::size_t>(type)].Reserve(query); | ||||
| } | ||||
| 
 | ||||
| HostCounter::HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency, | ||||
|                          VideoCore::QueryType type) | ||||
|                          QueryType type) | ||||
|     : VideoCommon::HostCounterBase<VKQueryCache, HostCounter>{std::move(dependency)}, cache{cache}, | ||||
|       type{type}, query{cache.AllocateQuery(type)}, ticks{cache.Scheduler().Ticks()} { | ||||
|       type{type}, query{cache.AllocateQuery(type)}, tick{cache.Scheduler().CurrentTick()} { | ||||
|     const vk::Device* logical = &cache.Device().GetLogical(); | ||||
|     cache.Scheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { | ||||
|         logical->ResetQueryPoolEXT(query.first, query.second, 1); | ||||
|  | @ -110,7 +116,7 @@ void HostCounter::EndQuery() { | |||
| } | ||||
| 
 | ||||
| u64 HostCounter::BlockingQuery() const { | ||||
|     if (ticks >= cache.Scheduler().Ticks()) { | ||||
|     if (tick >= cache.Scheduler().CurrentTick()) { | ||||
|         cache.Scheduler().Flush(); | ||||
|     } | ||||
|     u64 data; | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/query_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_pool.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace VideoCore { | ||||
|  | @ -28,14 +28,12 @@ class VKScheduler; | |||
| 
 | ||||
| using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>; | ||||
| 
 | ||||
| class QueryPool final : public VKFencedPool { | ||||
| class QueryPool final : public ResourcePool { | ||||
| public: | ||||
|     explicit QueryPool(); | ||||
|     explicit QueryPool(const VKDevice& device, VKScheduler& scheduler, VideoCore::QueryType type); | ||||
|     ~QueryPool() override; | ||||
| 
 | ||||
|     void Initialize(const VKDevice& device, VideoCore::QueryType type); | ||||
| 
 | ||||
|     std::pair<VkQueryPool, u32> Commit(VKFence& fence); | ||||
|     std::pair<VkQueryPool, u32> Commit(); | ||||
| 
 | ||||
|     void Reserve(std::pair<VkQueryPool, u32> query); | ||||
| 
 | ||||
|  | @ -45,16 +43,15 @@ protected: | |||
| private: | ||||
|     static constexpr std::size_t GROW_STEP = 512; | ||||
| 
 | ||||
|     const VKDevice* device = nullptr; | ||||
|     VideoCore::QueryType type = {}; | ||||
|     const VKDevice& device; | ||||
|     const VideoCore::QueryType type; | ||||
| 
 | ||||
|     std::vector<vk::QueryPool> pools; | ||||
|     std::vector<bool> usage; | ||||
| }; | ||||
| 
 | ||||
| class VKQueryCache final | ||||
|     : public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter, | ||||
|                                          QueryPool> { | ||||
|     : public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter> { | ||||
| public: | ||||
|     explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer, | ||||
|                           Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory, | ||||
|  | @ -76,6 +73,7 @@ public: | |||
| private: | ||||
|     const VKDevice& device; | ||||
|     VKScheduler& scheduler; | ||||
|     std::array<QueryPool, VideoCore::NumQueryTypes> query_pools; | ||||
| }; | ||||
| 
 | ||||
| class HostCounter final : public VideoCommon::HostCounterBase<VKQueryCache, HostCounter> { | ||||
|  | @ -92,7 +90,7 @@ private: | |||
|     VKQueryCache& cache; | ||||
|     const VideoCore::QueryType type; | ||||
|     const std::pair<VkQueryPool, u32> query; | ||||
|     const u64 ticks; | ||||
|     const u64 tick; | ||||
| }; | ||||
| 
 | ||||
| class CachedQuery : public VideoCommon::CachedQueryBase<HostCounter> { | ||||
|  |  | |||
|  | @ -31,7 +31,6 @@ | |||
| #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_rasterizer.h" | ||||
| #include "video_core/renderer_vulkan/vk_renderpass_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_sampler_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | ||||
|  | @ -384,27 +383,25 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const { | |||
| RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_, | ||||
|                                    Tegra::MemoryManager& gpu_memory_, | ||||
|                                    Core::Memory::Memory& cpu_memory, VKScreenInfo& screen_info_, | ||||
|                                    const VKDevice& device_, VKResourceManager& resource_manager_, | ||||
|                                    VKMemoryManager& memory_manager_, StateTracker& state_tracker_, | ||||
|                                    VKScheduler& scheduler_) | ||||
|                                    const VKDevice& device_, VKMemoryManager& memory_manager_, | ||||
|                                    StateTracker& state_tracker_, VKScheduler& scheduler_) | ||||
|     : RasterizerAccelerated(cpu_memory), gpu(gpu_), gpu_memory(gpu_memory_), | ||||
|       maxwell3d(gpu.Maxwell3D()), kepler_compute(gpu.KeplerCompute()), screen_info(screen_info_), | ||||
|       device(device_), resource_manager(resource_manager_), memory_manager(memory_manager_), | ||||
|       state_tracker(state_tracker_), scheduler(scheduler_), | ||||
|       staging_pool(device, memory_manager, scheduler), descriptor_pool(device), | ||||
|       update_descriptor_queue(device, scheduler), renderpass_cache(device), | ||||
|       device(device_), memory_manager(memory_manager_), state_tracker(state_tracker_), | ||||
|       scheduler(scheduler_), staging_pool(device, memory_manager, scheduler), | ||||
|       descriptor_pool(device, scheduler_), update_descriptor_queue(device, scheduler), | ||||
|       renderpass_cache(device), | ||||
|       quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), | ||||
|       quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), | ||||
|       uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), | ||||
|       texture_cache(*this, maxwell3d, gpu_memory, device, resource_manager, memory_manager, | ||||
|                     scheduler, staging_pool), | ||||
|       texture_cache(*this, maxwell3d, gpu_memory, device, memory_manager, scheduler, staging_pool), | ||||
|       pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, | ||||
|                      descriptor_pool, update_descriptor_queue, renderpass_cache), | ||||
|       buffer_cache(*this, gpu_memory, cpu_memory, device, memory_manager, scheduler, staging_pool), | ||||
|       sampler_cache(device), query_cache(*this, maxwell3d, gpu_memory, device, scheduler), | ||||
|       fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device, | ||||
|                     scheduler), | ||||
|       wfi_event(device.GetLogical().CreateNewEvent()), async_shaders(emu_window) { | ||||
|       wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window) { | ||||
|     scheduler.SetQueryCache(query_cache); | ||||
|     if (device.UseAsynchronousShaders()) { | ||||
|         async_shaders.AllocateWorkers(); | ||||
|  |  | |||
|  | @ -25,7 +25,6 @@ | |||
| #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_query_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_renderpass_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_sampler_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | ||||
|  | @ -109,8 +108,8 @@ public: | |||
|     explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu, | ||||
|                               Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory, | ||||
|                               VKScreenInfo& screen_info, const VKDevice& device, | ||||
|                               VKResourceManager& resource_manager, VKMemoryManager& memory_manager, | ||||
|                               StateTracker& state_tracker, VKScheduler& scheduler); | ||||
|                               VKMemoryManager& memory_manager, StateTracker& state_tracker, | ||||
|                               VKScheduler& scheduler); | ||||
|     ~RasterizerVulkan() override; | ||||
| 
 | ||||
|     void Draw(bool is_indexed, bool is_instanced) override; | ||||
|  | @ -286,7 +285,6 @@ private: | |||
| 
 | ||||
|     VKScreenInfo& screen_info; | ||||
|     const VKDevice& device; | ||||
|     VKResourceManager& resource_manager; | ||||
|     VKMemoryManager& memory_manager; | ||||
|     StateTracker& state_tracker; | ||||
|     VKScheduler& scheduler; | ||||
|  |  | |||
|  | @ -1,311 +0,0 @@ | |||
| // Copyright 2018 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <optional> | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| // TODO(Rodrigo): Fine tune these numbers.
 | ||||
| constexpr std::size_t COMMAND_BUFFER_POOL_SIZE = 0x1000; | ||||
| constexpr std::size_t FENCES_GROW_STEP = 0x40; | ||||
| 
 | ||||
| constexpr VkFenceCreateInfo BuildFenceCreateInfo() { | ||||
|     return { | ||||
|         .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| class CommandBufferPool final : public VKFencedPool { | ||||
| public: | ||||
|     explicit CommandBufferPool(const VKDevice& device) | ||||
|         : VKFencedPool(COMMAND_BUFFER_POOL_SIZE), device{device} {} | ||||
| 
 | ||||
|     void Allocate(std::size_t begin, std::size_t end) override { | ||||
|         // Command buffers are going to be commited, recorded, executed every single usage cycle.
 | ||||
|         // They are also going to be reseted when commited.
 | ||||
|         Pool& pool = pools.emplace_back(); | ||||
|         pool.handle = device.GetLogical().CreateCommandPool({ | ||||
|             .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | ||||
|             .pNext = nullptr, | ||||
|             .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | | ||||
|                      VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, | ||||
|             .queueFamilyIndex = device.GetGraphicsFamily(), | ||||
|         }); | ||||
|         pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE); | ||||
|     } | ||||
| 
 | ||||
|     VkCommandBuffer Commit(VKFence& fence) { | ||||
|         const std::size_t index = CommitResource(fence); | ||||
|         const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE; | ||||
|         const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE; | ||||
|         return pools[pool_index].cmdbufs[sub_index]; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     struct Pool { | ||||
|         vk::CommandPool handle; | ||||
|         vk::CommandBuffers cmdbufs; | ||||
|     }; | ||||
| 
 | ||||
|     const VKDevice& device; | ||||
|     std::vector<Pool> pools; | ||||
| }; | ||||
| 
 | ||||
| VKResource::VKResource() = default; | ||||
| 
 | ||||
| VKResource::~VKResource() = default; | ||||
| 
 | ||||
| VKFence::VKFence(const VKDevice& device) | ||||
|     : device{device}, handle{device.GetLogical().CreateFence(BuildFenceCreateInfo())} {} | ||||
| 
 | ||||
| VKFence::~VKFence() = default; | ||||
| 
 | ||||
| void VKFence::Wait() { | ||||
|     switch (const VkResult result = handle.Wait()) { | ||||
|     case VK_SUCCESS: | ||||
|         return; | ||||
|     case VK_ERROR_DEVICE_LOST: | ||||
|         device.ReportLoss(); | ||||
|         [[fallthrough]]; | ||||
|     default: | ||||
|         throw vk::Exception(result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void VKFence::Release() { | ||||
|     ASSERT(is_owned); | ||||
|     is_owned = false; | ||||
| } | ||||
| 
 | ||||
| void VKFence::Commit() { | ||||
|     is_owned = true; | ||||
|     is_used = true; | ||||
| } | ||||
| 
 | ||||
| bool VKFence::Tick(bool gpu_wait, bool owner_wait) { | ||||
|     if (!is_used) { | ||||
|         // If a fence is not used it's always free.
 | ||||
|         return true; | ||||
|     } | ||||
|     if (is_owned && !owner_wait) { | ||||
|         // The fence is still being owned (Release has not been called) and ownership wait has
 | ||||
|         // not been asked.
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (gpu_wait) { | ||||
|         // Wait for the fence if it has been requested.
 | ||||
|         (void)handle.Wait(); | ||||
|     } else { | ||||
|         if (handle.GetStatus() != VK_SUCCESS) { | ||||
|             // Vulkan fence is not ready, not much it can do here
 | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Broadcast resources their free state.
 | ||||
|     for (auto* resource : protected_resources) { | ||||
|         resource->OnFenceRemoval(this); | ||||
|     } | ||||
|     protected_resources.clear(); | ||||
| 
 | ||||
|     // Prepare fence for reusage.
 | ||||
|     handle.Reset(); | ||||
|     is_used = false; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void VKFence::Protect(VKResource* resource) { | ||||
|     protected_resources.push_back(resource); | ||||
| } | ||||
| 
 | ||||
| void VKFence::Unprotect(VKResource* resource) { | ||||
|     const auto it = std::find(protected_resources.begin(), protected_resources.end(), resource); | ||||
|     ASSERT(it != protected_resources.end()); | ||||
| 
 | ||||
|     resource->OnFenceRemoval(this); | ||||
|     protected_resources.erase(it); | ||||
| } | ||||
| 
 | ||||
| void VKFence::RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept { | ||||
|     std::replace(std::begin(protected_resources), std::end(protected_resources), old_resource, | ||||
|                  new_resource); | ||||
| } | ||||
| 
 | ||||
| VKFenceWatch::VKFenceWatch() = default; | ||||
| 
 | ||||
| VKFenceWatch::VKFenceWatch(VKFence& initial_fence) { | ||||
|     Watch(initial_fence); | ||||
| } | ||||
| 
 | ||||
| VKFenceWatch::VKFenceWatch(VKFenceWatch&& rhs) noexcept { | ||||
|     fence = std::exchange(rhs.fence, nullptr); | ||||
|     if (fence) { | ||||
|         fence->RedirectProtection(&rhs, this); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| VKFenceWatch& VKFenceWatch::operator=(VKFenceWatch&& rhs) noexcept { | ||||
|     fence = std::exchange(rhs.fence, nullptr); | ||||
|     if (fence) { | ||||
|         fence->RedirectProtection(&rhs, this); | ||||
|     } | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| VKFenceWatch::~VKFenceWatch() { | ||||
|     if (fence) { | ||||
|         fence->Unprotect(this); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void VKFenceWatch::Wait() { | ||||
|     if (fence == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     fence->Wait(); | ||||
|     fence->Unprotect(this); | ||||
| } | ||||
| 
 | ||||
| void VKFenceWatch::Watch(VKFence& new_fence) { | ||||
|     Wait(); | ||||
|     fence = &new_fence; | ||||
|     fence->Protect(this); | ||||
| } | ||||
| 
 | ||||
| bool VKFenceWatch::TryWatch(VKFence& new_fence) { | ||||
|     if (fence) { | ||||
|         return false; | ||||
|     } | ||||
|     fence = &new_fence; | ||||
|     fence->Protect(this); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void VKFenceWatch::OnFenceRemoval(VKFence* signaling_fence) { | ||||
|     ASSERT_MSG(signaling_fence == fence, "Removing the wrong fence"); | ||||
|     fence = nullptr; | ||||
| } | ||||
| 
 | ||||
| VKFencedPool::VKFencedPool(std::size_t grow_step) : grow_step{grow_step} {} | ||||
| 
 | ||||
| VKFencedPool::~VKFencedPool() = default; | ||||
| 
 | ||||
| std::size_t VKFencedPool::CommitResource(VKFence& fence) { | ||||
|     const auto Search = [&](std::size_t begin, std::size_t end) -> std::optional<std::size_t> { | ||||
|         for (std::size_t iterator = begin; iterator < end; ++iterator) { | ||||
|             if (watches[iterator]->TryWatch(fence)) { | ||||
|                 // The resource is now being watched, a free resource was successfully found.
 | ||||
|                 return iterator; | ||||
|             } | ||||
|         } | ||||
|         return {}; | ||||
|     }; | ||||
|     // Try to find a free resource from the hinted position to the end.
 | ||||
|     auto found = Search(free_iterator, watches.size()); | ||||
|     if (!found) { | ||||
|         // Search from beginning to the hinted position.
 | ||||
|         found = Search(0, free_iterator); | ||||
|         if (!found) { | ||||
|             // Both searches failed, the pool is full; handle it.
 | ||||
|             const std::size_t free_resource = ManageOverflow(); | ||||
| 
 | ||||
|             // Watch will wait for the resource to be free.
 | ||||
|             watches[free_resource]->Watch(fence); | ||||
|             found = free_resource; | ||||
|         } | ||||
|     } | ||||
|     // Free iterator is hinted to the resource after the one that's been commited.
 | ||||
|     free_iterator = (*found + 1) % watches.size(); | ||||
|     return *found; | ||||
| } | ||||
| 
 | ||||
| std::size_t VKFencedPool::ManageOverflow() { | ||||
|     const std::size_t old_capacity = watches.size(); | ||||
|     Grow(); | ||||
| 
 | ||||
|     // The last entry is guaranted to be free, since it's the first element of the freshly
 | ||||
|     // allocated resources.
 | ||||
|     return old_capacity; | ||||
| } | ||||
| 
 | ||||
| void VKFencedPool::Grow() { | ||||
|     const std::size_t old_capacity = watches.size(); | ||||
|     watches.resize(old_capacity + grow_step); | ||||
|     std::generate(watches.begin() + old_capacity, watches.end(), | ||||
|                   []() { return std::make_unique<VKFenceWatch>(); }); | ||||
|     Allocate(old_capacity, old_capacity + grow_step); | ||||
| } | ||||
| 
 | ||||
| VKResourceManager::VKResourceManager(const VKDevice& device) : device{device} { | ||||
|     GrowFences(FENCES_GROW_STEP); | ||||
|     command_buffer_pool = std::make_unique<CommandBufferPool>(device); | ||||
| } | ||||
| 
 | ||||
| VKResourceManager::~VKResourceManager() = default; | ||||
| 
 | ||||
| VKFence& VKResourceManager::CommitFence() { | ||||
|     const auto StepFences = [&](bool gpu_wait, bool owner_wait) -> VKFence* { | ||||
|         const auto Tick = [=](auto& fence) { return fence->Tick(gpu_wait, owner_wait); }; | ||||
|         const auto hinted = fences.begin() + fences_iterator; | ||||
| 
 | ||||
|         auto it = std::find_if(hinted, fences.end(), Tick); | ||||
|         if (it == fences.end()) { | ||||
|             it = std::find_if(fences.begin(), hinted, Tick); | ||||
|             if (it == hinted) { | ||||
|                 return nullptr; | ||||
|             } | ||||
|         } | ||||
|         fences_iterator = std::distance(fences.begin(), it) + 1; | ||||
|         if (fences_iterator >= fences.size()) | ||||
|             fences_iterator = 0; | ||||
| 
 | ||||
|         auto& fence = *it; | ||||
|         fence->Commit(); | ||||
|         return fence.get(); | ||||
|     }; | ||||
| 
 | ||||
|     VKFence* found_fence = StepFences(false, false); | ||||
|     if (!found_fence) { | ||||
|         // Try again, this time waiting.
 | ||||
|         found_fence = StepFences(true, false); | ||||
| 
 | ||||
|         if (!found_fence) { | ||||
|             // Allocate new fences and try again.
 | ||||
|             LOG_INFO(Render_Vulkan, "Allocating new fences {} -> {}", fences.size(), | ||||
|                      fences.size() + FENCES_GROW_STEP); | ||||
| 
 | ||||
|             GrowFences(FENCES_GROW_STEP); | ||||
|             found_fence = StepFences(true, false); | ||||
|             ASSERT(found_fence != nullptr); | ||||
|         } | ||||
|     } | ||||
|     return *found_fence; | ||||
| } | ||||
| 
 | ||||
| VkCommandBuffer VKResourceManager::CommitCommandBuffer(VKFence& fence) { | ||||
|     return command_buffer_pool->Commit(fence); | ||||
| } | ||||
| 
 | ||||
| void VKResourceManager::GrowFences(std::size_t new_fences_count) { | ||||
|     const std::size_t previous_size = fences.size(); | ||||
|     fences.resize(previous_size + new_fences_count); | ||||
| 
 | ||||
|     std::generate(fences.begin() + previous_size, fences.end(), | ||||
|                   [this] { return std::make_unique<VKFence>(device); }); | ||||
| } | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
|  | @ -1,196 +0,0 @@ | |||
| // Copyright 2018 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <cstddef> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| class VKDevice; | ||||
| class VKFence; | ||||
| class VKResourceManager; | ||||
| 
 | ||||
| class CommandBufferPool; | ||||
| 
 | ||||
| /// Interface for a Vulkan resource
 | ||||
| class VKResource { | ||||
| public: | ||||
|     explicit VKResource(); | ||||
|     virtual ~VKResource(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Signals the object that an owning fence has been signaled. | ||||
|      * @param signaling_fence Fence that signals its usage end. | ||||
|      */ | ||||
|     virtual void OnFenceRemoval(VKFence* signaling_fence) = 0; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Fences take ownership of objects, protecting them from GPU-side or driver-side concurrent access. | ||||
|  * They must be commited from the resource manager. Their usage flow is: commit the fence from the | ||||
|  * resource manager, protect resources with it and use them, send the fence to an execution queue | ||||
|  * and Wait for it if needed and then call Release. Used resources will automatically be signaled | ||||
|  * when they are free to be reused. | ||||
|  * @brief Protects resources for concurrent usage and signals its release. | ||||
|  */ | ||||
| class VKFence { | ||||
|     friend class VKResourceManager; | ||||
| 
 | ||||
| public: | ||||
|     explicit VKFence(const VKDevice& device); | ||||
|     ~VKFence(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Waits for the fence to be signaled. | ||||
|      * @warning You must have ownership of the fence and it has to be previously sent to a queue to | ||||
|      * call this function. | ||||
|      */ | ||||
|     void Wait(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Releases ownership of the fence. Pass after it has been sent to an execution queue. | ||||
|      * Unmanaged usage of the fence after the call will result in undefined behavior because it may | ||||
|      * be being used for something else. | ||||
|      */ | ||||
|     void Release(); | ||||
| 
 | ||||
|     /// Protects a resource with this fence.
 | ||||
|     void Protect(VKResource* resource); | ||||
| 
 | ||||
|     /// Removes protection for a resource.
 | ||||
|     void Unprotect(VKResource* resource); | ||||
| 
 | ||||
|     /// Redirects one protected resource to a new address.
 | ||||
|     void RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept; | ||||
| 
 | ||||
|     /// Retreives the fence.
 | ||||
|     operator VkFence() const { | ||||
|         return *handle; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     /// Take ownership of the fence.
 | ||||
|     void Commit(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Updates the fence status. | ||||
|      * @warning Waiting for the owner might soft lock the execution. | ||||
|      * @param gpu_wait Wait for the fence to be signaled by the driver. | ||||
|      * @param owner_wait Wait for the owner to signal its freedom. | ||||
|      * @returns True if the fence is free. Waiting for gpu and owner will always return true. | ||||
|      */ | ||||
|     bool Tick(bool gpu_wait, bool owner_wait); | ||||
| 
 | ||||
|     const VKDevice& device;                       ///< Device handler
 | ||||
|     vk::Fence handle;                             ///< Vulkan fence
 | ||||
|     std::vector<VKResource*> protected_resources; ///< List of resources protected by this fence
 | ||||
|     bool is_owned = false; ///< The fence has been commited but not released yet.
 | ||||
|     bool is_used = false;  ///< The fence has been commited but it has not been checked to be free.
 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * A fence watch is used to keep track of the usage of a fence and protect a resource or set of | ||||
|  * resources without having to inherit VKResource from their handlers. | ||||
|  */ | ||||
| class VKFenceWatch final : public VKResource { | ||||
| public: | ||||
|     explicit VKFenceWatch(); | ||||
|     VKFenceWatch(VKFence& initial_fence); | ||||
|     VKFenceWatch(VKFenceWatch&&) noexcept; | ||||
|     VKFenceWatch(const VKFenceWatch&) = delete; | ||||
|     ~VKFenceWatch() override; | ||||
| 
 | ||||
|     VKFenceWatch& operator=(VKFenceWatch&&) noexcept; | ||||
| 
 | ||||
|     /// Waits for the fence to be released.
 | ||||
|     void Wait(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Waits for a previous fence and watches a new one. | ||||
|      * @param new_fence New fence to wait to. | ||||
|      */ | ||||
|     void Watch(VKFence& new_fence); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Checks if it's currently being watched and starts watching it if it's available. | ||||
|      * @returns True if a watch has started, false if it's being watched. | ||||
|      */ | ||||
|     bool TryWatch(VKFence& new_fence); | ||||
| 
 | ||||
|     void OnFenceRemoval(VKFence* signaling_fence) override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Do not use it paired with Watch. Use TryWatch instead. | ||||
|      * Returns true when the watch is free. | ||||
|      */ | ||||
|     bool IsUsed() const { | ||||
|         return fence != nullptr; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     VKFence* fence{}; ///< Fence watching this resource. nullptr when the watch is free.
 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Handles a pool of resources protected by fences. Manages resource overflow allocating more | ||||
|  * resources. | ||||
|  */ | ||||
| class VKFencedPool { | ||||
| public: | ||||
|     explicit VKFencedPool(std::size_t grow_step); | ||||
|     virtual ~VKFencedPool(); | ||||
| 
 | ||||
| protected: | ||||
|     /**
 | ||||
|      * Commits a free resource and protects it with a fence. It may allocate new resources. | ||||
|      * @param fence Fence that protects the commited resource. | ||||
|      * @returns Index of the resource commited. | ||||
|      */ | ||||
|     std::size_t CommitResource(VKFence& fence); | ||||
| 
 | ||||
|     /// Called when a chunk of resources have to be allocated.
 | ||||
|     virtual void Allocate(std::size_t begin, std::size_t end) = 0; | ||||
| 
 | ||||
| private: | ||||
|     /// Manages pool overflow allocating new resources.
 | ||||
|     std::size_t ManageOverflow(); | ||||
| 
 | ||||
|     /// Allocates a new page of resources.
 | ||||
|     void Grow(); | ||||
| 
 | ||||
|     std::size_t grow_step = 0;     ///< Number of new resources created after an overflow
 | ||||
|     std::size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
 | ||||
|     std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Set of watched resources
 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * The resource manager handles all resources that can be protected with a fence avoiding | ||||
|  * driver-side or GPU-side concurrent usage. Usage is documented in VKFence. | ||||
|  */ | ||||
| class VKResourceManager final { | ||||
| public: | ||||
|     explicit VKResourceManager(const VKDevice& device); | ||||
|     ~VKResourceManager(); | ||||
| 
 | ||||
|     /// Commits a fence. It has to be sent to a queue and released.
 | ||||
|     VKFence& CommitFence(); | ||||
| 
 | ||||
|     /// Commits an unused command buffer and protects it with a fence.
 | ||||
|     VkCommandBuffer CommitCommandBuffer(VKFence& fence); | ||||
| 
 | ||||
| private: | ||||
|     /// Allocates new fences.
 | ||||
|     void GrowFences(std::size_t new_fences_count); | ||||
| 
 | ||||
|     const VKDevice& device;          ///< Device handler.
 | ||||
|     std::size_t fences_iterator = 0; ///< Index where a free fence is likely to be found.
 | ||||
|     std::vector<std::unique_ptr<VKFence>> fences;           ///< Pool of fences.
 | ||||
|     std::unique_ptr<CommandBufferPool> command_buffer_pool; ///< Pool of command buffers.
 | ||||
| }; | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
							
								
								
									
										63
									
								
								src/video_core/renderer_vulkan/vk_resource_pool.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/video_core/renderer_vulkan/vk_resource_pool.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <optional> | ||||
| 
 | ||||
| #include "video_core/renderer_vulkan/vk_master_semaphore.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_pool.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| ResourcePool::ResourcePool(MasterSemaphore& master_semaphore_, size_t grow_step_) | ||||
|     : master_semaphore{master_semaphore_}, grow_step{grow_step_} {} | ||||
| 
 | ||||
| ResourcePool::~ResourcePool() = default; | ||||
| 
 | ||||
| size_t ResourcePool::CommitResource() { | ||||
|     // Refresh semaphore to query updated results
 | ||||
|     master_semaphore.Refresh(); | ||||
| 
 | ||||
|     const auto search = [this](size_t begin, size_t end) -> std::optional<size_t> { | ||||
|         for (size_t iterator = begin; iterator < end; ++iterator) { | ||||
|             if (master_semaphore.IsFree(ticks[iterator])) { | ||||
|                 ticks[iterator] = master_semaphore.CurrentTick(); | ||||
|                 return iterator; | ||||
|             } | ||||
|         } | ||||
|         return {}; | ||||
|     }; | ||||
|     // Try to find a free resource from the hinted position to the end.
 | ||||
|     auto found = search(free_iterator, ticks.size()); | ||||
|     if (!found) { | ||||
|         // Search from beginning to the hinted position.
 | ||||
|         found = search(0, free_iterator); | ||||
|         if (!found) { | ||||
|             // Both searches failed, the pool is full; handle it.
 | ||||
|             const size_t free_resource = ManageOverflow(); | ||||
| 
 | ||||
|             ticks[free_resource] = master_semaphore.CurrentTick(); | ||||
|             found = free_resource; | ||||
|         } | ||||
|     } | ||||
|     // Free iterator is hinted to the resource after the one that's been commited.
 | ||||
|     free_iterator = (*found + 1) % ticks.size(); | ||||
|     return *found; | ||||
| } | ||||
| 
 | ||||
| size_t ResourcePool::ManageOverflow() { | ||||
|     const size_t old_capacity = ticks.size(); | ||||
|     Grow(); | ||||
| 
 | ||||
|     // The last entry is guaranted to be free, since it's the first element of the freshly
 | ||||
|     // allocated resources.
 | ||||
|     return old_capacity; | ||||
| } | ||||
| 
 | ||||
| void ResourcePool::Grow() { | ||||
|     const size_t old_capacity = ticks.size(); | ||||
|     ticks.resize(old_capacity + grow_step); | ||||
|     Allocate(old_capacity, old_capacity + grow_step); | ||||
| } | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
							
								
								
									
										43
									
								
								src/video_core/renderer_vulkan/vk_resource_pool.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/video_core/renderer_vulkan/vk_resource_pool.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| class MasterSemaphore; | ||||
| 
 | ||||
| /**
 | ||||
|  * Handles a pool of resources protected by fences. Manages resource overflow allocating more | ||||
|  * resources. | ||||
|  */ | ||||
| class ResourcePool { | ||||
| public: | ||||
|     explicit ResourcePool(MasterSemaphore& master_semaphore, size_t grow_step); | ||||
|     virtual ~ResourcePool(); | ||||
| 
 | ||||
| protected: | ||||
|     size_t CommitResource(); | ||||
| 
 | ||||
|     /// Called when a chunk of resources have to be allocated.
 | ||||
|     virtual void Allocate(size_t begin, size_t end) = 0; | ||||
| 
 | ||||
| private: | ||||
|     /// Manages pool overflow allocating new resources.
 | ||||
|     size_t ManageOverflow(); | ||||
| 
 | ||||
|     /// Allocates a new page of resources.
 | ||||
|     void Grow(); | ||||
| 
 | ||||
|     MasterSemaphore& master_semaphore; | ||||
|     size_t grow_step = 0;     ///< Number of new resources created after an overflow
 | ||||
|     size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
 | ||||
|     std::vector<u64> ticks;   ///< Ticks for each resource
 | ||||
| }; | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
|  | @ -10,9 +10,10 @@ | |||
| 
 | ||||
| #include "common/microprofile.h" | ||||
| #include "common/thread.h" | ||||
| #include "video_core/renderer_vulkan/vk_command_pool.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_master_semaphore.h" | ||||
| #include "video_core/renderer_vulkan/vk_query_cache.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_state_tracker.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
|  | @ -35,10 +36,10 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) { | |||
|     last = nullptr; | ||||
| } | ||||
| 
 | ||||
| VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager, | ||||
|                          StateTracker& state_tracker) | ||||
|     : device{device}, resource_manager{resource_manager}, state_tracker{state_tracker}, | ||||
|       next_fence{&resource_manager.CommitFence()} { | ||||
| VKScheduler::VKScheduler(const VKDevice& device_, StateTracker& state_tracker_) | ||||
|     : device{device_}, state_tracker{state_tracker_}, | ||||
|       master_semaphore{std::make_unique<MasterSemaphore>(device)}, | ||||
|       command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} { | ||||
|     AcquireNewChunk(); | ||||
|     AllocateNewContext(); | ||||
|     worker_thread = std::thread(&VKScheduler::WorkerThread, this); | ||||
|  | @ -50,20 +51,27 @@ VKScheduler::~VKScheduler() { | |||
|     worker_thread.join(); | ||||
| } | ||||
| 
 | ||||
| void VKScheduler::Flush(bool release_fence, VkSemaphore semaphore) { | ||||
|     SubmitExecution(semaphore); | ||||
|     if (release_fence) { | ||||
|         current_fence->Release(); | ||||
| u64 VKScheduler::CurrentTick() const noexcept { | ||||
|     return master_semaphore->CurrentTick(); | ||||
| } | ||||
| 
 | ||||
| bool VKScheduler::IsFree(u64 tick) const noexcept { | ||||
|     return master_semaphore->IsFree(tick); | ||||
| } | ||||
| 
 | ||||
| void VKScheduler::Wait(u64 tick) { | ||||
|     master_semaphore->Wait(tick); | ||||
| } | ||||
| 
 | ||||
| void VKScheduler::Flush(VkSemaphore semaphore) { | ||||
|     SubmitExecution(semaphore); | ||||
|     AllocateNewContext(); | ||||
| } | ||||
| 
 | ||||
| void VKScheduler::Finish(bool release_fence, VkSemaphore semaphore) { | ||||
| void VKScheduler::Finish(VkSemaphore semaphore) { | ||||
|     const u64 presubmit_tick = CurrentTick(); | ||||
|     SubmitExecution(semaphore); | ||||
|     current_fence->Wait(); | ||||
|     if (release_fence) { | ||||
|         current_fence->Release(); | ||||
|     } | ||||
|     Wait(presubmit_tick); | ||||
|     AllocateNewContext(); | ||||
| } | ||||
| 
 | ||||
|  | @ -160,18 +168,38 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) { | |||
| 
 | ||||
|     current_cmdbuf.End(); | ||||
| 
 | ||||
|     const VkSemaphore timeline_semaphore = master_semaphore->Handle(); | ||||
|     const u32 num_signal_semaphores = semaphore ? 2U : 1U; | ||||
| 
 | ||||
|     const u64 signal_value = master_semaphore->CurrentTick(); | ||||
|     const u64 wait_value = signal_value - 1; | ||||
|     const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; | ||||
| 
 | ||||
|     master_semaphore->NextTick(); | ||||
| 
 | ||||
|     const std::array signal_values{signal_value, u64(0)}; | ||||
|     const std::array signal_semaphores{timeline_semaphore, semaphore}; | ||||
| 
 | ||||
|     const VkTimelineSemaphoreSubmitInfoKHR timeline_si{ | ||||
|         .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, | ||||
|         .pNext = nullptr, | ||||
|         .waitSemaphoreValueCount = 1, | ||||
|         .pWaitSemaphoreValues = &wait_value, | ||||
|         .signalSemaphoreValueCount = num_signal_semaphores, | ||||
|         .pSignalSemaphoreValues = signal_values.data(), | ||||
|     }; | ||||
|     const VkSubmitInfo submit_info{ | ||||
|         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .waitSemaphoreCount = 0, | ||||
|         .pWaitSemaphores = nullptr, | ||||
|         .pWaitDstStageMask = nullptr, | ||||
|         .pNext = &timeline_si, | ||||
|         .waitSemaphoreCount = 1, | ||||
|         .pWaitSemaphores = &timeline_semaphore, | ||||
|         .pWaitDstStageMask = &wait_stage_mask, | ||||
|         .commandBufferCount = 1, | ||||
|         .pCommandBuffers = current_cmdbuf.address(), | ||||
|         .signalSemaphoreCount = semaphore ? 1U : 0U, | ||||
|         .pSignalSemaphores = &semaphore, | ||||
|         .signalSemaphoreCount = num_signal_semaphores, | ||||
|         .pSignalSemaphores = signal_semaphores.data(), | ||||
|     }; | ||||
|     switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) { | ||||
|     switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) { | ||||
|     case VK_SUCCESS: | ||||
|         break; | ||||
|     case VK_ERROR_DEVICE_LOST: | ||||
|  | @ -183,14 +211,9 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) { | |||
| } | ||||
| 
 | ||||
| void VKScheduler::AllocateNewContext() { | ||||
|     ++ticks; | ||||
| 
 | ||||
|     std::unique_lock lock{mutex}; | ||||
|     current_fence = next_fence; | ||||
|     next_fence = &resource_manager.CommitFence(); | ||||
| 
 | ||||
|     current_cmdbuf = vk::CommandBuffer(resource_manager.CommitCommandBuffer(*current_fence), | ||||
|                                        device.GetDispatchLoader()); | ||||
|     current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader()); | ||||
|     current_cmdbuf.Begin({ | ||||
|         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | ||||
|         .pNext = nullptr, | ||||
|  |  | |||
|  | @ -16,42 +16,33 @@ | |||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| class CommandPool; | ||||
| class MasterSemaphore; | ||||
| class StateTracker; | ||||
| class VKDevice; | ||||
| class VKFence; | ||||
| class VKQueryCache; | ||||
| class VKResourceManager; | ||||
| 
 | ||||
| class VKFenceView { | ||||
| public: | ||||
|     VKFenceView() = default; | ||||
|     VKFenceView(VKFence* const& fence) : fence{fence} {} | ||||
| 
 | ||||
|     VKFence* operator->() const noexcept { | ||||
|         return fence; | ||||
|     } | ||||
| 
 | ||||
|     operator VKFence&() const noexcept { | ||||
|         return *fence; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     VKFence* const& fence; | ||||
| }; | ||||
| 
 | ||||
| /// The scheduler abstracts command buffer and fence management with an interface that's able to do
 | ||||
| /// OpenGL-like operations on Vulkan command buffers.
 | ||||
| class VKScheduler { | ||||
| public: | ||||
|     explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager, | ||||
|                          StateTracker& state_tracker); | ||||
|     explicit VKScheduler(const VKDevice& device, StateTracker& state_tracker); | ||||
|     ~VKScheduler(); | ||||
| 
 | ||||
|     /// Returns the current command buffer tick.
 | ||||
|     [[nodiscard]] u64 CurrentTick() const noexcept; | ||||
| 
 | ||||
|     /// Returns true when a tick has been triggered by the GPU.
 | ||||
|     [[nodiscard]] bool IsFree(u64 tick) const noexcept; | ||||
| 
 | ||||
|     /// Waits for the given tick to trigger on the GPU.
 | ||||
|     void Wait(u64 tick); | ||||
| 
 | ||||
|     /// Sends the current execution context to the GPU.
 | ||||
|     void Flush(bool release_fence = true, VkSemaphore semaphore = nullptr); | ||||
|     void Flush(VkSemaphore semaphore = nullptr); | ||||
| 
 | ||||
|     /// Sends the current execution context to the GPU and waits for it to complete.
 | ||||
|     void Finish(bool release_fence = true, VkSemaphore semaphore = nullptr); | ||||
|     void Finish(VkSemaphore semaphore = nullptr); | ||||
| 
 | ||||
|     /// Waits for the worker thread to finish executing everything. After this function returns it's
 | ||||
|     /// safe to touch worker resources.
 | ||||
|  | @ -86,14 +77,9 @@ public: | |||
|         (void)chunk->Record(command); | ||||
|     } | ||||
| 
 | ||||
|     /// Gets a reference to the current fence.
 | ||||
|     VKFenceView GetFence() const { | ||||
|         return current_fence; | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the current command buffer tick.
 | ||||
|     u64 Ticks() const { | ||||
|         return ticks; | ||||
|     /// Returns the master timeline semaphore.
 | ||||
|     [[nodiscard]] MasterSemaphore& GetMasterSemaphore() const noexcept { | ||||
|         return *master_semaphore; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|  | @ -171,6 +157,13 @@ private: | |||
|         std::array<u8, 0x8000> data{}; | ||||
|     }; | ||||
| 
 | ||||
|     struct State { | ||||
|         VkRenderPass renderpass = nullptr; | ||||
|         VkFramebuffer framebuffer = nullptr; | ||||
|         VkExtent2D render_area = {0, 0}; | ||||
|         VkPipeline graphics_pipeline = nullptr; | ||||
|     }; | ||||
| 
 | ||||
|     void WorkerThread(); | ||||
| 
 | ||||
|     void SubmitExecution(VkSemaphore semaphore); | ||||
|  | @ -186,30 +179,23 @@ private: | |||
|     void AcquireNewChunk(); | ||||
| 
 | ||||
|     const VKDevice& device; | ||||
|     VKResourceManager& resource_manager; | ||||
|     StateTracker& state_tracker; | ||||
| 
 | ||||
|     std::unique_ptr<MasterSemaphore> master_semaphore; | ||||
|     std::unique_ptr<CommandPool> command_pool; | ||||
| 
 | ||||
|     VKQueryCache* query_cache = nullptr; | ||||
| 
 | ||||
|     vk::CommandBuffer current_cmdbuf; | ||||
|     VKFence* current_fence = nullptr; | ||||
|     VKFence* next_fence = nullptr; | ||||
| 
 | ||||
|     struct State { | ||||
|         VkRenderPass renderpass = nullptr; | ||||
|         VkFramebuffer framebuffer = nullptr; | ||||
|         VkExtent2D render_area = {0, 0}; | ||||
|         VkPipeline graphics_pipeline = nullptr; | ||||
|     } state; | ||||
| 
 | ||||
|     std::unique_ptr<CommandChunk> chunk; | ||||
|     std::thread worker_thread; | ||||
| 
 | ||||
|     State state; | ||||
|     Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_queue; | ||||
|     Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve; | ||||
|     std::mutex mutex; | ||||
|     std::condition_variable cv; | ||||
|     std::atomic<u64> ticks = 0; | ||||
|     bool quit = false; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,36 +10,18 @@ | |||
| #include "common/bit_util.h" | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, | ||||
|                                                   u64 last_epoch) | ||||
|     : buffer{std::move(buffer)}, watch{fence}, last_epoch{last_epoch} {} | ||||
| VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_) | ||||
|     : buffer{std::move(buffer_)} {} | ||||
| 
 | ||||
| VKStagingBufferPool::StagingBuffer::StagingBuffer(StagingBuffer&& rhs) noexcept { | ||||
|     buffer = std::move(rhs.buffer); | ||||
|     watch = std::move(rhs.watch); | ||||
|     last_epoch = rhs.last_epoch; | ||||
| } | ||||
| 
 | ||||
| VKStagingBufferPool::StagingBuffer::~StagingBuffer() = default; | ||||
| 
 | ||||
| VKStagingBufferPool::StagingBuffer& VKStagingBufferPool::StagingBuffer::operator=( | ||||
|     StagingBuffer&& rhs) noexcept { | ||||
|     buffer = std::move(rhs.buffer); | ||||
|     watch = std::move(rhs.watch); | ||||
|     last_epoch = rhs.last_epoch; | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager, | ||||
|                                          VKScheduler& scheduler) | ||||
|     : device{device}, memory_manager{memory_manager}, scheduler{scheduler} {} | ||||
| VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device_, VKMemoryManager& memory_manager_, | ||||
|                                          VKScheduler& scheduler_) | ||||
|     : device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {} | ||||
| 
 | ||||
| VKStagingBufferPool::~VKStagingBufferPool() = default; | ||||
| 
 | ||||
|  | @ -51,7 +33,6 @@ VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visib | |||
| } | ||||
| 
 | ||||
| void VKStagingBufferPool::TickFrame() { | ||||
|     ++epoch; | ||||
|     current_delete_level = (current_delete_level + 1) % NumLevels; | ||||
| 
 | ||||
|     ReleaseCache(true); | ||||
|  | @ -59,11 +40,12 @@ void VKStagingBufferPool::TickFrame() { | |||
| } | ||||
| 
 | ||||
| VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) { | ||||
|     for (auto& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) { | ||||
|         if (entry.watch.TryWatch(scheduler.GetFence())) { | ||||
|             entry.last_epoch = epoch; | ||||
|             return &*entry.buffer; | ||||
|     for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) { | ||||
|         if (!scheduler.IsFree(entry.tick)) { | ||||
|             continue; | ||||
|         } | ||||
|         entry.tick = scheduler.CurrentTick(); | ||||
|         return &*entry.buffer; | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
|  | @ -86,8 +68,10 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v | |||
|     }); | ||||
|     buffer->commit = memory_manager.Commit(buffer->handle, host_visible); | ||||
| 
 | ||||
|     auto& entries = GetCache(host_visible)[log2].entries; | ||||
|     return *entries.emplace_back(std::move(buffer), scheduler.GetFence(), epoch).buffer; | ||||
|     std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries; | ||||
|     StagingBuffer& entry = entries.emplace_back(std::move(buffer)); | ||||
|     entry.tick = scheduler.CurrentTick(); | ||||
|     return *entry.buffer; | ||||
| } | ||||
| 
 | ||||
| VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) { | ||||
|  | @ -109,9 +93,8 @@ u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t lo | |||
|     auto& entries = staging.entries; | ||||
|     const std::size_t old_size = entries.size(); | ||||
| 
 | ||||
|     const auto is_deleteable = [this](const auto& entry) { | ||||
|         static constexpr u64 epochs_to_destroy = 180; | ||||
|         return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed(); | ||||
|     const auto is_deleteable = [this](const StagingBuffer& entry) { | ||||
|         return scheduler.IsFree(entry.tick); | ||||
|     }; | ||||
|     const std::size_t begin_offset = staging.delete_index; | ||||
|     const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); | ||||
|  |  | |||
|  | @ -10,13 +10,11 @@ | |||
| #include "common/common_types.h" | ||||
| 
 | ||||
| #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| class VKDevice; | ||||
| class VKFenceWatch; | ||||
| class VKScheduler; | ||||
| 
 | ||||
| struct VKBuffer final { | ||||
|  | @ -36,16 +34,10 @@ public: | |||
| 
 | ||||
| private: | ||||
|     struct StagingBuffer final { | ||||
|         explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, u64 last_epoch); | ||||
|         StagingBuffer(StagingBuffer&& rhs) noexcept; | ||||
|         StagingBuffer(const StagingBuffer&) = delete; | ||||
|         ~StagingBuffer(); | ||||
| 
 | ||||
|         StagingBuffer& operator=(StagingBuffer&& rhs) noexcept; | ||||
|         explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer); | ||||
| 
 | ||||
|         std::unique_ptr<VKBuffer> buffer; | ||||
|         VKFenceWatch watch; | ||||
|         u64 last_epoch = 0; | ||||
|         u64 tick = 0; | ||||
|     }; | ||||
| 
 | ||||
|     struct StagingBuffers final { | ||||
|  | @ -73,8 +65,6 @@ private: | |||
|     StagingBuffersCache host_staging_buffers; | ||||
|     StagingBuffersCache device_staging_buffers; | ||||
| 
 | ||||
|     u64 epoch = 0; | ||||
| 
 | ||||
|     std::size_t current_delete_level = 0; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,7 +11,6 @@ | |||
| #include "common/alignment.h" | ||||
| #include "common/assert.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_stream_buffer.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
|  | @ -111,7 +110,7 @@ void VKStreamBuffer::Unmap(u64 size) { | |||
|     } | ||||
|     auto& watch = current_watches[current_watch_cursor++]; | ||||
|     watch.upper_bound = offset; | ||||
|     watch.fence.Watch(scheduler.GetFence()); | ||||
|     watch.tick = scheduler.CurrentTick(); | ||||
| } | ||||
| 
 | ||||
| void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) { | ||||
|  | @ -157,7 +156,7 @@ void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) { | |||
|     while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) { | ||||
|         auto& watch = previous_watches[wait_cursor]; | ||||
|         wait_bound = watch.upper_bound; | ||||
|         watch.fence.Wait(); | ||||
|         scheduler.Wait(watch.tick); | ||||
|         ++wait_cursor; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -14,7 +14,6 @@ | |||
| namespace Vulkan { | ||||
| 
 | ||||
| class VKDevice; | ||||
| class VKFence; | ||||
| class VKFenceWatch; | ||||
| class VKScheduler; | ||||
| 
 | ||||
|  | @ -44,8 +43,8 @@ public: | |||
|     } | ||||
| 
 | ||||
| private: | ||||
|     struct Watch final { | ||||
|         VKFenceWatch fence; | ||||
|     struct Watch { | ||||
|         u64 tick{}; | ||||
|         u64 upper_bound{}; | ||||
|     }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| #include "core/core.h" | ||||
| #include "core/frontend/framebuffer_layout.h" | ||||
| #include "video_core/renderer_vulkan/vk_device.h" | ||||
| #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_swapchain.h" | ||||
| #include "video_core/renderer_vulkan/wrapper.h" | ||||
| 
 | ||||
|  | @ -56,8 +56,8 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi | |||
| 
 | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| VKSwapchain::VKSwapchain(VkSurfaceKHR surface, const VKDevice& device) | ||||
|     : surface{surface}, device{device} {} | ||||
| VKSwapchain::VKSwapchain(VkSurfaceKHR surface_, const VKDevice& device_, VKScheduler& scheduler_) | ||||
|     : surface{surface_}, device{device_}, scheduler{scheduler_} {} | ||||
| 
 | ||||
| VKSwapchain::~VKSwapchain() = default; | ||||
| 
 | ||||
|  | @ -75,21 +75,18 @@ void VKSwapchain::Create(u32 width, u32 height, bool srgb) { | |||
|     CreateSemaphores(); | ||||
|     CreateImageViews(); | ||||
| 
 | ||||
|     fences.resize(image_count, nullptr); | ||||
|     resource_ticks.clear(); | ||||
|     resource_ticks.resize(image_count); | ||||
| } | ||||
| 
 | ||||
| void VKSwapchain::AcquireNextImage() { | ||||
|     device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(), | ||||
|                                             *present_semaphores[frame_index], {}, &image_index); | ||||
| 
 | ||||
|     if (auto& fence = fences[image_index]; fence) { | ||||
|         fence->Wait(); | ||||
|         fence->Release(); | ||||
|         fence = nullptr; | ||||
|     } | ||||
|     scheduler.Wait(resource_ticks[image_index]); | ||||
| } | ||||
| 
 | ||||
| bool VKSwapchain::Present(VkSemaphore render_semaphore, VKFence& fence) { | ||||
| bool VKSwapchain::Present(VkSemaphore render_semaphore) { | ||||
|     const VkSemaphore present_semaphore{*present_semaphores[frame_index]}; | ||||
|     const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore}; | ||||
|     const auto present_queue{device.GetPresentQueue()}; | ||||
|  | @ -123,8 +120,7 @@ bool VKSwapchain::Present(VkSemaphore render_semaphore, VKFence& fence) { | |||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     ASSERT(fences[image_index] == nullptr); | ||||
|     fences[image_index] = &fence; | ||||
|     resource_ticks[image_index] = scheduler.CurrentTick(); | ||||
|     frame_index = (frame_index + 1) % static_cast<u32>(image_count); | ||||
|     return recreated; | ||||
| } | ||||
|  |  | |||
|  | @ -16,11 +16,11 @@ struct FramebufferLayout; | |||
| namespace Vulkan { | ||||
| 
 | ||||
| class VKDevice; | ||||
| class VKFence; | ||||
| class VKScheduler; | ||||
| 
 | ||||
| class VKSwapchain { | ||||
| public: | ||||
|     explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device); | ||||
|     explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device, VKScheduler& scheduler); | ||||
|     ~VKSwapchain(); | ||||
| 
 | ||||
|     /// Creates (or recreates) the swapchain with a given size.
 | ||||
|  | @ -31,7 +31,7 @@ public: | |||
| 
 | ||||
|     /// Presents the rendered image to the swapchain. Returns true when the swapchains had to be
 | ||||
|     /// recreated. Takes responsability for the ownership of fence.
 | ||||
|     bool Present(VkSemaphore render_semaphore, VKFence& fence); | ||||
|     bool Present(VkSemaphore render_semaphore); | ||||
| 
 | ||||
|     /// Returns true when the framebuffer layout has changed.
 | ||||
|     bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const; | ||||
|  | @ -74,6 +74,7 @@ private: | |||
| 
 | ||||
|     const VkSurfaceKHR surface; | ||||
|     const VKDevice& device; | ||||
|     VKScheduler& scheduler; | ||||
| 
 | ||||
|     vk::SwapchainKHR swapchain; | ||||
| 
 | ||||
|  | @ -81,7 +82,7 @@ private: | |||
|     std::vector<VkImage> images; | ||||
|     std::vector<vk::ImageView> image_views; | ||||
|     std::vector<vk::Framebuffer> framebuffers; | ||||
|     std::vector<VKFence*> fences; | ||||
|     std::vector<u64> resource_ticks; | ||||
|     std::vector<vk::Semaphore> present_semaphores; | ||||
| 
 | ||||
|     u32 image_index{}; | ||||
|  |  | |||
|  | @ -188,13 +188,11 @@ u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, Tegra::Texture::Swizzl | |||
| 
 | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| CachedSurface::CachedSurface(const VKDevice& device, VKResourceManager& resource_manager, | ||||
|                              VKMemoryManager& memory_manager, VKScheduler& scheduler, | ||||
|                              VKStagingBufferPool& staging_pool, GPUVAddr gpu_addr, | ||||
|                              const SurfaceParams& params) | ||||
| CachedSurface::CachedSurface(const VKDevice& device, VKMemoryManager& memory_manager, | ||||
|                              VKScheduler& scheduler, VKStagingBufferPool& staging_pool, | ||||
|                              GPUVAddr gpu_addr, const SurfaceParams& params) | ||||
|     : SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, device{device}, | ||||
|       resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler}, | ||||
|       staging_pool{staging_pool} { | ||||
|       memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} { | ||||
|     if (params.IsBuffer()) { | ||||
|         buffer = CreateBuffer(device, params, host_memory_size); | ||||
|         commit = memory_manager.Commit(buffer, false); | ||||
|  | @ -493,18 +491,17 @@ VkImageView CachedSurfaceView::GetAttachment() { | |||
| VKTextureCache::VKTextureCache(VideoCore::RasterizerInterface& rasterizer, | ||||
|                                Tegra::Engines::Maxwell3D& maxwell3d, | ||||
|                                Tegra::MemoryManager& gpu_memory, const VKDevice& device_, | ||||
|                                VKResourceManager& resource_manager_, | ||||
|                                VKMemoryManager& memory_manager_, VKScheduler& scheduler_, | ||||
|                                VKStagingBufferPool& staging_pool_) | ||||
|     : TextureCache(rasterizer, maxwell3d, gpu_memory, device_.IsOptimalAstcSupported()), | ||||
|       device{device_}, resource_manager{resource_manager_}, | ||||
|       memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{staging_pool_} {} | ||||
|       device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{ | ||||
|                                                                                    staging_pool_} {} | ||||
| 
 | ||||
| VKTextureCache::~VKTextureCache() = default; | ||||
| 
 | ||||
| Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) { | ||||
|     return std::make_shared<CachedSurface>(device, resource_manager, memory_manager, scheduler, | ||||
|                                            staging_pool, gpu_addr, params); | ||||
|     return std::make_shared<CachedSurface>(device, memory_manager, scheduler, staging_pool, | ||||
|                                            gpu_addr, params); | ||||
| } | ||||
| 
 | ||||
| void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface, | ||||
|  |  | |||
|  | @ -23,7 +23,6 @@ namespace Vulkan { | |||
| 
 | ||||
| class RasterizerVulkan; | ||||
| class VKDevice; | ||||
| class VKResourceManager; | ||||
| class VKScheduler; | ||||
| class VKStagingBufferPool; | ||||
| 
 | ||||
|  | @ -41,10 +40,9 @@ class CachedSurface final : public VideoCommon::SurfaceBase<View> { | |||
|     friend CachedSurfaceView; | ||||
| 
 | ||||
| public: | ||||
|     explicit CachedSurface(const VKDevice& device, VKResourceManager& resource_manager, | ||||
|                            VKMemoryManager& memory_manager, VKScheduler& scheduler, | ||||
|                            VKStagingBufferPool& staging_pool, GPUVAddr gpu_addr, | ||||
|                            const SurfaceParams& params); | ||||
|     explicit CachedSurface(const VKDevice& device, VKMemoryManager& memory_manager, | ||||
|                            VKScheduler& scheduler, VKStagingBufferPool& staging_pool, | ||||
|                            GPUVAddr gpu_addr, const SurfaceParams& params); | ||||
|     ~CachedSurface(); | ||||
| 
 | ||||
|     void UploadTexture(const std::vector<u8>& staging_buffer) override; | ||||
|  | @ -98,7 +96,6 @@ private: | |||
|     VkImageSubresourceRange GetImageSubresourceRange() const; | ||||
| 
 | ||||
|     const VKDevice& device; | ||||
|     VKResourceManager& resource_manager; | ||||
|     VKMemoryManager& memory_manager; | ||||
|     VKScheduler& scheduler; | ||||
|     VKStagingBufferPool& staging_pool; | ||||
|  | @ -198,9 +195,8 @@ class VKTextureCache final : public TextureCacheBase { | |||
| public: | ||||
|     explicit VKTextureCache(VideoCore::RasterizerInterface& rasterizer, | ||||
|                             Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory, | ||||
|                             const VKDevice& device, VKResourceManager& resource_manager, | ||||
|                             VKMemoryManager& memory_manager, VKScheduler& scheduler, | ||||
|                             VKStagingBufferPool& staging_pool); | ||||
|                             const VKDevice& device, VKMemoryManager& memory_manager, | ||||
|                             VKScheduler& scheduler, VKStagingBufferPool& staging_pool); | ||||
|     ~VKTextureCache(); | ||||
| 
 | ||||
| private: | ||||
|  | @ -215,7 +211,6 @@ private: | |||
|     void BufferCopy(Surface& src_surface, Surface& dst_surface) override; | ||||
| 
 | ||||
|     const VKDevice& device; | ||||
|     VKResourceManager& resource_manager; | ||||
|     VKMemoryManager& memory_manager; | ||||
|     VKScheduler& scheduler; | ||||
|     VKStagingBufferPool& staging_pool; | ||||
|  |  | |||
|  | @ -148,6 +148,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
|     X(vkGetFenceStatus); | ||||
|     X(vkGetImageMemoryRequirements); | ||||
|     X(vkGetQueryPoolResults); | ||||
|     X(vkGetSemaphoreCounterValueKHR); | ||||
|     X(vkMapMemory); | ||||
|     X(vkQueueSubmit); | ||||
|     X(vkResetFences); | ||||
|  | @ -156,6 +157,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
|     X(vkUpdateDescriptorSetWithTemplateKHR); | ||||
|     X(vkUpdateDescriptorSets); | ||||
|     X(vkWaitForFences); | ||||
|     X(vkWaitSemaphoresKHR); | ||||
| #undef X | ||||
| } | ||||
| 
 | ||||
|  | @ -574,7 +576,10 @@ Semaphore Device::CreateSemaphore() const { | |||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|     }; | ||||
|     return CreateSemaphore(ci); | ||||
| } | ||||
| 
 | ||||
| Semaphore Device::CreateSemaphore(const VkSemaphoreCreateInfo& ci) const { | ||||
|     VkSemaphore object; | ||||
|     Check(dld->vkCreateSemaphore(handle, &ci, nullptr, &object)); | ||||
|     return Semaphore(object, handle, *dld); | ||||
|  | @ -660,7 +665,7 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons | |||
|     return ShaderModule(object, handle, *dld); | ||||
| } | ||||
| 
 | ||||
| Event Device::CreateNewEvent() const { | ||||
| Event Device::CreateEvent() const { | ||||
|     static constexpr VkEventCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|  |  | |||
|  | @ -267,6 +267,7 @@ struct DeviceDispatch : public InstanceDispatch { | |||
|     PFN_vkGetFenceStatus vkGetFenceStatus; | ||||
|     PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; | ||||
|     PFN_vkGetQueryPoolResults vkGetQueryPoolResults; | ||||
|     PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR; | ||||
|     PFN_vkMapMemory vkMapMemory; | ||||
|     PFN_vkQueueSubmit vkQueueSubmit; | ||||
|     PFN_vkResetFences vkResetFences; | ||||
|  | @ -275,6 +276,7 @@ struct DeviceDispatch : public InstanceDispatch { | |||
|     PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR; | ||||
|     PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; | ||||
|     PFN_vkWaitForFences vkWaitForFences; | ||||
|     PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR; | ||||
| }; | ||||
| 
 | ||||
| /// Loads instance agnostic function pointers.
 | ||||
|  | @ -550,7 +552,6 @@ using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>; | |||
| using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>; | ||||
| using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>; | ||||
| using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>; | ||||
| using Semaphore = Handle<VkSemaphore, VkDevice, DeviceDispatch>; | ||||
| using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>; | ||||
| using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>; | ||||
| 
 | ||||
|  | @ -582,7 +583,8 @@ public: | |||
|     /// Construct a queue handle.
 | ||||
|     constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {} | ||||
| 
 | ||||
|     VkResult Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const noexcept { | ||||
|     VkResult Submit(Span<VkSubmitInfo> submit_infos, | ||||
|                     VkFence fence = VK_NULL_HANDLE) const noexcept { | ||||
|         return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence); | ||||
|     } | ||||
| 
 | ||||
|  | @ -674,6 +676,44 @@ public: | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> { | ||||
|     using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle; | ||||
| 
 | ||||
| public: | ||||
|     [[nodiscard]] u64 GetCounter() const { | ||||
|         u64 value; | ||||
|         Check(dld->vkGetSemaphoreCounterValueKHR(owner, handle, &value)); | ||||
|         return value; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Waits for a timeline semaphore on the host. | ||||
|      * | ||||
|      * @param value   Value to wait | ||||
|      * @param timeout Time in nanoseconds to timeout | ||||
|      * @return        True on successful wait, false on timeout | ||||
|      */ | ||||
|     bool Wait(u64 value, u64 timeout = std::numeric_limits<u64>::max()) const { | ||||
|         const VkSemaphoreWaitInfoKHR wait_info{ | ||||
|             .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR, | ||||
|             .pNext = nullptr, | ||||
|             .flags = 0, | ||||
|             .semaphoreCount = 1, | ||||
|             .pSemaphores = &handle, | ||||
|             .pValues = &value, | ||||
|         }; | ||||
|         const VkResult result = dld->vkWaitSemaphoresKHR(owner, &wait_info, timeout); | ||||
|         switch (result) { | ||||
|         case VK_SUCCESS: | ||||
|             return true; | ||||
|         case VK_TIMEOUT: | ||||
|             return false; | ||||
|         default: | ||||
|             throw Exception(result); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> { | ||||
|     using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle; | ||||
| 
 | ||||
|  | @ -694,6 +734,8 @@ public: | |||
| 
 | ||||
|     Semaphore CreateSemaphore() const; | ||||
| 
 | ||||
|     Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const; | ||||
| 
 | ||||
|     Fence CreateFence(const VkFenceCreateInfo& ci) const; | ||||
| 
 | ||||
|     DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const; | ||||
|  | @ -721,7 +763,7 @@ public: | |||
| 
 | ||||
|     ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; | ||||
| 
 | ||||
|     Event CreateNewEvent() const; | ||||
|     Event CreateEvent() const; | ||||
| 
 | ||||
|     SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,6 +9,17 @@ | |||
| #include <shared_mutex> | ||||
| #include <thread> | ||||
| 
 | ||||
| // This header includes both Vulkan and OpenGL headers, this has to be fixed
 | ||||
| // Unfortunately, including OpenGL will include Windows.h that defines macros that can cause issues.
 | ||||
| // Forcefully include glad early and undefine macros
 | ||||
| #include <glad/glad.h> | ||||
| #ifdef CreateEvent | ||||
| #undef CreateEvent | ||||
| #endif | ||||
| #ifdef CreateSemaphore | ||||
| #undef CreateSemaphore | ||||
| #endif | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/renderer_opengl/gl_device.h" | ||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue