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/pica_uniforms.h
common/pipeline_cache.cpp common/pipeline_cache.cpp
common/pipeline_cache.h common/pipeline_cache.h
common/pool_manager.h
common/rasterizer.cpp common/rasterizer.cpp
common/rasterizer.h common/rasterizer.h
common/rasterizer_cache.cpp common/rasterizer_cache.cpp

View File

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

View File

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

View File

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

View File

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

View File

@@ -44,6 +44,10 @@ PipelineCache::PipelineCache(Frontend::EmuWindow& emu_window, std::unique_ptr<Ba
disk_cache(backend) { disk_cache(backend) {
// TODO: Don't hardcode this! // TODO: Don't hardcode this!
generator = std::make_unique<Vulkan::ShaderGenerator>(); 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) { 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 = backend->CreateBuffer(TEXEL_BUFFER_INFO);
texel_buffer_lut_lf = backend->CreateBuffer(TEXEL_BUFFER_LF_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 // TODO: Have the backend say this
uniform_buffer_alignment = 64; uniform_buffer_alignment = 64;
uniform_size_aligned_vs = Common::AlignUp<std::size_t>(sizeof(VSUniformData), uniform_buffer_alignment); 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()) { if (auto iter = sampler_cache.find(key); iter != sampler_cache.end()) {
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, iter->second); raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, iter->second);
} else { } else {
auto result = sampler_cache.emplace(key, backend->CreateSampler(key)); SamplerHandle texture_sampler = backend->CreateSampler(key);
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, result.first->second); auto result = sampler_cache.emplace(key, texture_sampler);
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, texture_sampler);
} }
Surface surface = res_cache.GetTextureSurface(texture); Surface surface = res_cache.GetTextureSurface(texture);
@@ -815,9 +827,16 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) {
} else { } else {
//state.texture_units[texture_index].texture_2d = 0; //state.texture_units[texture_index].texture_2d = 0;
raster_pipeline->BindTexture(TEXTURE_GROUP, texture_index, clear_texture); 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 // Sync the LUTs within the texture buffer
SyncAndUploadLUTs(); SyncAndUploadLUTs();
SyncAndUploadLUTsLF(); SyncAndUploadLUTsLF();
@@ -1921,7 +1940,9 @@ void Rasterizer::SyncAndUploadLUTsLF() {
uniform_block_data.fog_lut_dirty = false; 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() { void Rasterizer::SyncAndUploadLUTs() {
@@ -2022,7 +2043,9 @@ void Rasterizer::SyncAndUploadLUTs() {
uniform_block_data.proctex_diff_lut_dirty = false; 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) { 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 bool invalidate = uniform_buffer->IsInvalid();
const u32 offset = uniform_buffer->GetCurrentOffset(); 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) { if (sync_vs) {
VSUniformData vs_uniforms; VSUniformData vs_uniforms;
vs_uniforms.uniforms.SetFromRegs(Pica::g_state.regs.vs, Pica::g_state.vs); vs_uniforms.uniforms.SetFromRegs(Pica::g_state.regs.vs, Pica::g_state.vs);
std::memcpy(uniforms.data() + used_bytes, &vs_uniforms, sizeof(vs_uniforms)); 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; 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) { if (sync_fs || invalidate) {
std::memcpy(uniforms.data() + used_bytes, &uniform_block_data.data, sizeof(UniformData)); 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; uniform_block_data.dirty = false;
used_bytes += uniform_size_aligned_fs; used_bytes += uniform_size_aligned_fs;
} }
uniform_buffer->Commit(used_bytes); if (used_bytes > 0) {
uniform_buffer->Commit(used_bytes);
}
} }
} // namespace VideoCore } // namespace VideoCore

View File

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

View File

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

View File

@@ -26,22 +26,27 @@ enum class ShaderOptimization : u32 {
Debug = 1 Debug = 1
}; };
/// Compiles shader source to backend representation struct ShaderDeleter;
class ShaderBase : public IntrusivePtrEnabled<ShaderBase> {
// Compiles shader source to backend representation
class ShaderBase : public IntrusivePtrEnabled<ShaderBase, ShaderDeleter> {
public: public:
ShaderBase(ShaderStage stage, std::string_view name, std::string&& source) : ShaderBase(ShaderStage stage, std::string_view name, std::string&& source) :
name(name), stage(stage), source(source) {} name(name), stage(stage), source(source) {}
virtual ~ShaderBase() = default; 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; 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 { std::string_view GetName() const {
return name; return name;
} }
/// Returns the pipeline stage the shader is assigned to // Returns the pipeline stage the shader is assigned to
ShaderStage GetStage() const { ShaderStage GetStage() const {
return stage; return stage;
} }
@@ -52,6 +57,13 @@ protected:
std::string source = ""; std::string source = "";
}; };
// Foward pointer to its parent pool
struct ShaderDeleter {
void operator()(ShaderBase* shader) {
shader->Free();
}
};
using ShaderHandle = IntrusivePtr<ShaderBase>; using ShaderHandle = IntrusivePtr<ShaderBase>;
} // namespace VideoCore } // namespace VideoCore

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,10 @@
#include "video_core/common/buffer.h" #include "video_core/common/buffer.h"
#include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan { namespace VideoCore::Vulkan {
class Instance; class Instance;
@@ -16,20 +20,21 @@ class CommandScheduler;
class Buffer : public VideoCore::BufferBase { class Buffer : public VideoCore::BufferBase {
public: public:
Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo& info); Buffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
const BufferInfo& info);
~Buffer() override; ~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; void Commit(u32 size = 0) override;
/// Returns the Vulkan buffer handle // Returns the Vulkan buffer handle
vk::Buffer GetHandle() const { vk::Buffer GetHandle() const {
return buffer; 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 { const vk::BufferView& GetView(u32 index = 0) const {
ASSERT(index < view_count); ASSERT(index < view_count);
return views[index]; return views[index];
@@ -38,6 +43,7 @@ public:
protected: protected:
Instance& instance; Instance& instance;
CommandScheduler& scheduler; CommandScheduler& scheduler;
PoolManager& pool_manager;
// Vulkan buffer handle // Vulkan buffer handle
void* mapped_ptr = nullptr; void* mapped_ptr = nullptr;

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,10 @@
#include "video_core/common/pipeline.h" #include "video_core/common/pipeline.h"
#include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan { namespace VideoCore::Vulkan {
class Instance; class Instance;
@@ -82,11 +86,12 @@ private:
class Pipeline : public VideoCore::PipelineBase { class Pipeline : public VideoCore::PipelineBase {
public: public:
Pipeline(Instance& instance, CommandScheduler& scheduler, PipelineOwner& owner, Pipeline(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, PipelineOwner& owner,
PipelineType type, PipelineInfo info, PipelineType type, PipelineInfo info, vk::RenderPass renderpass, vk::PipelineCache cache);
vk::RenderPass renderpass, vk::PipelineCache cache);
~Pipeline() override; ~Pipeline() override;
void Free() override;
void BindTexture(u32 group, u32 slot, TextureHandle handle) override; void BindTexture(u32 group, u32 slot, TextureHandle handle) override;
void BindBuffer(u32 group, u32 slot, BufferHandle handle, void BindBuffer(u32 group, u32 slot, BufferHandle handle,
u32 offset = 0, u32 range = WHOLE_SIZE, u32 view = 0) override; u32 offset = 0, u32 range = WHOLE_SIZE, u32 view = 0) override;
@@ -111,6 +116,9 @@ public:
private: private:
Instance& instance; Instance& instance;
CommandScheduler& scheduler; CommandScheduler& scheduler;
PoolManager& pool_manager;
// The owner of this pipeline
PipelineOwner& owner; PipelineOwner& owner;
vk::Pipeline pipeline; 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 color_format = instance.GetFormatAlternative(color_formats[color]);
vk::Format depth_stencil_format = instance.GetFormatAlternative(depth_stencil_formats[depth]); 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 // Construct both load and clear pass
cached_renderpasses[color][depth][0] = CreateRenderPass(color_format, depth_stencil_format, cached_renderpasses[color][depth][0] = CreateRenderPass(color_format, depth_stencil_format,
vk::AttachmentLoadOp::eLoad, vk::AttachmentLoadOp::eLoad,
vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::eColorAttachmentOptimal); vk::ImageLayout::eColorAttachmentOptimal);
cached_renderpasses[color][depth][1] = CreateRenderPass(color_format, depth_stencil_format, cached_renderpasses[color][depth][1] = CreateRenderPass(color_format, depth_stencil_format,
vk::AttachmentLoadOp::eClear, vk::AttachmentLoadOp::eClear,
vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eColorAttachmentOptimal,
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 { vk::RenderPass RenderpassCache::GetRenderpass(TextureFormat color, TextureFormat depth, bool is_clear) const {
const u32 color_index = static_cast<u32>(color); 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); ASSERT(color_index < MAX_COLOR_FORMATS && depth_index < MAX_DEPTH_FORMATS);
return cached_renderpasses[color_index][depth_index][is_clear]; 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, .format = depth,
.loadOp = load_op, .loadOp = load_op,
.storeOp = vk::AttachmentStoreOp::eStore, .storeOp = vk::AttachmentStoreOp::eStore,
.initialLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .stencilLoadOp = vk::AttachmentLoadOp::eLoad,
.stencilStoreOp = vk::AttachmentStoreOp::eStore,
.initialLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal,
.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal .finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@
#define VULKAN_HPP_NO_CONSTRUCTORS #define VULKAN_HPP_NO_CONSTRUCTORS
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.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/pica_to_vulkan.h"
#include "video_core/renderer_vulkan/vk_buffer.h" #include "video_core/renderer_vulkan/vk_buffer.h"
#include "video_core/renderer_vulkan/vk_texture.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) : Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
instance(instance), scheduler(scheduler) {} const TextureInfo& info) : TextureBase(info), instance(instance), scheduler(scheduler),
pool_manager(pool_manager) {
Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info) : TextureBase(info),
instance(instance), scheduler(scheduler) {
// Convert the input format to another that supports attachments // Convert the input format to another that supports attachments
advertised_format = ToVkFormat(info.format); advertised_format = ToVkFormat(info.format);
@@ -118,7 +117,7 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureI
.image = image, .image = image,
.viewType = ToVkImageViewType(info.view_type), .viewType = ToVkImageViewType(info.view_type),
.format = internal_format, .format = internal_format,
.subresourceRange = {aspect, 0, info.levels, 0, 1} .subresourceRange = {aspect, 0, 1, 0, 1} // Should this include the levels?
}; };
// Create image view // Create image view
@@ -130,9 +129,9 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureI
Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal, 0, info.levels); Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal, 0, info.levels);
} }
Texture::Texture(Instance& instance, CommandScheduler& scheduler, vk::Image image, vk::Format format, Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
const TextureInfo& info) : TextureBase(info), instance(instance), vk::Image image, vk::Format format, const TextureInfo& info) : TextureBase(info),
scheduler(scheduler), image(image), is_texture_owned(false) { instance(instance), scheduler(scheduler), pool_manager(pool_manager), image(image), is_texture_owned(false) {
advertised_format = internal_format = format; advertised_format = internal_format = format;
aspect = vk::ImageAspectFlagBits::eColor; 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) { void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, u32 level, u32 level_count) {
ASSERT(level + level_count < TEXTURE_MAX_LEVELS); 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, void Texture::BlitTo(TextureHandle dest, Common::Rectangle<u32> source_rect, Common::Rectangle<u32> dest_rect,
u32 src_layer, u32 dest_layer) { u32 src_level, u32 dest_level, u32 src_layer, u32 dest_layer) {
Texture* dest_texture = static_cast<Texture*>(dest.Get()); 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); dest_texture->Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal);
const std::array source_offsets = { const std::array source_offsets = {
vk::Offset3D{source_rect.x, source_rect.y, 0}, vk::Offset3D{static_cast<s32>(source_rect.left), static_cast<s32>(source_rect.bottom), 0},
vk::Offset3D{static_cast<s32>(source_rect.x + source_rect.width), vk::Offset3D{static_cast<s32>(source_rect.right), static_cast<s32>(source_rect.top), 1}
static_cast<s32>(source_rect.y + source_rect.height), 1}
}; };
const std::array dest_offsets = { const std::array dest_offsets = {
vk::Offset3D{dest_rect.x, dest_rect.y, 0}, vk::Offset3D{static_cast<s32>(dest_rect.left), static_cast<s32>(dest_rect.bottom), 0},
vk::Offset3D{static_cast<s32>(dest_rect.x + dest_rect.width), vk::Offset3D{static_cast<s32>(dest_rect.right), static_cast<s32>(dest_rect.top), 1}
static_cast<s32>(dest_rect.y + dest_rect.height), 1}
}; };
const vk::ImageBlit blit_area = { const vk::ImageBlit blit_area = {
@@ -665,8 +666,8 @@ void StagingTexture::Commit(u32 size) {
vmaFlushAllocation(allocator, allocation, 0, size); vmaFlushAllocation(allocator, allocation, 0, size);
} }
Sampler::Sampler(Instance& instance, SamplerInfo info) : Sampler::Sampler(Instance& instance, PoolManager& pool_manager, SamplerInfo info) :
SamplerBase(info), instance(instance) { SamplerBase(info), instance(instance), pool_manager(pool_manager) {
auto properties = instance.GetPhysicalDevice().getProperties(); auto properties = instance.GetPhysicalDevice().getProperties();
const auto filtering = PicaToVK::TextureFilterMode(info.mag_filter, const auto filtering = PicaToVK::TextureFilterMode(info.mag_filter,
@@ -695,4 +696,8 @@ Sampler::~Sampler() {
device.destroySampler(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/common/texture.h"
#include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class PoolManager;
}
namespace VideoCore::Vulkan { namespace VideoCore::Vulkan {
// PICA texture have at most 8 mipmap levels // PICA texture have at most 8 mipmap levels
constexpr u32 TEXTURE_MAX_LEVELS = 10; constexpr u32 TEXTURE_MAX_LEVELS = 12;
class Instance; class Instance;
class CommandScheduler; class CommandScheduler;
@@ -20,24 +24,24 @@ class CommandScheduler;
*/ */
class Texture : public VideoCore::TextureBase { class Texture : public VideoCore::TextureBase {
public: public:
// Default constructor
Texture(Instance& instance, CommandScheduler& scheduler);
// Constructor for texture creation // Constructor for texture creation
Texture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info); Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
// Constructor for swapchain images
Texture(Instance& instance, CommandScheduler& scheduler, vk::Image image, vk::Format format,
const TextureInfo& info); 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; ~Texture() override;
void Free() override;
void Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32 level = 0) 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 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, void BlitTo(TextureHandle dest, Common::Rectangle<u32> src_rectangle, Common::Rectangle<u32> dest_rect,
u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override; u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override;
void CopyFrom(TextureHandle source) override; void CopyFrom(TextureHandle source) override;
@@ -83,6 +87,7 @@ public:
private: private:
Instance& instance; Instance& instance;
CommandScheduler& scheduler; CommandScheduler& scheduler;
PoolManager& pool_manager;
// Vulkan texture handle // Vulkan texture handle
vk::Image image = VK_NULL_HANDLE; vk::Image image = VK_NULL_HANDLE;
@@ -103,19 +108,20 @@ private:
*/ */
class StagingTexture : public VideoCore::TextureBase { class StagingTexture : public VideoCore::TextureBase {
public: public:
StagingTexture(Instance& instance, CommandScheduler& scheduler, StagingTexture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info);
const TextureInfo& info);
~StagingTexture(); ~StagingTexture();
/// Flushes any writes made to texture memory void Free() override {}
// Flushes any writes made to texture memory
void Commit(u32 size); void Commit(u32 size);
/// Returns a span of the mapped texture memory // Returns a span of the mapped texture memory
void* GetMappedPtr() { void* GetMappedPtr() {
return mapped_ptr; return mapped_ptr;
} }
/// Returns the staging image handle // Returns the staging image handle
vk::Image GetHandle() const { vk::Image GetHandle() const {
return image; return image;
} }
@@ -136,16 +142,19 @@ private:
*/ */
class Sampler : public VideoCore::SamplerBase { class Sampler : public VideoCore::SamplerBase {
public: public:
Sampler(Instance& instance, SamplerInfo info); Sampler(Instance& instance, PoolManager& pool_manager, SamplerInfo info);
~Sampler() override; ~Sampler() override;
/// Returns the underlying vulkan sampler handle void Free() override;
// Returns the underlying vulkan sampler handle
vk::Sampler GetHandle() const { vk::Sampler GetHandle() const {
return sampler; return sampler;
} }
private: private:
Instance& instance; Instance& instance;
PoolManager& pool_manager;
vk::Sampler sampler; vk::Sampler sampler;
}; };