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