video_core: Fix remaining validation errors. Introduce PoolManager

This commit is contained in:
emufan4568
2022-08-12 13:38:36 +03:00
parent 09997b31af
commit a2ca75bff4
29 changed files with 506 additions and 258 deletions

View File

@@ -33,6 +33,7 @@ add_library(video_core STATIC
common/pica_uniforms.h
common/pipeline_cache.cpp
common/pipeline_cache.h
common/pool_manager.h
common/rasterizer.cpp
common/rasterizer.h
common/rasterizer_cache.cpp

View File

@@ -5,6 +5,7 @@
#pragma once
#include "common/vector_math.h"
#include "video_core/common/pool_manager.h"
#include "video_core/common/pipeline.h"
#include "video_core/common/framebuffer.h"
@@ -77,6 +78,7 @@ public:
Common::Vec3<u32> groups) = 0;
protected:
Frontend::EmuWindow& window;
PoolManager pool_manager;
};
} // namespace VideoCore

View File

@@ -12,20 +12,20 @@
namespace VideoCore {
enum class BufferUsage : u8 {
Vertex = 0,
Index = 1,
Uniform = 2,
Texel = 3,
Staging = 4,
Undefined = 255
Undefined = 0,
Vertex = 1,
Index = 2,
Uniform = 3,
Texel = 4,
Staging = 5
};
enum class ViewFormat : u8 {
R32Float = 0,
R32G32Float = 1,
R32G32B32Float = 2,
R32G32B32A32Float = 3,
Undefined = 255
Undefined = 0,
R32Float = 1,
R32G32Float = 2,
R32G32B32Float = 3,
R32G32B32A32Float = 4,
};
constexpr u32 MAX_BUFFER_VIEWS = 3;
@@ -33,7 +33,7 @@ constexpr u32 MAX_BUFFER_VIEWS = 3;
struct BufferInfo {
u32 capacity = 0;
BufferUsage usage = BufferUsage::Undefined;
std::array<ViewFormat, MAX_BUFFER_VIEWS> views{ViewFormat::Undefined};
std::array<ViewFormat, MAX_BUFFER_VIEWS> views{};
auto operator<=>(const BufferInfo& info) const = default;
@@ -45,12 +45,17 @@ struct BufferInfo {
static_assert(sizeof(BufferInfo) == 8, "BufferInfo not packed!");
static_assert(std::is_standard_layout_v<BufferInfo>, "BufferInfo is not a standard layout!");
class BufferBase : public IntrusivePtrEnabled<BufferBase> {
struct BufferDeleter;
class BufferBase : public IntrusivePtrEnabled<BufferBase, BufferDeleter> {
public:
BufferBase() = default;
BufferBase(const BufferInfo& info) : info(info) {}
virtual ~BufferBase() = default;
// This method is called by BufferDeleter. Forward to the derived pool!
virtual void Free() = 0;
// Disable copy constructor
BufferBase(const BufferBase&) = delete;
BufferBase& operator=(const BufferBase&) = delete;
@@ -95,6 +100,13 @@ protected:
bool invalid = false;
};
// Foward pointer to its parent pool
struct BufferDeleter {
void operator()(BufferBase* buffer) {
buffer->Free();
}
};
using BufferHandle = IntrusivePtr<BufferBase>;
} // namespace VideoCore

View File

@@ -22,9 +22,7 @@ enum class LoadOp : u8 {
Clear = 1
};
/**
* Information about a framebuffer
*/
// Information about a framebuffer
struct FramebufferInfo {
TextureHandle color;
TextureHandle depth_stencil;
@@ -39,12 +37,17 @@ struct FramebufferInfo {
}
};
struct FramebufferDeleter;
// A framebuffer is a collection of render targets and their configuration
class FramebufferBase : public IntrusivePtrEnabled<FramebufferBase> {
class FramebufferBase : public IntrusivePtrEnabled<FramebufferBase, FramebufferDeleter> {
public:
FramebufferBase(const FramebufferInfo& info) : info(info) {}
virtual ~FramebufferBase() = default;
// This method is called by FramebufferDeleter. Forward to the derived pool!
virtual void Free() = 0;
// Disable copy constructor
FramebufferBase(const FramebufferBase&) = delete;
FramebufferBase& operator=(const FramebufferBase&) = delete;
@@ -100,6 +103,13 @@ protected:
FramebufferInfo info;
};
// Foward pointer to its parent pool
struct FramebufferDeleter {
void operator()(FramebufferBase* framebuffer) {
framebuffer->Free();
}
};
using FramebufferHandle = IntrusivePtr<FramebufferBase>;
} // namespace VideoCore

View File

@@ -140,12 +140,17 @@ struct PipelineInfo {
constexpr s32 WHOLE_SIZE = -1;
struct PipelineDeleter;
// An opaque handle to a backend specific program pipeline
class PipelineBase : public IntrusivePtrEnabled<PipelineBase> {
class PipelineBase : public IntrusivePtrEnabled<PipelineBase, PipelineDeleter> {
public:
PipelineBase(PipelineType type, PipelineInfo info) : type(type) {}
virtual ~PipelineBase() = default;
// This method is called by PipelineDeleter. Forward to the derived pool!
virtual void Free() = 0;
// Disable copy constructor
PipelineBase(const PipelineBase&) = delete;
PipelineBase& operator=(const PipelineBase&) = delete;
@@ -181,6 +186,13 @@ protected:
PipelineType type = PipelineType::Graphics;
};
// Foward pointer to its parent pool
struct PipelineDeleter {
void operator()(PipelineBase* pipeline) {
pipeline->Free();
}
};
using PipelineHandle = IntrusivePtr<PipelineBase>;
} // namespace VideoCore

View File

@@ -44,6 +44,10 @@ PipelineCache::PipelineCache(Frontend::EmuWindow& emu_window, std::unique_ptr<Ba
disk_cache(backend) {
// TODO: Don't hardcode this!
generator = std::make_unique<Vulkan::ShaderGenerator>();
trivial_vertex_shader = backend->CreateShader(ShaderStage::Vertex, "Trivial vertex shader",
generator->GenerateTrivialVertexShader());
trivial_vertex_shader->Compile(ShaderOptimization::Debug);
}
PipelineHandle PipelineCache::GetPipeline(PipelineInfo& info) {

View File

@@ -0,0 +1,35 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/object_pool.h"
#include "common/intrusive_ptr.h"
namespace VideoCore {
// Manages (de)allocation of video backend resources
class PoolManager {
public:
template <typename T, typename... P>
IntrusivePtr<T> Allocate(P&&... p) {
auto& pool = GetPoolForType<T>();
return IntrusivePtr<T>{pool.Allocate(std::forward<P>(p)...)};
}
template <typename T>
void Free(T* ptr) {
auto& pool = GetPoolForType<T>();
pool.Free(ptr);
}
private:
template <typename T>
ObjectPool<T>& GetPoolForType() {
static ObjectPool<T> resource_pool;
return resource_pool;
}
};
} // namespace VideoCore

View File

@@ -186,6 +186,17 @@ Rasterizer::Rasterizer(Frontend::EmuWindow& emu_window, std::unique_ptr<BackendB
texel_buffer_lut = backend->CreateBuffer(TEXEL_BUFFER_INFO);
texel_buffer_lut_lf = backend->CreateBuffer(TEXEL_BUFFER_LF_INFO);
const SamplerInfo cube_sampler_info = {
.mag_filter = Pica::TextureFilter::Linear,
.min_filter = Pica::TextureFilter::Linear,
.mip_filter = Pica::TextureFilter::Linear,
.wrap_s = Pica::WrapMode::ClampToEdge,
.wrap_t = Pica::WrapMode::ClampToEdge
};
// TODO: Texture cubes
texture_cube_sampler = backend->CreateSampler(cube_sampler_info);
// TODO: Have the backend say this
uniform_buffer_alignment = 64;
uniform_size_aligned_vs = Common::AlignUp<std::size_t>(sizeof(VSUniformData), uniform_buffer_alignment);
@@ -794,8 +805,9 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) {
if (auto iter = sampler_cache.find(key); iter != sampler_cache.end()) {
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, iter->second);
} else {
auto result = sampler_cache.emplace(key, backend->CreateSampler(key));
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, result.first->second);
SamplerHandle texture_sampler = backend->CreateSampler(key);
auto result = sampler_cache.emplace(key, texture_sampler);
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, texture_sampler);
}
Surface surface = res_cache.GetTextureSurface(texture);
@@ -815,9 +827,16 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) {
} else {
//state.texture_units[texture_index].texture_2d = 0;
raster_pipeline->BindTexture(TEXTURE_GROUP, texture_index, clear_texture);
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, texture_cube_sampler);
}
}
// TODO: Implement texture cubes
raster_pipeline->BindTexture(TEXTURE_GROUP, 3, clear_texture);
// TODO: Implement texture cubes
raster_pipeline->BindSampler(SAMPLER_GROUP, 3, texture_cube_sampler);
// Sync the LUTs within the texture buffer
SyncAndUploadLUTs();
SyncAndUploadLUTsLF();
@@ -1921,7 +1940,9 @@ void Rasterizer::SyncAndUploadLUTsLF() {
uniform_block_data.fog_lut_dirty = false;
}
texel_buffer_lut_lf->Commit(bytes_used);
if (bytes_used > 0) {
texel_buffer_lut_lf->Commit(bytes_used);
}
}
void Rasterizer::SyncAndUploadLUTs() {
@@ -2022,7 +2043,9 @@ void Rasterizer::SyncAndUploadLUTs() {
uniform_block_data.proctex_diff_lut_dirty = false;
}
texel_buffer_lut->Commit(bytes_used);
if (bytes_used > 0) {
texel_buffer_lut->Commit(bytes_used);
}
}
void Rasterizer::UploadUniforms(PipelineHandle pipeline, bool accelerate_draw) {
@@ -2040,28 +2063,30 @@ void Rasterizer::UploadUniforms(PipelineHandle pipeline, bool accelerate_draw) {
const bool invalidate = uniform_buffer->IsInvalid();
const u32 offset = uniform_buffer->GetCurrentOffset();
// Re-bind uniform buffer with the new range
pipeline->BindBuffer(UTILITY_GROUP, 0, uniform_buffer, offset + used_bytes, sizeof(VSUniformData));
if (sync_vs) {
VSUniformData vs_uniforms;
vs_uniforms.uniforms.SetFromRegs(Pica::g_state.regs.vs, Pica::g_state.vs);
std::memcpy(uniforms.data() + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
// Re-bind uniform buffer with the new range
pipeline->BindBuffer(UTILITY_GROUP, 0, uniform_buffer, offset + used_bytes, sizeof(VSUniformData));
used_bytes += uniform_size_aligned_vs;
}
// Re-bind uniform buffer with the new range
pipeline->BindBuffer(UTILITY_GROUP, 1, uniform_buffer, offset + used_bytes, sizeof(UniformData));
if (sync_fs || invalidate) {
std::memcpy(uniforms.data() + used_bytes, &uniform_block_data.data, sizeof(UniformData));
// Re-bind uniform buffer with the new range
pipeline->BindBuffer(UTILITY_GROUP, 1, uniform_buffer, offset + used_bytes, sizeof(UniformData));
uniform_block_data.dirty = false;
used_bytes += uniform_size_aligned_fs;
}
uniform_buffer->Commit(used_bytes);
if (used_bytes > 0) {
uniform_buffer->Commit(used_bytes);
}
}
} // namespace VideoCore

View File

@@ -236,7 +236,7 @@ private:
std::array<Common::Vec4f, 256> proctex_diff_lut_data{};
// Texture unit sampler cache
SamplerInfo texture_cube_sampler;
SamplerHandle texture_cube_sampler;
std::array<SamplerInfo, 3> texture_samplers;
std::unordered_map<SamplerInfo, SamplerHandle> sampler_cache;

View File

@@ -51,8 +51,7 @@ TextureFormat GetTextureFormat(PixelFormat pixel_format) {
return depth_texture_formats[tuple_idx];
}
LOG_ERROR(Render_Vulkan, "Unknown pixel format {}!", pixel_format);
return TextureFormat::Undefined;
return TextureFormat::RGBA8;
}
template <typename Map, typename Interval>

View File

@@ -26,22 +26,27 @@ enum class ShaderOptimization : u32 {
Debug = 1
};
/// Compiles shader source to backend representation
class ShaderBase : public IntrusivePtrEnabled<ShaderBase> {
struct ShaderDeleter;
// Compiles shader source to backend representation
class ShaderBase : public IntrusivePtrEnabled<ShaderBase, ShaderDeleter> {
public:
ShaderBase(ShaderStage stage, std::string_view name, std::string&& source) :
name(name), stage(stage), source(source) {}
virtual ~ShaderBase() = default;
/// Compiles the shader source code
// This method is called by ShaderDeleter. Forward to the derived pool!
virtual void Free() = 0;
// Compiles the shader source code
virtual bool Compile(ShaderOptimization level) = 0;
/// Returns the name given the shader module
// Returns the name given the shader module
std::string_view GetName() const {
return name;
}
/// Returns the pipeline stage the shader is assigned to
// Returns the pipeline stage the shader is assigned to
ShaderStage GetStage() const {
return stage;
}
@@ -52,6 +57,13 @@ protected:
std::string source = "";
};
// Foward pointer to its parent pool
struct ShaderDeleter {
void operator()(ShaderBase* shader) {
shader->Free();
}
};
using ShaderHandle = IntrusivePtr<ShaderBase>;
} // namespace VideoCore

View File

@@ -16,34 +16,34 @@ constexpr u32 MAX_COLOR_FORMATS = 5;
constexpr u32 MAX_DEPTH_FORMATS = 3;
enum class TextureFormat : u8 {
RGBA8 = 0,
RGB8 = 1,
RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
D16 = 5,
D24 = 6,
D24S8 = 7,
PresentColor = 8, // Backend specific swapchain format
Undefined = 255
Undefined = 0,
RGBA8 = 1,
RGB8 = 2,
RGB5A1 = 3,
RGB565 = 4,
RGBA4 = 5,
D16 = 6,
D24 = 7,
D24S8 = 8,
PresentColor = 9 // Backend specific swapchain format
};
enum class TextureType : u8 {
Texture1D = 0,
Texture2D = 1,
Texture3D = 2,
Undefined = 255
Undefined = 0,
Texture1D = 1,
Texture2D = 2,
Texture3D = 3
};
enum class TextureViewType : u8 {
View1D = 0,
View2D = 1,
View3D = 2,
ViewCube = 3,
View1DArray = 4,
View2DArray = 5,
ViewCubeArray = 6,
Undefined = 255
Undefined = 0,
View1D = 1,
View2D = 2,
View3D = 3,
ViewCube = 4,
View1DArray = 5,
View2DArray = 6,
ViewCubeArray = 7,
};
/**
@@ -64,9 +64,7 @@ struct Rect2D {
u32 height = 0;
};
/**
* Information about a texture packed to 8 bytes
*/
// Information about a texture packed to 8 bytes
struct TextureInfo {
u16 width = 0;
u16 height = 0;
@@ -92,12 +90,17 @@ static_assert(std::is_standard_layout_v<TextureInfo>, "TextureInfo is not a stan
class TextureBase;
using TextureHandle = IntrusivePtr<TextureBase>;
class TextureBase : public IntrusivePtrEnabled<TextureBase> {
struct TextureDeleter;
class TextureBase : public IntrusivePtrEnabled<TextureBase, TextureDeleter> {
public:
TextureBase() = default;
TextureBase(const TextureInfo& info) : info(info) {}
virtual ~TextureBase() = default;
// This method is called by TextureDeleter. Forward to the derived pool!
virtual void Free() = 0;
// Disable copy constructor
TextureBase(const TextureBase&) = delete;
TextureBase& operator=(const TextureBase&) = delete;
@@ -111,9 +114,8 @@ public:
u32 level = 0) {};
// Copies the rectangle area specified to the destionation texture
virtual void BlitTo(TextureHandle dest, Rect2D source_rect, Rect2D dest_rect,
u32 src_level = 0, u32 dest_level = 0,
u32 src_layer = 0, u32 dest_layer = 0) {};
virtual void BlitTo(TextureHandle dest, Common::Rectangle<u32> source_rect, Common::Rectangle<u32> dest_rect,
u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) {};
// Copies texture data from the source texture
virtual void CopyFrom(TextureHandle source) {};
@@ -155,6 +157,14 @@ protected:
TextureInfo info;
};
// Foward pointer to its parent pool
struct TextureDeleter {
void operator()(TextureBase* texture) {
texture->Free();
}
};
// Information about a sampler
struct SamplerInfo {
Pica::TextureFilter mag_filter;
Pica::TextureFilter min_filter;
@@ -173,11 +183,16 @@ struct SamplerInfo {
}
};
class SamplerBase : public IntrusivePtrEnabled<SamplerBase> {
struct SamplerDeleter;
class SamplerBase : public IntrusivePtrEnabled<SamplerBase, SamplerDeleter> {
public:
SamplerBase(SamplerInfo info) : info(info) {}
virtual ~SamplerBase() = default;
// This method is called by SamplerDeleter. Forward to the derived pool!
virtual void Free() = 0;
// Disable copy constructor
SamplerBase(const SamplerBase&) = delete;
SamplerBase& operator=(const SamplerBase&) = delete;
@@ -186,6 +201,13 @@ protected:
SamplerInfo info{};
};
// Foward pointer to its parent pool
struct SamplerDeleter {
void operator()(SamplerBase* sampler) {
sampler->Free();
}
};
using SamplerHandle = IntrusivePtr<SamplerBase>;
} // namespace VideoCore

View File

@@ -3,9 +3,9 @@
// Refer to the license.txt file included.
#define VULKAN_HPP_NO_CONSTRUCTORS
#include <functional>
#include "core/core.h"
#include "core/frontend/emu_window.h"
#include "common/object_pool.h"
#include "video_core/renderer_vulkan/vk_backend.h"
#include "video_core/renderer_vulkan/vk_buffer.h"
#include "video_core/renderer_vulkan/vk_texture.h"
@@ -43,8 +43,8 @@ constexpr vk::IndexType ToVkIndexType(AttribType type) {
}
Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
instance(window), scheduler(instance), renderpass_cache(instance),
swapchain(instance, scheduler, renderpass_cache, this, instance.GetSurface()) {
instance(window), scheduler(instance, pool_manager), renderpass_cache(instance),
swapchain(instance, scheduler, renderpass_cache, pool_manager, instance.GetSurface()) {
// TODO: Properly report GPU hardware
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
@@ -67,7 +67,7 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
const vk::DescriptorPoolCreateInfo pool_info = {
.maxSets = 2048,
.poolSizeCount = pool_sizes.size(),
.poolSizeCount = static_cast<u32>(pool_sizes.size()),
.pPoolSizes = pool_sizes.data()
};
@@ -76,9 +76,8 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
descriptor_pools[pool] = device.createDescriptorPool(pool_info);
}
// Create swapchain with the initial window dimentions
const auto& layout = window.GetFramebufferLayout();
swapchain.Create(layout.width, layout.height, false);
auto callback = std::bind(&Backend::OnCommandSwitch, this, std::placeholders::_1);
scheduler.SetSwitchCallback(callback);
}
Backend::~Backend() {
@@ -90,13 +89,10 @@ Backend::~Backend() {
}
}
static bool first = true;
bool Backend::BeginPresent() {
const auto& layout = window.GetFramebufferLayout();
if (swapchain.NeedsRecreation() || first) {
if (swapchain.NeedsRecreation()) {
swapchain.Create(layout.width, layout.height, false);
first = false;
}
swapchain.AcquireNextImage();
@@ -109,7 +105,11 @@ void Backend::EndPresent() {
}
FramebufferHandle Backend::GetWindowFramebuffer() {
return swapchain.GetCurrentFramebuffer();
auto framebuffer = swapchain.GetCurrentFramebuffer();
auto extent = swapchain.GetExtent();
framebuffer->SetDrawRect({0, extent.height, extent.width, 0});
return framebuffer;
}
u64 Backend::PipelineInfoHash(const PipelineInfo& info) {
@@ -130,64 +130,58 @@ u64 Backend::PipelineInfoHash(const PipelineInfo& info) {
* associated with it that batch-allocates memory.
*/
BufferHandle Backend::CreateBuffer(BufferInfo info) {
static ObjectPool<Buffer> buffer_pool;
return BufferHandle{buffer_pool.Allocate(instance, scheduler, info)};
return pool_manager.Allocate<Buffer>(instance, scheduler, pool_manager, info);
}
FramebufferHandle Backend::CreateFramebuffer(FramebufferInfo info) {
static ObjectPool<Framebuffer> framebuffer_pool;
// Get renderpass
TextureFormat color = info.color.IsValid() ? info.color->GetFormat() : TextureFormat::Undefined;
TextureFormat depth = info.depth_stencil.IsValid() ? info.depth_stencil->GetFormat() : TextureFormat::Undefined;
vk::RenderPass load_renderpass = GetRenderPass(color, depth, false);
vk::RenderPass clear_renderpass = GetRenderPass(color, depth, true);
return FramebufferHandle{framebuffer_pool.Allocate(instance, scheduler, info, load_renderpass, clear_renderpass)};
return pool_manager.Allocate<Framebuffer>(instance, scheduler, pool_manager, info,
load_renderpass, clear_renderpass);
}
TextureHandle Backend::CreateTexture(TextureInfo info) {
static ObjectPool<Texture> texture_pool;
return TextureHandle{texture_pool.Allocate(instance, scheduler, info)};
return pool_manager.Allocate<Texture>(instance, scheduler, pool_manager, info);
}
PipelineHandle Backend::CreatePipeline(PipelineType type, PipelineInfo info) {
static ObjectPool<Pipeline> pipeline_pool;
// Get renderpass
vk::RenderPass renderpass = GetRenderPass(info.color_attachment, info.depth_attachment);
// Find an owner first
const u64 layout_hash = Common::ComputeHash64(&info.layout, sizeof(PipelineLayoutInfo));
if (auto iter = pipeline_owners.find(layout_hash); iter != pipeline_owners.end()) {
return PipelineHandle{pipeline_pool.Allocate(instance, scheduler, *iter->second.get(), type, info,
renderpass, pipeline_cache)};
return pool_manager.Allocate<Pipeline>(instance, scheduler, pool_manager, *iter->second.get(), type,
info, renderpass, pipeline_cache);
}
// Create the layout
auto result = pipeline_owners.emplace(layout_hash, std::make_unique<PipelineOwner>(instance, info.layout));
return PipelineHandle{pipeline_pool.Allocate(instance, scheduler, *result.first->second.get(), type, info,
renderpass, pipeline_cache)};
return pool_manager.Allocate<Pipeline>(instance, scheduler, pool_manager, *result.first->second.get(), type,
info, renderpass, pipeline_cache);
}
SamplerHandle Backend::CreateSampler(SamplerInfo info) {
static ObjectPool<Sampler> sampler_pool;
return SamplerHandle{sampler_pool.Allocate(instance, info)};
return pool_manager.Allocate<Sampler>(instance, pool_manager, info);
}
ShaderHandle Backend::CreateShader(ShaderStage stage, std::string_view name, std::string source) {
static ObjectPool<Shader> shader_pool;
return ShaderHandle{shader_pool.Allocate(instance, stage, name, std::move(source))};
return pool_manager.Allocate<Shader>(instance, pool_manager, stage, name, std::move(source));
}
void Backend::BindVertexBuffer(BufferHandle buffer, std::span<const u64> offsets) {
const Buffer* vertex = static_cast<const Buffer*>(buffer.Get());
const u32 buffer_count = static_cast<u32>(offsets.size());
std::array<vk::Buffer, 16> buffers;
buffers.fill(vertex->GetHandle());
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.bindVertexBuffers(0, offsets.size(), buffers.data(), offsets.data());
command_buffer.bindVertexBuffers(0, buffer_count, buffers.data(), offsets.data());
}
void Backend::BindIndexBuffer(BufferHandle buffer, AttribType index_type, u64 offset) {
@@ -253,7 +247,7 @@ void Backend::BindDescriptorSets(PipelineHandle handle) {
std::array<vk::DescriptorSet, MAX_BINDING_GROUPS> bound_sets;
const u32 set_count = pipeline_owner.GetDescriptorSetLayoutCount();
for (int i = 0; i < set_count; i++) {
for (u32 i = 0; i < set_count; i++) {
if (!pipeline_owner.descriptor_dirty[i]) {
// Get the ready descriptor if it hasn't been modified
bound_sets[i] = pipeline_owner.descriptor_bank[i];
@@ -284,7 +278,7 @@ void Backend::BindDescriptorSets(PipelineHandle handle) {
}
void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) {
const Framebuffer* framebuffer = static_cast<const Framebuffer*>(draw_framebuffer.Get());
Framebuffer* framebuffer = static_cast<Framebuffer*>(draw_framebuffer.Get());
u32 clear_value_count = 0;
std::array<vk::ClearValue, 2> clear_values{};
@@ -301,6 +295,9 @@ void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) {
clear_values[clear_value_count++].depthStencil.stencil = framebuffer->clear_stencil_value;
}
// Transition attachments to required layout
framebuffer->PrepareAttachments();
// Use the clear renderpass if the framebuffer was configured so
const vk::RenderPassBeginInfo renderpass_begin = {
.renderPass = framebuffer->GetLoadOp() == LoadOp::Load ?
@@ -316,4 +313,17 @@ void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) {
command_buffer.beginRenderPass(renderpass_begin, vk::SubpassContents::eInline);
}
void Backend::OnCommandSwitch(u32 new_slot) {
// Reset the descriptor pool assigned to the new command slot. This is called
// after Synchronize, so it's guaranteed that the descriptor sets are no longer
// in use.
vk::Device device = instance.GetDevice();
device.resetDescriptorPool(descriptor_pools[new_slot]);
// Mark all descriptor sets as dirty
for (auto& [key, owner] : pipeline_owners) {
owner->descriptor_dirty.fill(true);
}
}
} // namespace VideoCore::Vulkan

View File

@@ -66,6 +66,8 @@ private:
// Begins the renderpass for the provided framebuffer
void BeginRenderpass(FramebufferHandle framebuffer);
void OnCommandSwitch(u32 new_slot);
private:
Instance instance;
CommandScheduler scheduler;

View File

@@ -6,6 +6,7 @@
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/common/pool_manager.h"
#include "video_core/renderer_vulkan/vk_buffer.h"
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
#include "video_core/renderer_vulkan/vk_instance.h"
@@ -13,35 +14,45 @@
namespace VideoCore::Vulkan {
inline vk::BufferUsageFlags ToVkBufferUsage(BufferUsage usage) {
constexpr std::array vk_buffer_usages = {
vk::BufferUsageFlagBits::eVertexBuffer,
vk::BufferUsageFlagBits::eIndexBuffer,
vk::BufferUsageFlagBits::eUniformBuffer,
vk::BufferUsageFlagBits::eUniformTexelBuffer,
vk::BufferUsageFlagBits::eTransferSrc
};
return vk::BufferUsageFlagBits::eTransferDst |
vk_buffer_usages.at(static_cast<u32>(usage));
switch (usage) {
case BufferUsage::Vertex:
return vk::BufferUsageFlagBits::eVertexBuffer;
case BufferUsage::Index:
return vk::BufferUsageFlagBits::eIndexBuffer;
case BufferUsage::Uniform:
return vk::BufferUsageFlagBits::eUniformBuffer;
case BufferUsage::Texel:
return vk::BufferUsageFlagBits::eUniformTexelBuffer;
case BufferUsage::Staging:
return vk::BufferUsageFlagBits::eTransferSrc;
default:
LOG_CRITICAL(Render_Vulkan, "Unknown buffer usage flag {}!", usage);
UNREACHABLE();
}
}
inline vk::Format ToVkViewFormat(ViewFormat format) {
constexpr std::array vk_view_formats = {
vk::Format::eR32Sfloat,
vk::Format::eR32G32Sfloat,
vk::Format::eR32G32B32Sfloat,
vk::Format::eR32G32B32A32Sfloat
};
return vk_view_formats.at(static_cast<u32>(format));
switch (format) {
case ViewFormat::R32Float:
return vk::Format::eR32Sfloat;
case ViewFormat::R32G32Float:
return vk::Format::eR32G32Sfloat;
case ViewFormat::R32G32B32Float:
return vk::Format::eR32G32B32Sfloat;
case ViewFormat::R32G32B32A32Float:
return vk::Format::eR32G32B32A32Sfloat;
default:
LOG_CRITICAL(Render_Vulkan, "Unknown buffer view format {}!", format);
UNREACHABLE();
}
}
Buffer::Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo& info) :
BufferBase(info), instance(instance), scheduler(scheduler) {
Buffer::Buffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, const BufferInfo& info)
: BufferBase(info), instance(instance), scheduler(scheduler), pool_manager(pool_manager) {
vk::BufferCreateInfo buffer_info = {
.size = info.capacity,
.usage = ToVkBufferUsage(info.usage)
.usage = ToVkBufferUsage(info.usage) | vk::BufferUsageFlagBits::eTransferDst
};
VmaAllocationCreateInfo alloc_create_info = {
@@ -65,6 +76,7 @@ Buffer::Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo
vk::Device device = instance.GetDevice();
for (u32 view = 0; view < info.views.size(); view++) {
if (info.views[view] == ViewFormat::Undefined) {
view_count = view;
break;
}
@@ -107,6 +119,10 @@ Buffer::~Buffer() {
}
}
void Buffer::Free() {
pool_manager.Free<Buffer>(this);
}
std::span<u8> Buffer::Map(u32 size, u32 alignment) {
ASSERT(size <= info.capacity && alignment <= info.capacity);

View File

@@ -9,6 +9,10 @@
#include "video_core/common/buffer.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan {
class Instance;
@@ -16,20 +20,21 @@ class CommandScheduler;
class Buffer : public VideoCore::BufferBase {
public:
Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo& info);
Buffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
const BufferInfo& info);
~Buffer() override;
std::span<u8> Map(u32 size, u32 alignment = 0) override;
void Free() override;
/// Flushes write to buffer memory
std::span<u8> Map(u32 size, u32 alignment = 0) override;
void Commit(u32 size = 0) override;
/// Returns the Vulkan buffer handle
// Returns the Vulkan buffer handle
vk::Buffer GetHandle() const {
return buffer;
}
/// Returns an immutable reference to the requested buffer view
// Returns an immutable reference to the requested buffer view
const vk::BufferView& GetView(u32 index = 0) const {
ASSERT(index < view_count);
return views[index];
@@ -38,6 +43,7 @@ public:
protected:
Instance& instance;
CommandScheduler& scheduler;
PoolManager& pool_manager;
// Vulkan buffer handle
void* mapped_ptr = nullptr;

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#define VULKAN_HPP_NO_CONSTRUCTORS
#include "video_core/common/pool_manager.h"
#include "video_core/renderer_vulkan/vk_framebuffer.h"
#include "video_core/renderer_vulkan/vk_texture.h"
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
@@ -17,10 +18,10 @@ inline vk::Rect2D ToVkRect2D(Rect2D rect) {
};
}
Framebuffer::Framebuffer(Instance& instance, CommandScheduler& scheduler, const FramebufferInfo& info,
vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass) :
FramebufferBase(info), instance(instance), scheduler(scheduler), load_renderpass(load_renderpass),
clear_renderpass(clear_renderpass) {
Framebuffer::Framebuffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
const FramebufferInfo& info, vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass) :
FramebufferBase(info), instance(instance), scheduler(scheduler), pool_manager(pool_manager),
load_renderpass(load_renderpass), clear_renderpass(clear_renderpass) {
const Texture* color = static_cast<const Texture*>(info.color.Get());
const Texture* depth_stencil = static_cast<const Texture*>(info.depth_stencil.Get());
@@ -57,9 +58,11 @@ Framebuffer::~Framebuffer() {
device.destroyFramebuffer(framebuffer);
}
void Framebuffer::DoClear() {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
void Framebuffer::Free() {
pool_manager.Free<Framebuffer>(this);
}
void Framebuffer::DoClear() {
u32 clear_value_count = 0;
std::array<vk::ClearValue, 2> clear_values{};
@@ -81,6 +84,9 @@ void Framebuffer::DoClear() {
};
}
// Prepare attachments
PrepareAttachments();
const vk::RenderPassBeginInfo begin_info = {
.renderPass = clear_renderpass,
.framebuffer = framebuffer,
@@ -90,8 +96,22 @@ void Framebuffer::DoClear() {
};
// Begin clear pass
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.beginRenderPass(begin_info, vk::SubpassContents::eInline);
command_buffer.endRenderPass();
}
void Framebuffer::PrepareAttachments() {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
if (info.color.IsValid()) {
Texture* color = static_cast<Texture*>(info.color.Get());
color->Transition(command_buffer, vk::ImageLayout::eColorAttachmentOptimal);
}
if (info.depth_stencil.IsValid()) {
Texture* depth_stencil = static_cast<Texture*>(info.depth_stencil.Get());
depth_stencil->Transition(command_buffer, vk::ImageLayout::eDepthAttachmentOptimal);
}
}
} // namespace VideoCore::Vulkan

View File

@@ -7,6 +7,10 @@
#include "video_core/common/framebuffer.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan {
class Instance;
@@ -15,37 +19,38 @@ class CommandScheduler;
class Framebuffer : public VideoCore::FramebufferBase {
friend class Backend;
public:
Framebuffer(Instance& instance, CommandScheduler& scheduler, const FramebufferInfo& info,
vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass);
Framebuffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
const FramebufferInfo& info, vk::RenderPass load_renderpass,
vk::RenderPass clear_renderpass);
~Framebuffer() override;
void Free() override;
void DoClear() override;
// Transitions the attachments to the required layout
void PrepareAttachments();
// Returns the vulkan framebuffer handle
vk::Framebuffer GetHandle() const {
return framebuffer;
}
// Returns the renderpass with VK_LOAD_OP_LOAD
vk::RenderPass GetLoadRenderpass() const {
return load_renderpass;
}
// Returns the renderpass with VK_LOAD_OP_CLEAR (used for optimized GPU clear)
vk::RenderPass GetClearRenderpass() const {
return clear_renderpass;
}
u32 GetAttachmentCount() const {
if (info.color.IsValid() && info.depth_stencil.IsValid()) {
return 2;
} else if (!info.color.IsValid() && !info.depth_stencil.IsValid()) {
return 0;
} else {
return 1;
}
}
private:
Instance& instance;
CommandScheduler& scheduler;
PoolManager& pool_manager;
// Vulkan framebuffer
vk::Framebuffer framebuffer;
vk::RenderPass load_renderpass, clear_renderpass;
};

View File

@@ -4,6 +4,7 @@
#define VULKAN_HPP_NO_CONSTRUCTORS
#include "common/logging/log.h"
#include "video_core/common/pool_manager.h"
#include "video_core/renderer_vulkan/pica_to_vulkan.h"
#include "video_core/renderer_vulkan/vk_pipeline.h"
#include "video_core/renderer_vulkan/vk_shader.h"
@@ -186,10 +187,9 @@ PipelineOwner::~PipelineOwner() {
}
}
Pipeline::Pipeline(Instance& instance, CommandScheduler& scheduler, PipelineOwner& owner,
PipelineType type, PipelineInfo info,
vk::RenderPass renderpass, vk::PipelineCache cache) : PipelineBase(type, info),
instance(instance), scheduler(scheduler), owner(owner) {
Pipeline::Pipeline(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, PipelineOwner& owner,
PipelineType type, PipelineInfo info, vk::RenderPass renderpass, vk::PipelineCache cache) :
PipelineBase(type, info), instance(instance), scheduler(scheduler), pool_manager(pool_manager), owner(owner) {
vk::Device device = instance.GetDevice();
@@ -382,6 +382,9 @@ Pipeline::~Pipeline() {
device.destroyPipeline(pipeline);
}
void Pipeline::Free() {
pool_manager.Free<Pipeline>(this);
}
void Pipeline::BindTexture(u32 group, u32 slot, TextureHandle handle) {
Texture* texture = static_cast<Texture*>(handle.Get());

View File

@@ -8,6 +8,10 @@
#include "video_core/common/pipeline.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan {
class Instance;
@@ -82,11 +86,12 @@ private:
class Pipeline : public VideoCore::PipelineBase {
public:
Pipeline(Instance& instance, CommandScheduler& scheduler, PipelineOwner& owner,
PipelineType type, PipelineInfo info,
vk::RenderPass renderpass, vk::PipelineCache cache);
Pipeline(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, PipelineOwner& owner,
PipelineType type, PipelineInfo info, vk::RenderPass renderpass, vk::PipelineCache cache);
~Pipeline() override;
void Free() override;
void BindTexture(u32 group, u32 slot, TextureHandle handle) override;
void BindBuffer(u32 group, u32 slot, BufferHandle handle,
u32 offset = 0, u32 range = WHOLE_SIZE, u32 view = 0) override;
@@ -111,6 +116,9 @@ public:
private:
Instance& instance;
CommandScheduler& scheduler;
PoolManager& pool_manager;
// The owner of this pipeline
PipelineOwner& owner;
vk::Pipeline pipeline;
};

View File

@@ -37,18 +37,14 @@ RenderpassCache::RenderpassCache(Instance& instance) : instance(instance) {
vk::Format color_format = instance.GetFormatAlternative(color_formats[color]);
vk::Format depth_stencil_format = instance.GetFormatAlternative(depth_stencil_formats[depth]);
if (color_format == vk::Format::eA8B8G8R8SintPack32) {
LOG_INFO(Render_Vulkan, "");
}
// Construct both load and clear pass
cached_renderpasses[color][depth][0] = CreateRenderPass(color_format, depth_stencil_format,
vk::AttachmentLoadOp::eLoad,
vk::ImageLayout::eShaderReadOnlyOptimal,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::eColorAttachmentOptimal);
cached_renderpasses[color][depth][1] = CreateRenderPass(color_format, depth_stencil_format,
vk::AttachmentLoadOp::eClear,
vk::ImageLayout::eShaderReadOnlyOptimal,
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::eColorAttachmentOptimal);
}
}
@@ -85,7 +81,7 @@ void RenderpassCache::CreatePresentRenderpass(vk::Format format) {
vk::RenderPass RenderpassCache::GetRenderpass(TextureFormat color, TextureFormat depth, bool is_clear) const {
const u32 color_index = static_cast<u32>(color);
const u32 depth_index = static_cast<u32>(depth) - MAX_COLOR_FORMATS;
const u32 depth_index = (depth == TextureFormat::Undefined ? 0 : (static_cast<u32>(depth) - MAX_COLOR_FORMATS));
ASSERT(color_index < MAX_COLOR_FORMATS && depth_index < MAX_DEPTH_FORMATS);
return cached_renderpasses[color_index][depth_index][is_clear];
@@ -127,7 +123,9 @@ vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format de
.format = depth,
.loadOp = load_op,
.storeOp = vk::AttachmentStoreOp::eStore,
.initialLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.stencilLoadOp = vk::AttachmentLoadOp::eLoad,
.stencilStoreOp = vk::AttachmentStoreOp::eStore,
.initialLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal,
.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal
};

View File

@@ -5,6 +5,7 @@
#define VULKAN_HPP_NO_CONSTRUCTORS
#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/common/pool_manager.h"
#include "video_core/renderer_vulkan/vk_shader.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include <glslang/Public/ShaderLang.h>
@@ -154,9 +155,9 @@ bool InitializeCompiler() {
return true;
}
Shader::Shader(Instance& instance, ShaderStage stage, std::string_view name,
std::string&& source) :
ShaderBase(stage, name, std::move(source)), instance(instance) {
Shader::Shader(Instance& instance, PoolManager& pool_manager, ShaderStage stage, std::string_view name,
std::string&& source) : ShaderBase(stage, name, std::move(source)),
instance(instance), pool_manager(pool_manager) {
}
Shader::~Shader() {
@@ -164,6 +165,10 @@ Shader::~Shader() {
device.destroyShaderModule(module);
}
void Shader::Free() {
pool_manager.Free<Shader>(this);
}
bool Shader::Compile(ShaderOptimization level) {
if (!InitializeCompiler()) {
return false;

View File

@@ -7,16 +7,22 @@
#include "video_core/common/shader.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan {
class Instance;
class Shader : public VideoCore::ShaderBase {
public:
Shader(Instance& instance, ShaderStage stage, std::string_view name,
Shader(Instance& instance, PoolManager& pool_manager, ShaderStage stage, std::string_view name,
std::string&& source);
~Shader() override;
void Free() override;
bool Compile(ShaderOptimization level) override;
/// Returns the underlying vulkan shader module handle
@@ -26,6 +32,7 @@ public:
private:
Instance& instance;
PoolManager& pool_manager;
vk::ShaderModule module;
};

View File

@@ -3,22 +3,25 @@
// Refer to the license.txt file included.
#define VULKAN_HPP_NO_CONSTRUCTORS
#include <array>
#include "common/logging/log.h"
#include "video_core/common/pool_manager.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_backend.h"
#include "video_core/renderer_vulkan/vk_framebuffer.h"
#include "video_core/renderer_vulkan/vk_texture.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
namespace VideoCore::Vulkan {
Swapchain::Swapchain(Instance& instance, CommandScheduler& scheduler, RenderpassCache& renderpass_cache,
Backend* backend, vk::SurfaceKHR surface) :
backend(backend), instance(instance), scheduler(scheduler),
renderpass_cache(renderpass_cache), surface(surface) {
PoolManager& pool_manager, vk::SurfaceKHR surface) : instance(instance), scheduler(scheduler),
renderpass_cache(renderpass_cache), pool_manager(pool_manager), surface(surface) {
// Set the surface format early for RenderpassCache to create the present renderpass
Configure(0, 0);
// Create the present renderpass
renderpass_cache.CreatePresentRenderpass(surface_format.format);
}
Swapchain::~Swapchain() {
@@ -42,7 +45,7 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) {
};
const bool exclusive = queue_family_indices[0] == queue_family_indices[1];
const u32 queue_family_indices_count = exclusive ? 2u : 1u;
const u32 queue_family_indices_count = exclusive ? 1u : 2u;
const vk::SharingMode sharing_mode = exclusive ? vk::SharingMode::eExclusive :
vk::SharingMode::eConcurrent;
@@ -81,9 +84,6 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) {
render_finished = device.createSemaphore({});
}
// Create the present renderpass
renderpass_cache.CreatePresentRenderpass(surface_format.format);
// Create framebuffer and image views
vk_images = device.getSwapchainImagesKHR(swapchain);
@@ -102,16 +102,16 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) {
framebuffers.clear();
framebuffers.resize(vk_images.size());
for (int i = 0; i < vk_images.size(); i++) {
textures[i] = MakeHandle<Texture>(instance, scheduler, vk_images[i],
surface_format.format, image_info);
textures[i] = pool_manager.Allocate<Texture>(instance, scheduler, pool_manager, vk_images[i],
surface_format.format, image_info);
const FramebufferInfo framebuffer_info = {
.color = textures[i]
};
vk::RenderPass renderpass = renderpass_cache.GetPresentRenderpass();
framebuffers[i] = MakeHandle<Framebuffer>(instance, scheduler, framebuffer_info,
renderpass, renderpass);
framebuffers[i] = pool_manager.Allocate<Framebuffer>(instance, scheduler, pool_manager, framebuffer_info,
renderpass, renderpass);
}
}

View File

@@ -9,6 +9,10 @@
#include "video_core/common/framebuffer.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan {
class Instance;
@@ -20,59 +24,59 @@ class RenderpassCache;
class Swapchain {
public:
Swapchain(Instance& instance, CommandScheduler& scheduler, RenderpassCache& renderpass_cache,
Backend* backend, vk::SurfaceKHR surface);
PoolManager& pool_manager, vk::SurfaceKHR surface);
~Swapchain();
/// Creates (or recreates) the swapchain with a given size.
// Creates (or recreates) the swapchain with a given size.
void Create(u32 width, u32 height, bool vsync_enabled);
/// Acquire the next image in the swapchain.
// Acquires the next image in the swapchain.
void AcquireNextImage();
/// Present the current image and move to the next one
// Presents the current image and move to the next one
void Present();
FramebufferHandle GetCurrentFramebuffer() const {
return framebuffers[current_image];
}
/// Return current swapchain state
inline vk::Extent2D GetExtent() const {
// Returns current swapchain state
vk::Extent2D GetExtent() const {
return extent;
}
/// Return the swapchain surface
inline vk::SurfaceKHR GetSurface() const {
// Returns the swapchain surface
vk::SurfaceKHR GetSurface() const {
return surface;
}
/// Return the swapchain format
inline vk::SurfaceFormatKHR GetSurfaceFormat() const {
// Returns the swapchain format
vk::SurfaceFormatKHR GetSurfaceFormat() const {
return surface_format;
}
/// Return the Vulkan swapchain handle
inline vk::SwapchainKHR GetHandle() const {
// Returns the Vulkan swapchain handle
vk::SwapchainKHR GetHandle() const {
return swapchain;
}
/// Return the semaphore that will be signaled when vkAcquireNextImageKHR completes
inline vk::Semaphore GetAvailableSemaphore() const {
// Returns the semaphore that will be signaled when vkAcquireNextImageKHR completes
vk::Semaphore GetAvailableSemaphore() const {
return image_available;
}
/// Return the semaphore that will signal when the current image will be presented
inline vk::Semaphore GetPresentSemaphore() const {
// Returns the semaphore that will signal when the current image will be presented
vk::Semaphore GetPresentSemaphore() const {
return render_finished;
}
/// Return the current swapchain image
inline vk::Image GetCurrentImage() {
// Returns the current swapchain image
vk::Image GetCurrentImage() {
return vk_images[current_image];
}
/// Returns true when the swapchain should be recreated
inline bool NeedsRecreation() const {
// Returns true when the swapchain should be recreated
bool NeedsRecreation() const {
return is_suboptimal || is_outdated;
}
@@ -80,10 +84,10 @@ private:
void Configure(u32 width, u32 height);
private:
Backend* backend = nullptr;
Instance& instance;
CommandScheduler& scheduler;
RenderpassCache& renderpass_cache;
PoolManager& pool_manager;
vk::SwapchainKHR swapchain = VK_NULL_HANDLE;
vk::SurfaceKHR surface = VK_NULL_HANDLE;

View File

@@ -4,6 +4,7 @@
#define VULKAN_HPP_NO_CONSTRUCTORS
#include "common/logging/log.h"
#include "video_core/common/pool_manager.h"
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_buffer.h"
@@ -16,7 +17,9 @@ constexpr BufferInfo STAGING_INFO = {
.usage = BufferUsage::Staging
};
CommandScheduler::CommandScheduler(Instance& instance) : instance(instance) {
CommandScheduler::CommandScheduler(Instance& instance, PoolManager& pool_manager) : instance(instance),
pool_manager(pool_manager) {
vk::Device device = instance.GetDevice();
const vk::CommandPoolCreateInfo pool_info = {
.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
@@ -43,7 +46,7 @@ CommandScheduler::CommandScheduler(Instance& instance) : instance(instance) {
.upload_command_buffer = command_buffers[2 * i + 1],
};
commands[i].upload_buffer = std::make_unique<Buffer>(instance, *this, STAGING_INFO);
commands[i].upload_buffer = pool_manager.Allocate<Buffer>(instance, *this, pool_manager, STAGING_INFO);
}
const vk::CommandBufferBeginInfo begin_info = {
@@ -103,9 +106,11 @@ void CommandScheduler::Synchronize() {
completed_fence_counter = now_fence_counter;
}
void CommandScheduler::Submit(bool wait_completion,
vk::Semaphore wait_semaphore,
vk::Semaphore signal_semaphore) {
void CommandScheduler::SetSwitchCallback(std::function<void(u32)> callback) {
switch_callback = callback;
}
void CommandScheduler::Submit(bool wait_completion, vk::Semaphore wait_semaphore, vk::Semaphore signal_semaphore) {
const CommandSlot& command = commands[current_command];
// End command buffers
@@ -149,8 +154,8 @@ void CommandScheduler::Submit(bool wait_completion,
SwitchSlot();
}
void CommandScheduler::Schedule(Deleter&& func) {
auto& command = commands[current_command];
void CommandScheduler::Schedule(std::function<void(vk::Device, VmaAllocator)>&& func) {
CommandSlot& command = commands[current_command];
command.cleanups.push_back(func);
}
@@ -175,6 +180,9 @@ void CommandScheduler::SwitchSlot() {
// Wait for the GPU to finish with all resources for this command.
Synchronize();
// Invoke the callback function
switch_callback(current_command);
const vk::CommandBufferBeginInfo begin_info = {
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
};

View File

@@ -8,59 +8,66 @@
#include <array>
#include <functional>
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_buffer.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan {
constexpr u32 SCHEDULER_COMMAND_COUNT = 4;
using Deleter = std::function<void(vk::Device, VmaAllocator)>;
class Buffer;
class Instance;
class CommandScheduler {
public:
CommandScheduler(Instance& instance);
CommandScheduler(Instance& instance, PoolManager& pool_manager);
~CommandScheduler();
/// Block host until the current command completes execution
// Blocks the host until the current command completes execution
void Synchronize();
/// Defer operation until the current command completes execution
void Schedule(Deleter&& func);
// Sets a function to be called when the command slot is switched
void SetSwitchCallback(std::function<void(u32)> callback);
/// Submits the current command to the graphics queue
// Defers operation until the current command completes execution
void Schedule(std::function<void(vk::Device, VmaAllocator)>&& func);
// Submits the current command to the graphics queue
void Submit(bool wait_completion = false, vk::Semaphore wait = VK_NULL_HANDLE,
vk::Semaphore signal = VK_NULL_HANDLE);
/// Returns the command buffer used for early upload operations.
/// This is useful for vertex/uniform buffer uploads that happen once per frame
// Returns the command buffer used for early upload operations.
// This is useful for vertex/uniform buffer uploads that happen once per frame
vk::CommandBuffer GetUploadCommandBuffer();
/// Returns the command buffer used for rendering
inline vk::CommandBuffer GetRenderCommandBuffer() const {
// Returns the command buffer used for rendering
vk::CommandBuffer GetRenderCommandBuffer() const {
const CommandSlot& command = commands[current_command];
return command.render_command_buffer;
}
/// Returns the upload buffer of the active command slot
inline Buffer& GetCommandUploadBuffer() {
// Returns the upload buffer of the active command slot
Buffer& GetCommandUploadBuffer() {
CommandSlot& command = commands[current_command];
return *command.upload_buffer;
return *static_cast<Buffer*>(command.upload_buffer.Get());
}
/// Returns the index of the current command slot
// Returns the index of the current command slot
inline u32 GetCurrentSlotIndex() const {
return current_command;
}
private:
/// Activates the next command slot and optionally waits for its completion
// Activates the next command slot and optionally waits for its completion
void SwitchSlot();
private:
Instance& instance;
PoolManager& pool_manager;
u64 next_fence_counter = 1;
u64 completed_fence_counter = 0;
@@ -70,12 +77,13 @@ private:
vk::Fence fence = VK_NULL_HANDLE;
vk::CommandBuffer render_command_buffer;
vk::CommandBuffer upload_command_buffer;
std::unique_ptr<Buffer> upload_buffer;
std::vector<Deleter> cleanups;
BufferHandle upload_buffer;
std::vector<std::function<void(vk::Device, VmaAllocator)>> cleanups;
};
vk::CommandPool command_pool = VK_NULL_HANDLE;
std::array<CommandSlot, SCHEDULER_COMMAND_COUNT> commands;
std::function<void(u32)> switch_callback;
u32 current_command = 0;
};

View File

@@ -5,6 +5,7 @@
#define VULKAN_HPP_NO_CONSTRUCTORS
#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/common/pool_manager.h"
#include "video_core/renderer_vulkan/pica_to_vulkan.h"
#include "video_core/renderer_vulkan/vk_buffer.h"
#include "video_core/renderer_vulkan/vk_texture.h"
@@ -73,11 +74,9 @@ inline vk::ImageViewType ToVkImageViewType(TextureViewType view_type) {
}
}
Texture::Texture(Instance& instance, CommandScheduler& scheduler) :
instance(instance), scheduler(scheduler) {}
Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info) : TextureBase(info),
instance(instance), scheduler(scheduler) {
Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
const TextureInfo& info) : TextureBase(info), instance(instance), scheduler(scheduler),
pool_manager(pool_manager) {
// Convert the input format to another that supports attachments
advertised_format = ToVkFormat(info.format);
@@ -118,7 +117,7 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureI
.image = image,
.viewType = ToVkImageViewType(info.view_type),
.format = internal_format,
.subresourceRange = {aspect, 0, info.levels, 0, 1}
.subresourceRange = {aspect, 0, 1, 0, 1} // Should this include the levels?
};
// Create image view
@@ -130,9 +129,9 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureI
Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal, 0, info.levels);
}
Texture::Texture(Instance& instance, CommandScheduler& scheduler, vk::Image image, vk::Format format,
const TextureInfo& info) : TextureBase(info), instance(instance),
scheduler(scheduler), image(image), is_texture_owned(false) {
Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
vk::Image image, vk::Format format, const TextureInfo& info) : TextureBase(info),
instance(instance), scheduler(scheduler), pool_manager(pool_manager), image(image), is_texture_owned(false) {
advertised_format = internal_format = format;
aspect = vk::ImageAspectFlagBits::eColor;
@@ -167,6 +166,10 @@ Texture::~Texture() {
}
}
void Texture::Free() {
pool_manager.Free<Texture>(this);
}
void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, u32 level, u32 level_count) {
ASSERT(level + level_count < TEXTURE_MAX_LEVELS);
@@ -458,8 +461,8 @@ void Texture::Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 lev
}
}
void Texture::BlitTo(TextureHandle dest, Rect2D source_rect, Rect2D dest_rect, u32 src_level, u32 dest_level,
u32 src_layer, u32 dest_layer) {
void Texture::BlitTo(TextureHandle dest, Common::Rectangle<u32> source_rect, Common::Rectangle<u32> dest_rect,
u32 src_level, u32 dest_level, u32 src_layer, u32 dest_layer) {
Texture* dest_texture = static_cast<Texture*>(dest.Get());
@@ -469,15 +472,13 @@ void Texture::BlitTo(TextureHandle dest, Rect2D source_rect, Rect2D dest_rect, u
dest_texture->Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal);
const std::array source_offsets = {
vk::Offset3D{source_rect.x, source_rect.y, 0},
vk::Offset3D{static_cast<s32>(source_rect.x + source_rect.width),
static_cast<s32>(source_rect.y + source_rect.height), 1}
vk::Offset3D{static_cast<s32>(source_rect.left), static_cast<s32>(source_rect.bottom), 0},
vk::Offset3D{static_cast<s32>(source_rect.right), static_cast<s32>(source_rect.top), 1}
};
const std::array dest_offsets = {
vk::Offset3D{dest_rect.x, dest_rect.y, 0},
vk::Offset3D{static_cast<s32>(dest_rect.x + dest_rect.width),
static_cast<s32>(dest_rect.y + dest_rect.height), 1}
vk::Offset3D{static_cast<s32>(dest_rect.left), static_cast<s32>(dest_rect.bottom), 0},
vk::Offset3D{static_cast<s32>(dest_rect.right), static_cast<s32>(dest_rect.top), 1}
};
const vk::ImageBlit blit_area = {
@@ -665,8 +666,8 @@ void StagingTexture::Commit(u32 size) {
vmaFlushAllocation(allocator, allocation, 0, size);
}
Sampler::Sampler(Instance& instance, SamplerInfo info) :
SamplerBase(info), instance(instance) {
Sampler::Sampler(Instance& instance, PoolManager& pool_manager, SamplerInfo info) :
SamplerBase(info), instance(instance), pool_manager(pool_manager) {
auto properties = instance.GetPhysicalDevice().getProperties();
const auto filtering = PicaToVK::TextureFilterMode(info.mag_filter,
@@ -695,4 +696,8 @@ Sampler::~Sampler() {
device.destroySampler(sampler);
}
} // namespace Vulkan
void Sampler::Free() {
pool_manager.Free<Sampler>(this);
}
} // namespace VideoCore::Vulkan

View File

@@ -7,10 +7,14 @@
#include "video_core/common/texture.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan {
// PICA texture have at most 8 mipmap levels
constexpr u32 TEXTURE_MAX_LEVELS = 10;
constexpr u32 TEXTURE_MAX_LEVELS = 12;
class Instance;
class CommandScheduler;
@@ -20,24 +24,24 @@ class CommandScheduler;
*/
class Texture : public VideoCore::TextureBase {
public:
// Default constructor
Texture(Instance& instance, CommandScheduler& scheduler);
// Constructor for texture creation
Texture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info);
// Constructor for swapchain images
Texture(Instance& instance, CommandScheduler& scheduler, vk::Image image, vk::Format format,
Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
const TextureInfo& info);
// Constructor for swapchain images
Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
vk::Image image, vk::Format format, const TextureInfo& info);
~Texture() override;
void Free() override;
void Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32 level = 0) override;
void Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 level = 0) override;
void BlitTo(TextureHandle dest, Rect2D src_rectangle, Rect2D dest_rect, u32 src_level = 0,
u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override;
void BlitTo(TextureHandle dest, Common::Rectangle<u32> src_rectangle, Common::Rectangle<u32> dest_rect,
u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override;
void CopyFrom(TextureHandle source) override;
@@ -83,6 +87,7 @@ public:
private:
Instance& instance;
CommandScheduler& scheduler;
PoolManager& pool_manager;
// Vulkan texture handle
vk::Image image = VK_NULL_HANDLE;
@@ -103,19 +108,20 @@ private:
*/
class StagingTexture : public VideoCore::TextureBase {
public:
StagingTexture(Instance& instance, CommandScheduler& scheduler,
const TextureInfo& info);
StagingTexture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info);
~StagingTexture();
/// Flushes any writes made to texture memory
void Free() override {}
// Flushes any writes made to texture memory
void Commit(u32 size);
/// Returns a span of the mapped texture memory
// Returns a span of the mapped texture memory
void* GetMappedPtr() {
return mapped_ptr;
}
/// Returns the staging image handle
// Returns the staging image handle
vk::Image GetHandle() const {
return image;
}
@@ -136,16 +142,19 @@ private:
*/
class Sampler : public VideoCore::SamplerBase {
public:
Sampler(Instance& instance, SamplerInfo info);
Sampler(Instance& instance, PoolManager& pool_manager, SamplerInfo info);
~Sampler() override;
/// Returns the underlying vulkan sampler handle
void Free() override;
// Returns the underlying vulkan sampler handle
vk::Sampler GetHandle() const {
return sampler;
}
private:
Instance& instance;
PoolManager& pool_manager;
vk::Sampler sampler;
};