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_blit_screen.h
|
||||||
renderer_vulkan/vk_buffer_cache.cpp
|
renderer_vulkan/vk_buffer_cache.cpp
|
||||||
renderer_vulkan/vk_buffer_cache.h
|
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.cpp
|
||||||
renderer_vulkan/vk_compute_pass.h
|
renderer_vulkan/vk_compute_pass.h
|
||||||
renderer_vulkan/vk_compute_pipeline.cpp
|
renderer_vulkan/vk_compute_pipeline.cpp
|
||||||
|
@ -204,6 +206,8 @@ if (ENABLE_VULKAN)
|
||||||
renderer_vulkan/vk_graphics_pipeline.h
|
renderer_vulkan/vk_graphics_pipeline.h
|
||||||
renderer_vulkan/vk_image.cpp
|
renderer_vulkan/vk_image.cpp
|
||||||
renderer_vulkan/vk_image.h
|
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.cpp
|
||||||
renderer_vulkan/vk_memory_manager.h
|
renderer_vulkan/vk_memory_manager.h
|
||||||
renderer_vulkan/vk_pipeline_cache.cpp
|
renderer_vulkan/vk_pipeline_cache.cpp
|
||||||
|
@ -214,8 +218,8 @@ if (ENABLE_VULKAN)
|
||||||
renderer_vulkan/vk_rasterizer.h
|
renderer_vulkan/vk_rasterizer.h
|
||||||
renderer_vulkan/vk_renderpass_cache.cpp
|
renderer_vulkan/vk_renderpass_cache.cpp
|
||||||
renderer_vulkan/vk_renderpass_cache.h
|
renderer_vulkan/vk_renderpass_cache.h
|
||||||
renderer_vulkan/vk_resource_manager.cpp
|
renderer_vulkan/vk_resource_pool.cpp
|
||||||
renderer_vulkan/vk_resource_manager.h
|
renderer_vulkan/vk_resource_pool.h
|
||||||
renderer_vulkan/vk_sampler_cache.cpp
|
renderer_vulkan/vk_sampler_cache.cpp
|
||||||
renderer_vulkan/vk_sampler_cache.h
|
renderer_vulkan/vk_sampler_cache.h
|
||||||
renderer_vulkan/vk_scheduler.cpp
|
renderer_vulkan/vk_scheduler.cpp
|
||||||
|
|
|
@ -91,8 +91,7 @@ private:
|
||||||
std::shared_ptr<HostCounter> last;
|
std::shared_ptr<HostCounter> last;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter,
|
template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter>
|
||||||
class QueryPool>
|
|
||||||
class QueryCacheBase {
|
class QueryCacheBase {
|
||||||
public:
|
public:
|
||||||
explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
|
explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
|
||||||
|
@ -206,9 +205,6 @@ public:
|
||||||
committed_flushes.pop_front();
|
committed_flushes.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Flushes a memory range to guest memory and removes it from the cache.
|
/// Flushes a memory range to guest memory and removes it from the cache.
|
||||||
void FlushAndRemoveRegion(VAddr addr, std::size_t size) {
|
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,
|
QueryCache::QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
|
||||||
Tegra::MemoryManager& gpu_memory)
|
Tegra::MemoryManager& gpu_memory)
|
||||||
: VideoCommon::QueryCacheBase<
|
: VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter>(
|
||||||
QueryCache, CachedQuery, CounterStream, HostCounter,
|
rasterizer, maxwell3d, gpu_memory),
|
||||||
std::vector<OGLQuery>>{static_cast<VideoCore::RasterizerInterface&>(rasterizer),
|
|
||||||
maxwell3d, gpu_memory},
|
|
||||||
gl_rasterizer{rasterizer} {}
|
gl_rasterizer{rasterizer} {}
|
||||||
|
|
||||||
QueryCache::~QueryCache() = default;
|
QueryCache::~QueryCache() = default;
|
||||||
|
@ -91,6 +89,8 @@ u64 HostCounter::BlockingQuery() const {
|
||||||
CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr)
|
CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr)
|
||||||
: VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {}
|
: VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {}
|
||||||
|
|
||||||
|
CachedQuery::~CachedQuery() = default;
|
||||||
|
|
||||||
CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept
|
CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept
|
||||||
: VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {}
|
: VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@ class RasterizerOpenGL;
|
||||||
|
|
||||||
using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
|
using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
|
||||||
|
|
||||||
class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream,
|
class QueryCache final
|
||||||
HostCounter, std::vector<OGLQuery>> {
|
: public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
|
||||||
public:
|
public:
|
||||||
explicit QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
|
explicit QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
|
||||||
Tegra::MemoryManager& gpu_memory);
|
Tegra::MemoryManager& gpu_memory);
|
||||||
|
@ -41,6 +41,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RasterizerOpenGL& gl_rasterizer;
|
RasterizerOpenGL& gl_rasterizer;
|
||||||
|
std::array<std::vector<OGLQuery>, VideoCore::NumQueryTypes> query_pools;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> {
|
class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> {
|
||||||
|
@ -63,10 +64,12 @@ class CachedQuery final : public VideoCommon::CachedQueryBase<HostCounter> {
|
||||||
public:
|
public:
|
||||||
explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr,
|
explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr,
|
||||||
u8* host_ptr);
|
u8* host_ptr);
|
||||||
CachedQuery(CachedQuery&& rhs) noexcept;
|
~CachedQuery() override;
|
||||||
CachedQuery(const CachedQuery&) = delete;
|
|
||||||
|
|
||||||
|
CachedQuery(CachedQuery&& rhs) noexcept;
|
||||||
CachedQuery& operator=(CachedQuery&& rhs) noexcept;
|
CachedQuery& operator=(CachedQuery&& rhs) noexcept;
|
||||||
|
|
||||||
|
CachedQuery(const CachedQuery&) = delete;
|
||||||
CachedQuery& operator=(const CachedQuery&) = delete;
|
CachedQuery& operator=(const CachedQuery&) = delete;
|
||||||
|
|
||||||
void Flush() override;
|
void Flush() override;
|
||||||
|
|
|
@ -25,9 +25,9 @@
|
||||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
||||||
#include "video_core/renderer_vulkan/vk_device.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_memory_manager.h"
|
||||||
#include "video_core/renderer_vulkan/vk_rasterizer.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_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||||
|
@ -56,7 +56,7 @@ VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||||
VkDebugUtilsMessageTypeFlagsEXT type,
|
VkDebugUtilsMessageTypeFlagsEXT type,
|
||||||
const VkDebugUtilsMessengerCallbackDataEXT* data,
|
const VkDebugUtilsMessengerCallbackDataEXT* data,
|
||||||
[[maybe_unused]] void* user_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) {
|
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||||
LOG_CRITICAL(Render_Vulkan, "{}", message);
|
LOG_CRITICAL(Render_Vulkan, "{}", message);
|
||||||
|
@ -269,11 +269,11 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
scheduler->WaitWorker();
|
scheduler->WaitWorker();
|
||||||
|
|
||||||
swapchain->AcquireNextImage();
|
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();
|
blit_screen->Recreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,23 +295,21 @@ bool RendererVulkan::Init() {
|
||||||
|
|
||||||
memory_manager = std::make_unique<VKMemoryManager>(*device);
|
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);
|
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>(
|
const auto& framebuffer = render_window.GetFramebufferLayout();
|
||||||
render_window, gpu, gpu.MemoryManager(), cpu_memory, screen_info, *device,
|
swapchain = std::make_unique<VKSwapchain>(*surface, *device, *scheduler);
|
||||||
*resource_manager, *memory_manager, *state_tracker, *scheduler);
|
swapchain->Create(framebuffer.width, framebuffer.height, false);
|
||||||
|
|
||||||
blit_screen = std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
|
rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(),
|
||||||
*resource_manager, *memory_manager, *swapchain,
|
cpu_memory, screen_info, *device,
|
||||||
*scheduler, screen_info);
|
*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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -329,7 +327,6 @@ void RendererVulkan::ShutDown() {
|
||||||
scheduler.reset();
|
scheduler.reset();
|
||||||
swapchain.reset();
|
swapchain.reset();
|
||||||
memory_manager.reset();
|
memory_manager.reset();
|
||||||
resource_manager.reset();
|
|
||||||
device.reset();
|
device.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,7 @@ namespace Vulkan {
|
||||||
class StateTracker;
|
class StateTracker;
|
||||||
class VKBlitScreen;
|
class VKBlitScreen;
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
class VKFence;
|
|
||||||
class VKMemoryManager;
|
class VKMemoryManager;
|
||||||
class VKResourceManager;
|
|
||||||
class VKSwapchain;
|
class VKSwapchain;
|
||||||
class VKScheduler;
|
class VKScheduler;
|
||||||
class VKImage;
|
class VKImage;
|
||||||
|
@ -81,11 +79,10 @@ private:
|
||||||
|
|
||||||
vk::DebugCallback debug_callback;
|
vk::DebugCallback debug_callback;
|
||||||
std::unique_ptr<VKDevice> device;
|
std::unique_ptr<VKDevice> device;
|
||||||
std::unique_ptr<VKSwapchain> swapchain;
|
|
||||||
std::unique_ptr<VKMemoryManager> memory_manager;
|
std::unique_ptr<VKMemoryManager> memory_manager;
|
||||||
std::unique_ptr<VKResourceManager> resource_manager;
|
|
||||||
std::unique_ptr<StateTracker> state_tracker;
|
std::unique_ptr<StateTracker> state_tracker;
|
||||||
std::unique_ptr<VKScheduler> scheduler;
|
std::unique_ptr<VKScheduler> scheduler;
|
||||||
|
std::unique_ptr<VKSwapchain> swapchain;
|
||||||
std::unique_ptr<VKBlitScreen> blit_screen;
|
std::unique_ptr<VKBlitScreen> blit_screen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,9 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/math_util.h"
|
#include "common/math_util.h"
|
||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/morton.h"
|
#include "video_core/morton.h"
|
||||||
#include "video_core/rasterizer_interface.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_blit_screen.h"
|
||||||
#include "video_core/renderer_vulkan/vk_device.h"
|
#include "video_core/renderer_vulkan/vk_device.h"
|
||||||
#include "video_core/renderer_vulkan/vk_image.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_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_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||||
|
@ -213,16 +211,12 @@ struct VKBlitScreen::BufferData {
|
||||||
VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
|
VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
|
||||||
Core::Frontend::EmuWindow& render_window_,
|
Core::Frontend::EmuWindow& render_window_,
|
||||||
VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_,
|
VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_,
|
||||||
VKResourceManager& resource_manager_, VKMemoryManager& memory_manager_,
|
VKMemoryManager& memory_manager_, VKSwapchain& swapchain_,
|
||||||
VKSwapchain& swapchain_, VKScheduler& scheduler_,
|
VKScheduler& scheduler_, const VKScreenInfo& screen_info_)
|
||||||
const VKScreenInfo& screen_info_)
|
: cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_},
|
||||||
: cpu_memory{cpu_memory_}, render_window{render_window_},
|
device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_},
|
||||||
rasterizer{rasterizer_}, device{device_}, resource_manager{resource_manager_},
|
scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
|
||||||
memory_manager{memory_manager_}, swapchain{swapchain_}, scheduler{scheduler_},
|
resource_ticks.resize(image_count);
|
||||||
image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
|
|
||||||
watches.resize(image_count);
|
|
||||||
std::generate(watches.begin(), watches.end(),
|
|
||||||
[]() { return std::make_unique<VKFenceWatch>(); });
|
|
||||||
|
|
||||||
CreateStaticResources();
|
CreateStaticResources();
|
||||||
CreateDynamicResources();
|
CreateDynamicResources();
|
||||||
|
@ -234,15 +228,16 @@ void VKBlitScreen::Recreate() {
|
||||||
CreateDynamicResources();
|
CreateDynamicResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated) {
|
||||||
bool use_accelerated) {
|
|
||||||
RefreshResources(framebuffer);
|
RefreshResources(framebuffer);
|
||||||
|
|
||||||
// Finish any pending renderpass
|
// Finish any pending renderpass
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
|
||||||
const std::size_t image_index = swapchain.GetImageIndex();
|
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();
|
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();
|
cmdbuf.EndRenderPass();
|
||||||
});
|
});
|
||||||
|
|
||||||
return {scheduler.GetFence(), *semaphores[image_index]};
|
return *semaphores[image_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKBlitScreen::CreateStaticResources() {
|
void VKBlitScreen::CreateStaticResources() {
|
||||||
|
@ -713,7 +708,7 @@ void VKBlitScreen::CreateFramebuffers() {
|
||||||
|
|
||||||
void VKBlitScreen::ReleaseRawImages() {
|
void VKBlitScreen::ReleaseRawImages() {
|
||||||
for (std::size_t i = 0; i < raw_images.size(); ++i) {
|
for (std::size_t i = 0; i < raw_images.size(); ++i) {
|
||||||
watches[i]->Wait();
|
scheduler.Wait(resource_ticks.at(i));
|
||||||
}
|
}
|
||||||
raw_images.clear();
|
raw_images.clear();
|
||||||
raw_buffer_commits.clear();
|
raw_buffer_commits.clear();
|
||||||
|
|
|
@ -5,10 +5,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
#include "video_core/renderer_vulkan/vk_memory_manager.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"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -34,9 +32,9 @@ class RasterizerInterface;
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
struct ScreenInfo;
|
struct ScreenInfo;
|
||||||
|
|
||||||
class RasterizerVulkan;
|
class RasterizerVulkan;
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
class VKFence;
|
|
||||||
class VKImage;
|
class VKImage;
|
||||||
class VKScheduler;
|
class VKScheduler;
|
||||||
class VKSwapchain;
|
class VKSwapchain;
|
||||||
|
@ -46,14 +44,13 @@ public:
|
||||||
explicit VKBlitScreen(Core::Memory::Memory& cpu_memory,
|
explicit VKBlitScreen(Core::Memory::Memory& cpu_memory,
|
||||||
Core::Frontend::EmuWindow& render_window,
|
Core::Frontend::EmuWindow& render_window,
|
||||||
VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
|
VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
|
||||||
VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
|
VKMemoryManager& memory_manager, VKSwapchain& swapchain,
|
||||||
VKSwapchain& swapchain, VKScheduler& scheduler,
|
VKScheduler& scheduler, const VKScreenInfo& screen_info);
|
||||||
const VKScreenInfo& screen_info);
|
|
||||||
~VKBlitScreen();
|
~VKBlitScreen();
|
||||||
|
|
||||||
void Recreate();
|
void Recreate();
|
||||||
|
|
||||||
std::tuple<VKFence&, VkSemaphore> Draw(const Tegra::FramebufferConfig& framebuffer,
|
[[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer,
|
||||||
bool use_accelerated);
|
bool use_accelerated);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -90,7 +87,6 @@ private:
|
||||||
Core::Frontend::EmuWindow& render_window;
|
Core::Frontend::EmuWindow& render_window;
|
||||||
VideoCore::RasterizerInterface& rasterizer;
|
VideoCore::RasterizerInterface& rasterizer;
|
||||||
const VKDevice& device;
|
const VKDevice& device;
|
||||||
VKResourceManager& resource_manager;
|
|
||||||
VKMemoryManager& memory_manager;
|
VKMemoryManager& memory_manager;
|
||||||
VKSwapchain& swapchain;
|
VKSwapchain& swapchain;
|
||||||
VKScheduler& scheduler;
|
VKScheduler& scheduler;
|
||||||
|
@ -111,7 +107,7 @@ private:
|
||||||
vk::Buffer buffer;
|
vk::Buffer buffer;
|
||||||
VKMemoryCommit buffer_commit;
|
VKMemoryCommit buffer_commit;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<VKFenceWatch>> watches;
|
std::vector<u64> resource_ticks;
|
||||||
|
|
||||||
std::vector<vk::Semaphore> semaphores;
|
std::vector<vk::Semaphore> semaphores;
|
||||||
std::vector<std::unique_ptr<VKImage>> raw_images;
|
std::vector<std::unique_ptr<VKImage>> raw_images;
|
||||||
|
|
|
@ -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
|
|
@ -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, 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, 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,
|
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() {
|
VkDescriptorSetLayoutBinding BuildQuadArrayPassDescriptorSetLayoutBinding() {
|
||||||
return {
|
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,
|
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,
|
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,
|
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.
|
// Quad indexed SPIR-V module. Generated from the "shaders/" directory.
|
||||||
constexpr u8 QUAD_INDEXED_SPV[] = {
|
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, 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, 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,
|
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() {
|
std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBindings() {
|
||||||
return {{
|
return {{
|
||||||
|
@ -448,12 +451,12 @@ VKComputePass::VKComputePass(const VKDevice& device, VKDescriptorPool& descripto
|
||||||
|
|
||||||
VKComputePass::~VKComputePass() = default;
|
VKComputePass::~VKComputePass() = default;
|
||||||
|
|
||||||
VkDescriptorSet VKComputePass::CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue,
|
VkDescriptorSet VKComputePass::CommitDescriptorSet(
|
||||||
VKFence& fence) {
|
VKUpdateDescriptorQueue& update_descriptor_queue) {
|
||||||
if (!descriptor_template) {
|
if (!descriptor_template) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
const auto set = descriptor_allocator->Commit(fence);
|
const VkDescriptorSet set = descriptor_allocator->Commit();
|
||||||
update_descriptor_queue.Send(*descriptor_template, set);
|
update_descriptor_queue.Send(*descriptor_template, set);
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
@ -477,7 +480,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32
|
||||||
|
|
||||||
update_descriptor_queue.Acquire();
|
update_descriptor_queue.Acquire();
|
||||||
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_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.RequestOutsideRenderPassOperationContext();
|
||||||
|
|
||||||
|
@ -520,13 +523,13 @@ Uint8Pass::~Uint8Pass() = default;
|
||||||
|
|
||||||
std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
|
std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
|
||||||
u64 src_offset) {
|
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);
|
auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false);
|
||||||
|
|
||||||
update_descriptor_queue.Acquire();
|
update_descriptor_queue.Acquire();
|
||||||
update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
|
update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
|
||||||
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_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.RequestOutsideRenderPassOperationContext();
|
||||||
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
|
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.Acquire();
|
||||||
update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
|
update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
|
||||||
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_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.RequestOutsideRenderPassOperationContext();
|
||||||
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
|
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
class VKFence;
|
|
||||||
class VKScheduler;
|
class VKScheduler;
|
||||||
class VKStagingBufferPool;
|
class VKStagingBufferPool;
|
||||||
class VKUpdateDescriptorQueue;
|
class VKUpdateDescriptorQueue;
|
||||||
|
@ -30,8 +29,7 @@ public:
|
||||||
~VKComputePass();
|
~VKComputePass();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue,
|
VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue);
|
||||||
VKFence& fence);
|
|
||||||
|
|
||||||
vk::DescriptorUpdateTemplateKHR descriptor_template;
|
vk::DescriptorUpdateTemplateKHR descriptor_template;
|
||||||
vk::PipelineLayout layout;
|
vk::PipelineLayout layout;
|
||||||
|
|
|
@ -32,7 +32,7 @@ VkDescriptorSet VKComputePipeline::CommitDescriptorSet() {
|
||||||
if (!descriptor_template) {
|
if (!descriptor_template) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const auto set = descriptor_allocator.Commit(scheduler.GetFence());
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
update_descriptor_queue.Send(*descriptor_template, set);
|
update_descriptor_queue.Send(*descriptor_template, set);
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||||
#include "video_core/renderer_vulkan/vk_device.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"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
@ -15,14 +16,15 @@ namespace Vulkan {
|
||||||
// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines.
|
// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines.
|
||||||
constexpr std::size_t SETS_GROW_RATE = 0x20;
|
constexpr std::size_t SETS_GROW_RATE = 0x20;
|
||||||
|
|
||||||
DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool,
|
DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool_,
|
||||||
VkDescriptorSetLayout layout)
|
VkDescriptorSetLayout layout_)
|
||||||
: VKFencedPool{SETS_GROW_RATE}, descriptor_pool{descriptor_pool}, layout{layout} {}
|
: ResourcePool(descriptor_pool_.master_semaphore, SETS_GROW_RATE),
|
||||||
|
descriptor_pool{descriptor_pool_}, layout{layout_} {}
|
||||||
|
|
||||||
DescriptorAllocator::~DescriptorAllocator() = default;
|
DescriptorAllocator::~DescriptorAllocator() = default;
|
||||||
|
|
||||||
VkDescriptorSet DescriptorAllocator::Commit(VKFence& fence) {
|
VkDescriptorSet DescriptorAllocator::Commit() {
|
||||||
const std::size_t index = CommitResource(fence);
|
const std::size_t index = CommitResource();
|
||||||
return descriptors_allocations[index / SETS_GROW_RATE][index % SETS_GROW_RATE];
|
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));
|
descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin));
|
||||||
}
|
}
|
||||||
|
|
||||||
VKDescriptorPool::VKDescriptorPool(const VKDevice& device)
|
VKDescriptorPool::VKDescriptorPool(const VKDevice& device_, VKScheduler& scheduler)
|
||||||
: device{device}, active_pool{AllocateNewPool()} {}
|
: device{device_}, master_semaphore{scheduler.GetMasterSemaphore()}, active_pool{
|
||||||
|
AllocateNewPool()} {}
|
||||||
|
|
||||||
VKDescriptorPool::~VKDescriptorPool() = default;
|
VKDescriptorPool::~VKDescriptorPool() = default;
|
||||||
|
|
||||||
|
|
|
@ -6,21 +6,24 @@
|
||||||
|
|
||||||
#include <vector>
|
#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"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
|
class VKDevice;
|
||||||
class VKDescriptorPool;
|
class VKDescriptorPool;
|
||||||
|
class VKScheduler;
|
||||||
|
|
||||||
class DescriptorAllocator final : public VKFencedPool {
|
class DescriptorAllocator final : public ResourcePool {
|
||||||
public:
|
public:
|
||||||
explicit DescriptorAllocator(VKDescriptorPool& descriptor_pool, VkDescriptorSetLayout layout);
|
explicit DescriptorAllocator(VKDescriptorPool& descriptor_pool, VkDescriptorSetLayout layout);
|
||||||
~DescriptorAllocator() override;
|
~DescriptorAllocator() override;
|
||||||
|
|
||||||
|
DescriptorAllocator& operator=(const DescriptorAllocator&) = delete;
|
||||||
DescriptorAllocator(const DescriptorAllocator&) = delete;
|
DescriptorAllocator(const DescriptorAllocator&) = delete;
|
||||||
|
|
||||||
VkDescriptorSet Commit(VKFence& fence);
|
VkDescriptorSet Commit();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void Allocate(std::size_t begin, std::size_t end) override;
|
void Allocate(std::size_t begin, std::size_t end) override;
|
||||||
|
@ -36,15 +39,19 @@ class VKDescriptorPool final {
|
||||||
friend DescriptorAllocator;
|
friend DescriptorAllocator;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit VKDescriptorPool(const VKDevice& device);
|
explicit VKDescriptorPool(const VKDevice& device, VKScheduler& scheduler);
|
||||||
~VKDescriptorPool();
|
~VKDescriptorPool();
|
||||||
|
|
||||||
|
VKDescriptorPool(const VKDescriptorPool&) = delete;
|
||||||
|
VKDescriptorPool& operator=(const VKDescriptorPool&) = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vk::DescriptorPool* AllocateNewPool();
|
vk::DescriptorPool* AllocateNewPool();
|
||||||
|
|
||||||
vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count);
|
vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count);
|
||||||
|
|
||||||
const VKDevice& device;
|
const VKDevice& device;
|
||||||
|
MasterSemaphore& master_semaphore;
|
||||||
|
|
||||||
std::vector<vk::DescriptorPool> pools;
|
std::vector<vk::DescriptorPool> pools;
|
||||||
vk::DescriptorPool* active_pool;
|
vk::DescriptorPool* active_pool;
|
||||||
|
|
|
@ -42,6 +42,7 @@ constexpr std::array REQUIRED_EXTENSIONS{
|
||||||
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
|
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
|
||||||
VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
|
VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
|
||||||
VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
|
VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
|
||||||
|
VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
|
||||||
VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
|
VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
|
||||||
VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
|
VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
|
||||||
VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
|
VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
|
||||||
|
@ -250,6 +251,13 @@ bool VKDevice::Create() {
|
||||||
.inheritedQueries = false,
|
.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{
|
VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{
|
||||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
|
|
@ -29,8 +29,8 @@ void InnerFence::Queue() {
|
||||||
}
|
}
|
||||||
ASSERT(!event);
|
ASSERT(!event);
|
||||||
|
|
||||||
event = device.GetLogical().CreateNewEvent();
|
event = device.GetLogical().CreateEvent();
|
||||||
ticks = scheduler.Ticks();
|
ticks = scheduler.CurrentTick();
|
||||||
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) {
|
scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) {
|
||||||
|
@ -52,7 +52,7 @@ void InnerFence::Wait() {
|
||||||
}
|
}
|
||||||
ASSERT(event);
|
ASSERT(event);
|
||||||
|
|
||||||
if (ticks >= scheduler.Ticks()) {
|
if (ticks >= scheduler.CurrentTick()) {
|
||||||
scheduler.Flush();
|
scheduler.Flush();
|
||||||
}
|
}
|
||||||
while (!IsEventSignalled()) {
|
while (!IsEventSignalled()) {
|
||||||
|
|
|
@ -93,7 +93,7 @@ VkDescriptorSet VKGraphicsPipeline::CommitDescriptorSet() {
|
||||||
if (!descriptor_template) {
|
if (!descriptor_template) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const auto set = descriptor_allocator.Commit(scheduler.GetFence());
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
update_descriptor_queue.Send(*descriptor_template, set);
|
update_descriptor_queue.Send(*descriptor_template, set);
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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 VKComputePipeline;
|
||||||
class VKDescriptorPool;
|
class VKDescriptorPool;
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
class VKFence;
|
|
||||||
class VKScheduler;
|
class VKScheduler;
|
||||||
class VKUpdateDescriptorQueue;
|
class VKUpdateDescriptorQueue;
|
||||||
|
|
||||||
|
|
|
@ -9,35 +9,33 @@
|
||||||
|
|
||||||
#include "video_core/renderer_vulkan/vk_device.h"
|
#include "video_core/renderer_vulkan/vk_device.h"
|
||||||
#include "video_core/renderer_vulkan/vk_query_cache.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/vk_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/wrapper.h"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
|
using VideoCore::QueryType;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr std::array QUERY_TARGETS = {VK_QUERY_TYPE_OCCLUSION};
|
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)];
|
return QUERY_TARGETS[static_cast<std::size_t>(type)];
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // 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;
|
QueryPool::~QueryPool() = default;
|
||||||
|
|
||||||
void QueryPool::Initialize(const VKDevice& device_, VideoCore::QueryType type_) {
|
std::pair<VkQueryPool, u32> QueryPool::Commit() {
|
||||||
device = &device_;
|
|
||||||
type = type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<VkQueryPool, u32> QueryPool::Commit(VKFence& fence) {
|
|
||||||
std::size_t index;
|
std::size_t index;
|
||||||
do {
|
do {
|
||||||
index = CommitResource(fence);
|
index = CommitResource();
|
||||||
} while (usage[index]);
|
} while (usage[index]);
|
||||||
usage[index] = true;
|
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) {
|
void QueryPool::Allocate(std::size_t begin, std::size_t end) {
|
||||||
usage.resize(end);
|
usage.resize(end);
|
||||||
|
|
||||||
pools.push_back(device->GetLogical().CreateQueryPool({
|
pools.push_back(device.GetLogical().CreateQueryPool({
|
||||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
@ -71,28 +69,36 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
|
||||||
VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
|
VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
|
||||||
Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
|
Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
|
||||||
const VKDevice& device, VKScheduler& scheduler)
|
const VKDevice& device, VKScheduler& scheduler)
|
||||||
: VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
|
: VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream,
|
||||||
QueryPool>{rasterizer, maxwell3d, gpu_memory},
|
HostCounter>{rasterizer, maxwell3d, gpu_memory},
|
||||||
device{device}, scheduler{scheduler} {
|
device{device}, scheduler{scheduler}, query_pools{
|
||||||
for (std::size_t i = 0; i < static_cast<std::size_t>(VideoCore::NumQueryTypes); ++i) {
|
QueryPool{device, scheduler,
|
||||||
query_pools[i].Initialize(device, static_cast<VideoCore::QueryType>(i));
|
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(QueryType type) {
|
||||||
|
return query_pools[static_cast<std::size_t>(type)].Commit();
|
||||||
std::pair<VkQueryPool, u32> VKQueryCache::AllocateQuery(VideoCore::QueryType type) {
|
|
||||||
return query_pools[static_cast<std::size_t>(type)].Commit(scheduler.GetFence());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
query_pools[static_cast<std::size_t>(type)].Reserve(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
HostCounter::HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency,
|
HostCounter::HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency,
|
||||||
VideoCore::QueryType type)
|
QueryType type)
|
||||||
: VideoCommon::HostCounterBase<VKQueryCache, HostCounter>{std::move(dependency)}, cache{cache},
|
: 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();
|
const vk::Device* logical = &cache.Device().GetLogical();
|
||||||
cache.Scheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
|
cache.Scheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
|
||||||
logical->ResetQueryPoolEXT(query.first, query.second, 1);
|
logical->ResetQueryPoolEXT(query.first, query.second, 1);
|
||||||
|
@ -110,7 +116,7 @@ void HostCounter::EndQuery() {
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 HostCounter::BlockingQuery() const {
|
u64 HostCounter::BlockingQuery() const {
|
||||||
if (ticks >= cache.Scheduler().Ticks()) {
|
if (tick >= cache.Scheduler().CurrentTick()) {
|
||||||
cache.Scheduler().Flush();
|
cache.Scheduler().Flush();
|
||||||
}
|
}
|
||||||
u64 data;
|
u64 data;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "video_core/query_cache.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"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
@ -28,14 +28,12 @@ class VKScheduler;
|
||||||
|
|
||||||
using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>;
|
using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>;
|
||||||
|
|
||||||
class QueryPool final : public VKFencedPool {
|
class QueryPool final : public ResourcePool {
|
||||||
public:
|
public:
|
||||||
explicit QueryPool();
|
explicit QueryPool(const VKDevice& device, VKScheduler& scheduler, VideoCore::QueryType type);
|
||||||
~QueryPool() override;
|
~QueryPool() override;
|
||||||
|
|
||||||
void Initialize(const VKDevice& device, VideoCore::QueryType type);
|
std::pair<VkQueryPool, u32> Commit();
|
||||||
|
|
||||||
std::pair<VkQueryPool, u32> Commit(VKFence& fence);
|
|
||||||
|
|
||||||
void Reserve(std::pair<VkQueryPool, u32> query);
|
void Reserve(std::pair<VkQueryPool, u32> query);
|
||||||
|
|
||||||
|
@ -45,16 +43,15 @@ protected:
|
||||||
private:
|
private:
|
||||||
static constexpr std::size_t GROW_STEP = 512;
|
static constexpr std::size_t GROW_STEP = 512;
|
||||||
|
|
||||||
const VKDevice* device = nullptr;
|
const VKDevice& device;
|
||||||
VideoCore::QueryType type = {};
|
const VideoCore::QueryType type;
|
||||||
|
|
||||||
std::vector<vk::QueryPool> pools;
|
std::vector<vk::QueryPool> pools;
|
||||||
std::vector<bool> usage;
|
std::vector<bool> usage;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VKQueryCache final
|
class VKQueryCache final
|
||||||
: public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
|
: public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter> {
|
||||||
QueryPool> {
|
|
||||||
public:
|
public:
|
||||||
explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
|
explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
|
||||||
Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
|
Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
|
||||||
|
@ -76,6 +73,7 @@ public:
|
||||||
private:
|
private:
|
||||||
const VKDevice& device;
|
const VKDevice& device;
|
||||||
VKScheduler& scheduler;
|
VKScheduler& scheduler;
|
||||||
|
std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HostCounter final : public VideoCommon::HostCounterBase<VKQueryCache, HostCounter> {
|
class HostCounter final : public VideoCommon::HostCounterBase<VKQueryCache, HostCounter> {
|
||||||
|
@ -92,7 +90,7 @@ private:
|
||||||
VKQueryCache& cache;
|
VKQueryCache& cache;
|
||||||
const VideoCore::QueryType type;
|
const VideoCore::QueryType type;
|
||||||
const std::pair<VkQueryPool, u32> query;
|
const std::pair<VkQueryPool, u32> query;
|
||||||
const u64 ticks;
|
const u64 tick;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CachedQuery : public VideoCommon::CachedQueryBase<HostCounter> {
|
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_pipeline_cache.h"
|
||||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||||
#include "video_core/renderer_vulkan/vk_renderpass_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_sampler_cache.h"
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.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_,
|
RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_,
|
||||||
Tegra::MemoryManager& gpu_memory_,
|
Tegra::MemoryManager& gpu_memory_,
|
||||||
Core::Memory::Memory& cpu_memory, VKScreenInfo& screen_info_,
|
Core::Memory::Memory& cpu_memory, VKScreenInfo& screen_info_,
|
||||||
const VKDevice& device_, VKResourceManager& resource_manager_,
|
const VKDevice& device_, VKMemoryManager& memory_manager_,
|
||||||
VKMemoryManager& memory_manager_, StateTracker& state_tracker_,
|
StateTracker& state_tracker_, VKScheduler& scheduler_)
|
||||||
VKScheduler& scheduler_)
|
|
||||||
: RasterizerAccelerated(cpu_memory), gpu(gpu_), gpu_memory(gpu_memory_),
|
: RasterizerAccelerated(cpu_memory), gpu(gpu_), gpu_memory(gpu_memory_),
|
||||||
maxwell3d(gpu.Maxwell3D()), kepler_compute(gpu.KeplerCompute()), screen_info(screen_info_),
|
maxwell3d(gpu.Maxwell3D()), kepler_compute(gpu.KeplerCompute()), screen_info(screen_info_),
|
||||||
device(device_), resource_manager(resource_manager_), memory_manager(memory_manager_),
|
device(device_), memory_manager(memory_manager_), state_tracker(state_tracker_),
|
||||||
state_tracker(state_tracker_), scheduler(scheduler_),
|
scheduler(scheduler_), staging_pool(device, memory_manager, scheduler),
|
||||||
staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
|
descriptor_pool(device, scheduler_), update_descriptor_queue(device, scheduler),
|
||||||
update_descriptor_queue(device, scheduler), renderpass_cache(device),
|
renderpass_cache(device),
|
||||||
quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
|
quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
|
||||||
quad_indexed_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),
|
uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
|
||||||
texture_cache(*this, maxwell3d, gpu_memory, device, resource_manager, memory_manager,
|
texture_cache(*this, maxwell3d, gpu_memory, device, memory_manager, scheduler, staging_pool),
|
||||||
scheduler, staging_pool),
|
|
||||||
pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
|
pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
|
||||||
descriptor_pool, update_descriptor_queue, renderpass_cache),
|
descriptor_pool, update_descriptor_queue, renderpass_cache),
|
||||||
buffer_cache(*this, gpu_memory, cpu_memory, device, memory_manager, scheduler, staging_pool),
|
buffer_cache(*this, gpu_memory, cpu_memory, device, memory_manager, scheduler, staging_pool),
|
||||||
sampler_cache(device), query_cache(*this, maxwell3d, gpu_memory, device, scheduler),
|
sampler_cache(device), query_cache(*this, maxwell3d, gpu_memory, device, scheduler),
|
||||||
fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device,
|
fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device,
|
||||||
scheduler),
|
scheduler),
|
||||||
wfi_event(device.GetLogical().CreateNewEvent()), async_shaders(emu_window) {
|
wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window) {
|
||||||
scheduler.SetQueryCache(query_cache);
|
scheduler.SetQueryCache(query_cache);
|
||||||
if (device.UseAsynchronousShaders()) {
|
if (device.UseAsynchronousShaders()) {
|
||||||
async_shaders.AllocateWorkers();
|
async_shaders.AllocateWorkers();
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||||
#include "video_core/renderer_vulkan/vk_query_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_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_sampler_cache.h"
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.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,
|
explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
|
||||||
Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
|
Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
|
||||||
VKScreenInfo& screen_info, const VKDevice& device,
|
VKScreenInfo& screen_info, const VKDevice& device,
|
||||||
VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
|
VKMemoryManager& memory_manager, StateTracker& state_tracker,
|
||||||
StateTracker& state_tracker, VKScheduler& scheduler);
|
VKScheduler& scheduler);
|
||||||
~RasterizerVulkan() override;
|
~RasterizerVulkan() override;
|
||||||
|
|
||||||
void Draw(bool is_indexed, bool is_instanced) override;
|
void Draw(bool is_indexed, bool is_instanced) override;
|
||||||
|
@ -286,7 +285,6 @@ private:
|
||||||
|
|
||||||
VKScreenInfo& screen_info;
|
VKScreenInfo& screen_info;
|
||||||
const VKDevice& device;
|
const VKDevice& device;
|
||||||
VKResourceManager& resource_manager;
|
|
||||||
VKMemoryManager& memory_manager;
|
VKMemoryManager& memory_manager;
|
||||||
StateTracker& state_tracker;
|
StateTracker& state_tracker;
|
||||||
VKScheduler& scheduler;
|
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
|
|
|
@ -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
|
|
@ -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/microprofile.h"
|
||||||
#include "common/thread.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_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_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_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||||
#include "video_core/renderer_vulkan/wrapper.h"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
@ -35,10 +36,10 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
|
||||||
last = nullptr;
|
last = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
|
VKScheduler::VKScheduler(const VKDevice& device_, StateTracker& state_tracker_)
|
||||||
StateTracker& state_tracker)
|
: device{device_}, state_tracker{state_tracker_},
|
||||||
: device{device}, resource_manager{resource_manager}, state_tracker{state_tracker},
|
master_semaphore{std::make_unique<MasterSemaphore>(device)},
|
||||||
next_fence{&resource_manager.CommitFence()} {
|
command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
|
||||||
AcquireNewChunk();
|
AcquireNewChunk();
|
||||||
AllocateNewContext();
|
AllocateNewContext();
|
||||||
worker_thread = std::thread(&VKScheduler::WorkerThread, this);
|
worker_thread = std::thread(&VKScheduler::WorkerThread, this);
|
||||||
|
@ -50,20 +51,27 @@ VKScheduler::~VKScheduler() {
|
||||||
worker_thread.join();
|
worker_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKScheduler::Flush(bool release_fence, VkSemaphore semaphore) {
|
u64 VKScheduler::CurrentTick() const noexcept {
|
||||||
SubmitExecution(semaphore);
|
return master_semaphore->CurrentTick();
|
||||||
if (release_fence) {
|
|
||||||
current_fence->Release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
AllocateNewContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKScheduler::Finish(bool release_fence, VkSemaphore semaphore) {
|
void VKScheduler::Finish(VkSemaphore semaphore) {
|
||||||
|
const u64 presubmit_tick = CurrentTick();
|
||||||
SubmitExecution(semaphore);
|
SubmitExecution(semaphore);
|
||||||
current_fence->Wait();
|
Wait(presubmit_tick);
|
||||||
if (release_fence) {
|
|
||||||
current_fence->Release();
|
|
||||||
}
|
|
||||||
AllocateNewContext();
|
AllocateNewContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,18 +168,38 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
|
||||||
|
|
||||||
current_cmdbuf.End();
|
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{
|
const VkSubmitInfo submit_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = &timeline_si,
|
||||||
.waitSemaphoreCount = 0,
|
.waitSemaphoreCount = 1,
|
||||||
.pWaitSemaphores = nullptr,
|
.pWaitSemaphores = &timeline_semaphore,
|
||||||
.pWaitDstStageMask = nullptr,
|
.pWaitDstStageMask = &wait_stage_mask,
|
||||||
.commandBufferCount = 1,
|
.commandBufferCount = 1,
|
||||||
.pCommandBuffers = current_cmdbuf.address(),
|
.pCommandBuffers = current_cmdbuf.address(),
|
||||||
.signalSemaphoreCount = semaphore ? 1U : 0U,
|
.signalSemaphoreCount = num_signal_semaphores,
|
||||||
.pSignalSemaphores = &semaphore,
|
.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:
|
case VK_SUCCESS:
|
||||||
break;
|
break;
|
||||||
case VK_ERROR_DEVICE_LOST:
|
case VK_ERROR_DEVICE_LOST:
|
||||||
|
@ -183,14 +211,9 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKScheduler::AllocateNewContext() {
|
void VKScheduler::AllocateNewContext() {
|
||||||
++ticks;
|
|
||||||
|
|
||||||
std::unique_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
current_fence = next_fence;
|
|
||||||
next_fence = &resource_manager.CommitFence();
|
|
||||||
|
|
||||||
current_cmdbuf = vk::CommandBuffer(resource_manager.CommitCommandBuffer(*current_fence),
|
current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
|
||||||
device.GetDispatchLoader());
|
|
||||||
current_cmdbuf.Begin({
|
current_cmdbuf.Begin({
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
|
|
@ -16,42 +16,33 @@
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
|
class CommandPool;
|
||||||
|
class MasterSemaphore;
|
||||||
class StateTracker;
|
class StateTracker;
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
class VKFence;
|
|
||||||
class VKQueryCache;
|
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
|
/// The scheduler abstracts command buffer and fence management with an interface that's able to do
|
||||||
/// OpenGL-like operations on Vulkan command buffers.
|
/// OpenGL-like operations on Vulkan command buffers.
|
||||||
class VKScheduler {
|
class VKScheduler {
|
||||||
public:
|
public:
|
||||||
explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
|
explicit VKScheduler(const VKDevice& device, StateTracker& state_tracker);
|
||||||
StateTracker& state_tracker);
|
|
||||||
~VKScheduler();
|
~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.
|
/// 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.
|
/// 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
|
/// Waits for the worker thread to finish executing everything. After this function returns it's
|
||||||
/// safe to touch worker resources.
|
/// safe to touch worker resources.
|
||||||
|
@ -86,14 +77,9 @@ public:
|
||||||
(void)chunk->Record(command);
|
(void)chunk->Record(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a reference to the current fence.
|
/// Returns the master timeline semaphore.
|
||||||
VKFenceView GetFence() const {
|
[[nodiscard]] MasterSemaphore& GetMasterSemaphore() const noexcept {
|
||||||
return current_fence;
|
return *master_semaphore;
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the current command buffer tick.
|
|
||||||
u64 Ticks() const {
|
|
||||||
return ticks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -171,6 +157,13 @@ private:
|
||||||
std::array<u8, 0x8000> data{};
|
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 WorkerThread();
|
||||||
|
|
||||||
void SubmitExecution(VkSemaphore semaphore);
|
void SubmitExecution(VkSemaphore semaphore);
|
||||||
|
@ -186,30 +179,23 @@ private:
|
||||||
void AcquireNewChunk();
|
void AcquireNewChunk();
|
||||||
|
|
||||||
const VKDevice& device;
|
const VKDevice& device;
|
||||||
VKResourceManager& resource_manager;
|
|
||||||
StateTracker& state_tracker;
|
StateTracker& state_tracker;
|
||||||
|
|
||||||
|
std::unique_ptr<MasterSemaphore> master_semaphore;
|
||||||
|
std::unique_ptr<CommandPool> command_pool;
|
||||||
|
|
||||||
VKQueryCache* query_cache = nullptr;
|
VKQueryCache* query_cache = nullptr;
|
||||||
|
|
||||||
vk::CommandBuffer current_cmdbuf;
|
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::unique_ptr<CommandChunk> chunk;
|
||||||
std::thread worker_thread;
|
std::thread worker_thread;
|
||||||
|
|
||||||
|
State state;
|
||||||
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_queue;
|
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_queue;
|
||||||
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve;
|
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::atomic<u64> ticks = 0;
|
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,36 +10,18 @@
|
||||||
#include "common/bit_util.h"
|
#include "common/bit_util.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "video_core/renderer_vulkan/vk_device.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_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||||
#include "video_core/renderer_vulkan/wrapper.h"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence,
|
VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_)
|
||||||
u64 last_epoch)
|
: buffer{std::move(buffer_)} {}
|
||||||
: buffer{std::move(buffer)}, watch{fence}, last_epoch{last_epoch} {}
|
|
||||||
|
|
||||||
VKStagingBufferPool::StagingBuffer::StagingBuffer(StagingBuffer&& rhs) noexcept {
|
VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device_, VKMemoryManager& memory_manager_,
|
||||||
buffer = std::move(rhs.buffer);
|
VKScheduler& scheduler_)
|
||||||
watch = std::move(rhs.watch);
|
: device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {}
|
||||||
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() = default;
|
VKStagingBufferPool::~VKStagingBufferPool() = default;
|
||||||
|
|
||||||
|
@ -51,7 +33,6 @@ VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visib
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKStagingBufferPool::TickFrame() {
|
void VKStagingBufferPool::TickFrame() {
|
||||||
++epoch;
|
|
||||||
current_delete_level = (current_delete_level + 1) % NumLevels;
|
current_delete_level = (current_delete_level + 1) % NumLevels;
|
||||||
|
|
||||||
ReleaseCache(true);
|
ReleaseCache(true);
|
||||||
|
@ -59,11 +40,12 @@ void VKStagingBufferPool::TickFrame() {
|
||||||
}
|
}
|
||||||
|
|
||||||
VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) {
|
VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) {
|
||||||
for (auto& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
|
for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
|
||||||
if (entry.watch.TryWatch(scheduler.GetFence())) {
|
if (!scheduler.IsFree(entry.tick)) {
|
||||||
entry.last_epoch = epoch;
|
continue;
|
||||||
return &*entry.buffer;
|
|
||||||
}
|
}
|
||||||
|
entry.tick = scheduler.CurrentTick();
|
||||||
|
return &*entry.buffer;
|
||||||
}
|
}
|
||||||
return nullptr;
|
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);
|
buffer->commit = memory_manager.Commit(buffer->handle, host_visible);
|
||||||
|
|
||||||
auto& entries = GetCache(host_visible)[log2].entries;
|
std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries;
|
||||||
return *entries.emplace_back(std::move(buffer), scheduler.GetFence(), epoch).buffer;
|
StagingBuffer& entry = entries.emplace_back(std::move(buffer));
|
||||||
|
entry.tick = scheduler.CurrentTick();
|
||||||
|
return *entry.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) {
|
VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) {
|
||||||
|
@ -109,9 +93,8 @@ u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t lo
|
||||||
auto& entries = staging.entries;
|
auto& entries = staging.entries;
|
||||||
const std::size_t old_size = entries.size();
|
const std::size_t old_size = entries.size();
|
||||||
|
|
||||||
const auto is_deleteable = [this](const auto& entry) {
|
const auto is_deleteable = [this](const StagingBuffer& entry) {
|
||||||
static constexpr u64 epochs_to_destroy = 180;
|
return scheduler.IsFree(entry.tick);
|
||||||
return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed();
|
|
||||||
};
|
};
|
||||||
const std::size_t begin_offset = staging.delete_index;
|
const std::size_t begin_offset = staging.delete_index;
|
||||||
const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
|
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 "common/common_types.h"
|
||||||
|
|
||||||
#include "video_core/renderer_vulkan/vk_memory_manager.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"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
class VKFenceWatch;
|
|
||||||
class VKScheduler;
|
class VKScheduler;
|
||||||
|
|
||||||
struct VKBuffer final {
|
struct VKBuffer final {
|
||||||
|
@ -36,16 +34,10 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct StagingBuffer final {
|
struct StagingBuffer final {
|
||||||
explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, u64 last_epoch);
|
explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer);
|
||||||
StagingBuffer(StagingBuffer&& rhs) noexcept;
|
|
||||||
StagingBuffer(const StagingBuffer&) = delete;
|
|
||||||
~StagingBuffer();
|
|
||||||
|
|
||||||
StagingBuffer& operator=(StagingBuffer&& rhs) noexcept;
|
|
||||||
|
|
||||||
std::unique_ptr<VKBuffer> buffer;
|
std::unique_ptr<VKBuffer> buffer;
|
||||||
VKFenceWatch watch;
|
u64 tick = 0;
|
||||||
u64 last_epoch = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StagingBuffers final {
|
struct StagingBuffers final {
|
||||||
|
@ -73,8 +65,6 @@ private:
|
||||||
StagingBuffersCache host_staging_buffers;
|
StagingBuffersCache host_staging_buffers;
|
||||||
StagingBuffersCache device_staging_buffers;
|
StagingBuffersCache device_staging_buffers;
|
||||||
|
|
||||||
u64 epoch = 0;
|
|
||||||
|
|
||||||
std::size_t current_delete_level = 0;
|
std::size_t current_delete_level = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "video_core/renderer_vulkan/vk_device.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_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||||
#include "video_core/renderer_vulkan/wrapper.h"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
@ -111,7 +110,7 @@ void VKStreamBuffer::Unmap(u64 size) {
|
||||||
}
|
}
|
||||||
auto& watch = current_watches[current_watch_cursor++];
|
auto& watch = current_watches[current_watch_cursor++];
|
||||||
watch.upper_bound = offset;
|
watch.upper_bound = offset;
|
||||||
watch.fence.Watch(scheduler.GetFence());
|
watch.tick = scheduler.CurrentTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) {
|
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) {
|
while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) {
|
||||||
auto& watch = previous_watches[wait_cursor];
|
auto& watch = previous_watches[wait_cursor];
|
||||||
wait_bound = watch.upper_bound;
|
wait_bound = watch.upper_bound;
|
||||||
watch.fence.Wait();
|
scheduler.Wait(watch.tick);
|
||||||
++wait_cursor;
|
++wait_cursor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
class VKFence;
|
|
||||||
class VKFenceWatch;
|
class VKFenceWatch;
|
||||||
class VKScheduler;
|
class VKScheduler;
|
||||||
|
|
||||||
|
@ -44,8 +43,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Watch final {
|
struct Watch {
|
||||||
VKFenceWatch fence;
|
u64 tick{};
|
||||||
u64 upper_bound{};
|
u64 upper_bound{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/frontend/framebuffer_layout.h"
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
#include "video_core/renderer_vulkan/vk_device.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/vk_swapchain.h"
|
||||||
#include "video_core/renderer_vulkan/wrapper.h"
|
#include "video_core/renderer_vulkan/wrapper.h"
|
||||||
|
|
||||||
|
@ -56,8 +56,8 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
VKSwapchain::VKSwapchain(VkSurfaceKHR surface, const VKDevice& device)
|
VKSwapchain::VKSwapchain(VkSurfaceKHR surface_, const VKDevice& device_, VKScheduler& scheduler_)
|
||||||
: surface{surface}, device{device} {}
|
: surface{surface_}, device{device_}, scheduler{scheduler_} {}
|
||||||
|
|
||||||
VKSwapchain::~VKSwapchain() = default;
|
VKSwapchain::~VKSwapchain() = default;
|
||||||
|
|
||||||
|
@ -75,21 +75,18 @@ void VKSwapchain::Create(u32 width, u32 height, bool srgb) {
|
||||||
CreateSemaphores();
|
CreateSemaphores();
|
||||||
CreateImageViews();
|
CreateImageViews();
|
||||||
|
|
||||||
fences.resize(image_count, nullptr);
|
resource_ticks.clear();
|
||||||
|
resource_ticks.resize(image_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKSwapchain::AcquireNextImage() {
|
void VKSwapchain::AcquireNextImage() {
|
||||||
device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(),
|
device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(),
|
||||||
*present_semaphores[frame_index], {}, &image_index);
|
*present_semaphores[frame_index], {}, &image_index);
|
||||||
|
|
||||||
if (auto& fence = fences[image_index]; fence) {
|
scheduler.Wait(resource_ticks[image_index]);
|
||||||
fence->Wait();
|
|
||||||
fence->Release();
|
|
||||||
fence = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VKSwapchain::Present(VkSemaphore render_semaphore, VKFence& fence) {
|
bool VKSwapchain::Present(VkSemaphore render_semaphore) {
|
||||||
const VkSemaphore present_semaphore{*present_semaphores[frame_index]};
|
const VkSemaphore present_semaphore{*present_semaphores[frame_index]};
|
||||||
const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore};
|
const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore};
|
||||||
const auto present_queue{device.GetPresentQueue()};
|
const auto present_queue{device.GetPresentQueue()};
|
||||||
|
@ -123,8 +120,7 @@ bool VKSwapchain::Present(VkSemaphore render_semaphore, VKFence& fence) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(fences[image_index] == nullptr);
|
resource_ticks[image_index] = scheduler.CurrentTick();
|
||||||
fences[image_index] = &fence;
|
|
||||||
frame_index = (frame_index + 1) % static_cast<u32>(image_count);
|
frame_index = (frame_index + 1) % static_cast<u32>(image_count);
|
||||||
return recreated;
|
return recreated;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@ struct FramebufferLayout;
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
class VKFence;
|
class VKScheduler;
|
||||||
|
|
||||||
class VKSwapchain {
|
class VKSwapchain {
|
||||||
public:
|
public:
|
||||||
explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device);
|
explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device, VKScheduler& scheduler);
|
||||||
~VKSwapchain();
|
~VKSwapchain();
|
||||||
|
|
||||||
/// Creates (or recreates) the swapchain with a given size.
|
/// 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
|
/// Presents the rendered image to the swapchain. Returns true when the swapchains had to be
|
||||||
/// recreated. Takes responsability for the ownership of fence.
|
/// 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.
|
/// Returns true when the framebuffer layout has changed.
|
||||||
bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const;
|
bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const;
|
||||||
|
@ -74,6 +74,7 @@ private:
|
||||||
|
|
||||||
const VkSurfaceKHR surface;
|
const VkSurfaceKHR surface;
|
||||||
const VKDevice& device;
|
const VKDevice& device;
|
||||||
|
VKScheduler& scheduler;
|
||||||
|
|
||||||
vk::SwapchainKHR swapchain;
|
vk::SwapchainKHR swapchain;
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ private:
|
||||||
std::vector<VkImage> images;
|
std::vector<VkImage> images;
|
||||||
std::vector<vk::ImageView> image_views;
|
std::vector<vk::ImageView> image_views;
|
||||||
std::vector<vk::Framebuffer> framebuffers;
|
std::vector<vk::Framebuffer> framebuffers;
|
||||||
std::vector<VKFence*> fences;
|
std::vector<u64> resource_ticks;
|
||||||
std::vector<vk::Semaphore> present_semaphores;
|
std::vector<vk::Semaphore> present_semaphores;
|
||||||
|
|
||||||
u32 image_index{};
|
u32 image_index{};
|
||||||
|
|
|
@ -188,13 +188,11 @@ u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, Tegra::Texture::Swizzl
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
CachedSurface::CachedSurface(const VKDevice& device, VKResourceManager& resource_manager,
|
CachedSurface::CachedSurface(const VKDevice& device, VKMemoryManager& memory_manager,
|
||||||
VKMemoryManager& memory_manager, VKScheduler& scheduler,
|
VKScheduler& scheduler, VKStagingBufferPool& staging_pool,
|
||||||
VKStagingBufferPool& staging_pool, GPUVAddr gpu_addr,
|
GPUVAddr gpu_addr, const SurfaceParams& params)
|
||||||
const SurfaceParams& params)
|
|
||||||
: SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, device{device},
|
: SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, device{device},
|
||||||
resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler},
|
memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {
|
||||||
staging_pool{staging_pool} {
|
|
||||||
if (params.IsBuffer()) {
|
if (params.IsBuffer()) {
|
||||||
buffer = CreateBuffer(device, params, host_memory_size);
|
buffer = CreateBuffer(device, params, host_memory_size);
|
||||||
commit = memory_manager.Commit(buffer, false);
|
commit = memory_manager.Commit(buffer, false);
|
||||||
|
@ -493,18 +491,17 @@ VkImageView CachedSurfaceView::GetAttachment() {
|
||||||
VKTextureCache::VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
|
VKTextureCache::VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
|
||||||
Tegra::Engines::Maxwell3D& maxwell3d,
|
Tegra::Engines::Maxwell3D& maxwell3d,
|
||||||
Tegra::MemoryManager& gpu_memory, const VKDevice& device_,
|
Tegra::MemoryManager& gpu_memory, const VKDevice& device_,
|
||||||
VKResourceManager& resource_manager_,
|
|
||||||
VKMemoryManager& memory_manager_, VKScheduler& scheduler_,
|
VKMemoryManager& memory_manager_, VKScheduler& scheduler_,
|
||||||
VKStagingBufferPool& staging_pool_)
|
VKStagingBufferPool& staging_pool_)
|
||||||
: TextureCache(rasterizer, maxwell3d, gpu_memory, device_.IsOptimalAstcSupported()),
|
: TextureCache(rasterizer, maxwell3d, gpu_memory, device_.IsOptimalAstcSupported()),
|
||||||
device{device_}, resource_manager{resource_manager_},
|
device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{
|
||||||
memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{staging_pool_} {}
|
staging_pool_} {}
|
||||||
|
|
||||||
VKTextureCache::~VKTextureCache() = default;
|
VKTextureCache::~VKTextureCache() = default;
|
||||||
|
|
||||||
Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
|
Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
|
||||||
return std::make_shared<CachedSurface>(device, resource_manager, memory_manager, scheduler,
|
return std::make_shared<CachedSurface>(device, memory_manager, scheduler, staging_pool,
|
||||||
staging_pool, gpu_addr, params);
|
gpu_addr, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface,
|
void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface,
|
||||||
|
|
|
@ -23,7 +23,6 @@ namespace Vulkan {
|
||||||
|
|
||||||
class RasterizerVulkan;
|
class RasterizerVulkan;
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
class VKResourceManager;
|
|
||||||
class VKScheduler;
|
class VKScheduler;
|
||||||
class VKStagingBufferPool;
|
class VKStagingBufferPool;
|
||||||
|
|
||||||
|
@ -41,10 +40,9 @@ class CachedSurface final : public VideoCommon::SurfaceBase<View> {
|
||||||
friend CachedSurfaceView;
|
friend CachedSurfaceView;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CachedSurface(const VKDevice& device, VKResourceManager& resource_manager,
|
explicit CachedSurface(const VKDevice& device, VKMemoryManager& memory_manager,
|
||||||
VKMemoryManager& memory_manager, VKScheduler& scheduler,
|
VKScheduler& scheduler, VKStagingBufferPool& staging_pool,
|
||||||
VKStagingBufferPool& staging_pool, GPUVAddr gpu_addr,
|
GPUVAddr gpu_addr, const SurfaceParams& params);
|
||||||
const SurfaceParams& params);
|
|
||||||
~CachedSurface();
|
~CachedSurface();
|
||||||
|
|
||||||
void UploadTexture(const std::vector<u8>& staging_buffer) override;
|
void UploadTexture(const std::vector<u8>& staging_buffer) override;
|
||||||
|
@ -98,7 +96,6 @@ private:
|
||||||
VkImageSubresourceRange GetImageSubresourceRange() const;
|
VkImageSubresourceRange GetImageSubresourceRange() const;
|
||||||
|
|
||||||
const VKDevice& device;
|
const VKDevice& device;
|
||||||
VKResourceManager& resource_manager;
|
|
||||||
VKMemoryManager& memory_manager;
|
VKMemoryManager& memory_manager;
|
||||||
VKScheduler& scheduler;
|
VKScheduler& scheduler;
|
||||||
VKStagingBufferPool& staging_pool;
|
VKStagingBufferPool& staging_pool;
|
||||||
|
@ -198,9 +195,8 @@ class VKTextureCache final : public TextureCacheBase {
|
||||||
public:
|
public:
|
||||||
explicit VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
|
explicit VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
|
||||||
Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
|
Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
|
||||||
const VKDevice& device, VKResourceManager& resource_manager,
|
const VKDevice& device, VKMemoryManager& memory_manager,
|
||||||
VKMemoryManager& memory_manager, VKScheduler& scheduler,
|
VKScheduler& scheduler, VKStagingBufferPool& staging_pool);
|
||||||
VKStagingBufferPool& staging_pool);
|
|
||||||
~VKTextureCache();
|
~VKTextureCache();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -215,7 +211,6 @@ private:
|
||||||
void BufferCopy(Surface& src_surface, Surface& dst_surface) override;
|
void BufferCopy(Surface& src_surface, Surface& dst_surface) override;
|
||||||
|
|
||||||
const VKDevice& device;
|
const VKDevice& device;
|
||||||
VKResourceManager& resource_manager;
|
|
||||||
VKMemoryManager& memory_manager;
|
VKMemoryManager& memory_manager;
|
||||||
VKScheduler& scheduler;
|
VKScheduler& scheduler;
|
||||||
VKStagingBufferPool& staging_pool;
|
VKStagingBufferPool& staging_pool;
|
||||||
|
|
|
@ -148,6 +148,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||||
X(vkGetFenceStatus);
|
X(vkGetFenceStatus);
|
||||||
X(vkGetImageMemoryRequirements);
|
X(vkGetImageMemoryRequirements);
|
||||||
X(vkGetQueryPoolResults);
|
X(vkGetQueryPoolResults);
|
||||||
|
X(vkGetSemaphoreCounterValueKHR);
|
||||||
X(vkMapMemory);
|
X(vkMapMemory);
|
||||||
X(vkQueueSubmit);
|
X(vkQueueSubmit);
|
||||||
X(vkResetFences);
|
X(vkResetFences);
|
||||||
|
@ -156,6 +157,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||||
X(vkUpdateDescriptorSetWithTemplateKHR);
|
X(vkUpdateDescriptorSetWithTemplateKHR);
|
||||||
X(vkUpdateDescriptorSets);
|
X(vkUpdateDescriptorSets);
|
||||||
X(vkWaitForFences);
|
X(vkWaitForFences);
|
||||||
|
X(vkWaitSemaphoresKHR);
|
||||||
#undef X
|
#undef X
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,7 +576,10 @@ Semaphore Device::CreateSemaphore() const {
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
};
|
};
|
||||||
|
return CreateSemaphore(ci);
|
||||||
|
}
|
||||||
|
|
||||||
|
Semaphore Device::CreateSemaphore(const VkSemaphoreCreateInfo& ci) const {
|
||||||
VkSemaphore object;
|
VkSemaphore object;
|
||||||
Check(dld->vkCreateSemaphore(handle, &ci, nullptr, &object));
|
Check(dld->vkCreateSemaphore(handle, &ci, nullptr, &object));
|
||||||
return Semaphore(object, handle, *dld);
|
return Semaphore(object, handle, *dld);
|
||||||
|
@ -660,7 +665,7 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons
|
||||||
return ShaderModule(object, handle, *dld);
|
return ShaderModule(object, handle, *dld);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event Device::CreateNewEvent() const {
|
Event Device::CreateEvent() const {
|
||||||
static constexpr VkEventCreateInfo ci{
|
static constexpr VkEventCreateInfo ci{
|
||||||
.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
|
|
@ -267,6 +267,7 @@ struct DeviceDispatch : public InstanceDispatch {
|
||||||
PFN_vkGetFenceStatus vkGetFenceStatus;
|
PFN_vkGetFenceStatus vkGetFenceStatus;
|
||||||
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
|
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
|
||||||
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
|
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
|
||||||
|
PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR;
|
||||||
PFN_vkMapMemory vkMapMemory;
|
PFN_vkMapMemory vkMapMemory;
|
||||||
PFN_vkQueueSubmit vkQueueSubmit;
|
PFN_vkQueueSubmit vkQueueSubmit;
|
||||||
PFN_vkResetFences vkResetFences;
|
PFN_vkResetFences vkResetFences;
|
||||||
|
@ -275,6 +276,7 @@ struct DeviceDispatch : public InstanceDispatch {
|
||||||
PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR;
|
PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR;
|
||||||
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
|
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
|
||||||
PFN_vkWaitForFences vkWaitForFences;
|
PFN_vkWaitForFences vkWaitForFences;
|
||||||
|
PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Loads instance agnostic function pointers.
|
/// Loads instance agnostic function pointers.
|
||||||
|
@ -550,7 +552,6 @@ using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>;
|
||||||
using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>;
|
using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>;
|
||||||
using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>;
|
using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>;
|
||||||
using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>;
|
using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>;
|
||||||
using Semaphore = Handle<VkSemaphore, VkDevice, DeviceDispatch>;
|
|
||||||
using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>;
|
using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>;
|
||||||
using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>;
|
using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>;
|
||||||
|
|
||||||
|
@ -582,7 +583,8 @@ public:
|
||||||
/// Construct a queue handle.
|
/// Construct a queue handle.
|
||||||
constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {}
|
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);
|
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> {
|
class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
|
||||||
using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle;
|
using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle;
|
||||||
|
|
||||||
|
@ -694,6 +734,8 @@ public:
|
||||||
|
|
||||||
Semaphore CreateSemaphore() const;
|
Semaphore CreateSemaphore() const;
|
||||||
|
|
||||||
|
Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const;
|
||||||
|
|
||||||
Fence CreateFence(const VkFenceCreateInfo& ci) const;
|
Fence CreateFence(const VkFenceCreateInfo& ci) const;
|
||||||
|
|
||||||
DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
|
DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
|
||||||
|
@ -721,7 +763,7 @@ public:
|
||||||
|
|
||||||
ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
|
ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
|
||||||
|
|
||||||
Event CreateNewEvent() const;
|
Event CreateEvent() const;
|
||||||
|
|
||||||
SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
|
SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,17 @@
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <thread>
|
#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 "common/common_types.h"
|
||||||
#include "video_core/renderer_opengl/gl_device.h"
|
#include "video_core/renderer_opengl/gl_device.h"
|
||||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||||
|
|
Loading…
Reference in New Issue