video_core: Fix remaining validation errors. Introduce PoolManager
This commit is contained in:
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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) {
|
||||||
|
35
src/video_core/common/pool_manager.h
Normal file
35
src/video_core/common/pool_manager.h
Normal 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
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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>
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
@@ -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);
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
@@ -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());
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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
|
||||||
};
|
};
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user