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/pipeline_cache.cpp
|
||||
common/pipeline_cache.h
|
||||
common/pool_manager.h
|
||||
common/rasterizer.cpp
|
||||
common/rasterizer.h
|
||||
common/rasterizer_cache.cpp
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/common/pool_manager.h"
|
||||
#include "video_core/common/pipeline.h"
|
||||
#include "video_core/common/framebuffer.h"
|
||||
|
||||
@@ -77,6 +78,7 @@ public:
|
||||
Common::Vec3<u32> groups) = 0;
|
||||
protected:
|
||||
Frontend::EmuWindow& window;
|
||||
PoolManager pool_manager;
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@@ -12,20 +12,20 @@
|
||||
namespace VideoCore {
|
||||
|
||||
enum class BufferUsage : u8 {
|
||||
Vertex = 0,
|
||||
Index = 1,
|
||||
Uniform = 2,
|
||||
Texel = 3,
|
||||
Staging = 4,
|
||||
Undefined = 255
|
||||
Undefined = 0,
|
||||
Vertex = 1,
|
||||
Index = 2,
|
||||
Uniform = 3,
|
||||
Texel = 4,
|
||||
Staging = 5
|
||||
};
|
||||
|
||||
enum class ViewFormat : u8 {
|
||||
R32Float = 0,
|
||||
R32G32Float = 1,
|
||||
R32G32B32Float = 2,
|
||||
R32G32B32A32Float = 3,
|
||||
Undefined = 255
|
||||
Undefined = 0,
|
||||
R32Float = 1,
|
||||
R32G32Float = 2,
|
||||
R32G32B32Float = 3,
|
||||
R32G32B32A32Float = 4,
|
||||
};
|
||||
|
||||
constexpr u32 MAX_BUFFER_VIEWS = 3;
|
||||
@@ -33,7 +33,7 @@ constexpr u32 MAX_BUFFER_VIEWS = 3;
|
||||
struct BufferInfo {
|
||||
u32 capacity = 0;
|
||||
BufferUsage usage = BufferUsage::Undefined;
|
||||
std::array<ViewFormat, MAX_BUFFER_VIEWS> views{ViewFormat::Undefined};
|
||||
std::array<ViewFormat, MAX_BUFFER_VIEWS> views{};
|
||||
|
||||
auto operator<=>(const BufferInfo& info) const = default;
|
||||
|
||||
@@ -45,12 +45,17 @@ struct BufferInfo {
|
||||
static_assert(sizeof(BufferInfo) == 8, "BufferInfo not packed!");
|
||||
static_assert(std::is_standard_layout_v<BufferInfo>, "BufferInfo is not a standard layout!");
|
||||
|
||||
class BufferBase : public IntrusivePtrEnabled<BufferBase> {
|
||||
struct BufferDeleter;
|
||||
|
||||
class BufferBase : public IntrusivePtrEnabled<BufferBase, BufferDeleter> {
|
||||
public:
|
||||
BufferBase() = default;
|
||||
BufferBase(const BufferInfo& info) : info(info) {}
|
||||
virtual ~BufferBase() = default;
|
||||
|
||||
// This method is called by BufferDeleter. Forward to the derived pool!
|
||||
virtual void Free() = 0;
|
||||
|
||||
// Disable copy constructor
|
||||
BufferBase(const BufferBase&) = delete;
|
||||
BufferBase& operator=(const BufferBase&) = delete;
|
||||
@@ -95,6 +100,13 @@ protected:
|
||||
bool invalid = false;
|
||||
};
|
||||
|
||||
// Foward pointer to its parent pool
|
||||
struct BufferDeleter {
|
||||
void operator()(BufferBase* buffer) {
|
||||
buffer->Free();
|
||||
}
|
||||
};
|
||||
|
||||
using BufferHandle = IntrusivePtr<BufferBase>;
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@@ -22,9 +22,7 @@ enum class LoadOp : u8 {
|
||||
Clear = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about a framebuffer
|
||||
*/
|
||||
// Information about a framebuffer
|
||||
struct FramebufferInfo {
|
||||
TextureHandle color;
|
||||
TextureHandle depth_stencil;
|
||||
@@ -39,12 +37,17 @@ struct FramebufferInfo {
|
||||
}
|
||||
};
|
||||
|
||||
struct FramebufferDeleter;
|
||||
|
||||
// A framebuffer is a collection of render targets and their configuration
|
||||
class FramebufferBase : public IntrusivePtrEnabled<FramebufferBase> {
|
||||
class FramebufferBase : public IntrusivePtrEnabled<FramebufferBase, FramebufferDeleter> {
|
||||
public:
|
||||
FramebufferBase(const FramebufferInfo& info) : info(info) {}
|
||||
virtual ~FramebufferBase() = default;
|
||||
|
||||
// This method is called by FramebufferDeleter. Forward to the derived pool!
|
||||
virtual void Free() = 0;
|
||||
|
||||
// Disable copy constructor
|
||||
FramebufferBase(const FramebufferBase&) = delete;
|
||||
FramebufferBase& operator=(const FramebufferBase&) = delete;
|
||||
@@ -100,6 +103,13 @@ protected:
|
||||
FramebufferInfo info;
|
||||
};
|
||||
|
||||
// Foward pointer to its parent pool
|
||||
struct FramebufferDeleter {
|
||||
void operator()(FramebufferBase* framebuffer) {
|
||||
framebuffer->Free();
|
||||
}
|
||||
};
|
||||
|
||||
using FramebufferHandle = IntrusivePtr<FramebufferBase>;
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@@ -140,12 +140,17 @@ struct PipelineInfo {
|
||||
|
||||
constexpr s32 WHOLE_SIZE = -1;
|
||||
|
||||
struct PipelineDeleter;
|
||||
|
||||
// An opaque handle to a backend specific program pipeline
|
||||
class PipelineBase : public IntrusivePtrEnabled<PipelineBase> {
|
||||
class PipelineBase : public IntrusivePtrEnabled<PipelineBase, PipelineDeleter> {
|
||||
public:
|
||||
PipelineBase(PipelineType type, PipelineInfo info) : type(type) {}
|
||||
virtual ~PipelineBase() = default;
|
||||
|
||||
// This method is called by PipelineDeleter. Forward to the derived pool!
|
||||
virtual void Free() = 0;
|
||||
|
||||
// Disable copy constructor
|
||||
PipelineBase(const PipelineBase&) = delete;
|
||||
PipelineBase& operator=(const PipelineBase&) = delete;
|
||||
@@ -181,6 +186,13 @@ protected:
|
||||
PipelineType type = PipelineType::Graphics;
|
||||
};
|
||||
|
||||
// Foward pointer to its parent pool
|
||||
struct PipelineDeleter {
|
||||
void operator()(PipelineBase* pipeline) {
|
||||
pipeline->Free();
|
||||
}
|
||||
};
|
||||
|
||||
using PipelineHandle = IntrusivePtr<PipelineBase>;
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@@ -44,6 +44,10 @@ PipelineCache::PipelineCache(Frontend::EmuWindow& emu_window, std::unique_ptr<Ba
|
||||
disk_cache(backend) {
|
||||
// TODO: Don't hardcode this!
|
||||
generator = std::make_unique<Vulkan::ShaderGenerator>();
|
||||
|
||||
trivial_vertex_shader = backend->CreateShader(ShaderStage::Vertex, "Trivial vertex shader",
|
||||
generator->GenerateTrivialVertexShader());
|
||||
trivial_vertex_shader->Compile(ShaderOptimization::Debug);
|
||||
}
|
||||
|
||||
PipelineHandle PipelineCache::GetPipeline(PipelineInfo& info) {
|
||||
|
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_lf = backend->CreateBuffer(TEXEL_BUFFER_LF_INFO);
|
||||
|
||||
const SamplerInfo cube_sampler_info = {
|
||||
.mag_filter = Pica::TextureFilter::Linear,
|
||||
.min_filter = Pica::TextureFilter::Linear,
|
||||
.mip_filter = Pica::TextureFilter::Linear,
|
||||
.wrap_s = Pica::WrapMode::ClampToEdge,
|
||||
.wrap_t = Pica::WrapMode::ClampToEdge
|
||||
};
|
||||
|
||||
// TODO: Texture cubes
|
||||
texture_cube_sampler = backend->CreateSampler(cube_sampler_info);
|
||||
|
||||
// TODO: Have the backend say this
|
||||
uniform_buffer_alignment = 64;
|
||||
uniform_size_aligned_vs = Common::AlignUp<std::size_t>(sizeof(VSUniformData), uniform_buffer_alignment);
|
||||
@@ -794,8 +805,9 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) {
|
||||
if (auto iter = sampler_cache.find(key); iter != sampler_cache.end()) {
|
||||
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, iter->second);
|
||||
} else {
|
||||
auto result = sampler_cache.emplace(key, backend->CreateSampler(key));
|
||||
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, result.first->second);
|
||||
SamplerHandle texture_sampler = backend->CreateSampler(key);
|
||||
auto result = sampler_cache.emplace(key, texture_sampler);
|
||||
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, texture_sampler);
|
||||
}
|
||||
|
||||
Surface surface = res_cache.GetTextureSurface(texture);
|
||||
@@ -815,9 +827,16 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) {
|
||||
} else {
|
||||
//state.texture_units[texture_index].texture_2d = 0;
|
||||
raster_pipeline->BindTexture(TEXTURE_GROUP, texture_index, clear_texture);
|
||||
raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, texture_cube_sampler);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement texture cubes
|
||||
raster_pipeline->BindTexture(TEXTURE_GROUP, 3, clear_texture);
|
||||
|
||||
// TODO: Implement texture cubes
|
||||
raster_pipeline->BindSampler(SAMPLER_GROUP, 3, texture_cube_sampler);
|
||||
|
||||
// Sync the LUTs within the texture buffer
|
||||
SyncAndUploadLUTs();
|
||||
SyncAndUploadLUTsLF();
|
||||
@@ -1921,7 +1940,9 @@ void Rasterizer::SyncAndUploadLUTsLF() {
|
||||
uniform_block_data.fog_lut_dirty = false;
|
||||
}
|
||||
|
||||
texel_buffer_lut_lf->Commit(bytes_used);
|
||||
if (bytes_used > 0) {
|
||||
texel_buffer_lut_lf->Commit(bytes_used);
|
||||
}
|
||||
}
|
||||
|
||||
void Rasterizer::SyncAndUploadLUTs() {
|
||||
@@ -2022,7 +2043,9 @@ void Rasterizer::SyncAndUploadLUTs() {
|
||||
uniform_block_data.proctex_diff_lut_dirty = false;
|
||||
}
|
||||
|
||||
texel_buffer_lut->Commit(bytes_used);
|
||||
if (bytes_used > 0) {
|
||||
texel_buffer_lut->Commit(bytes_used);
|
||||
}
|
||||
}
|
||||
|
||||
void Rasterizer::UploadUniforms(PipelineHandle pipeline, bool accelerate_draw) {
|
||||
@@ -2040,28 +2063,30 @@ void Rasterizer::UploadUniforms(PipelineHandle pipeline, bool accelerate_draw) {
|
||||
const bool invalidate = uniform_buffer->IsInvalid();
|
||||
const u32 offset = uniform_buffer->GetCurrentOffset();
|
||||
|
||||
// Re-bind uniform buffer with the new range
|
||||
pipeline->BindBuffer(UTILITY_GROUP, 0, uniform_buffer, offset + used_bytes, sizeof(VSUniformData));
|
||||
|
||||
if (sync_vs) {
|
||||
VSUniformData vs_uniforms;
|
||||
vs_uniforms.uniforms.SetFromRegs(Pica::g_state.regs.vs, Pica::g_state.vs);
|
||||
std::memcpy(uniforms.data() + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
|
||||
|
||||
// Re-bind uniform buffer with the new range
|
||||
pipeline->BindBuffer(UTILITY_GROUP, 0, uniform_buffer, offset + used_bytes, sizeof(VSUniformData));
|
||||
|
||||
used_bytes += uniform_size_aligned_vs;
|
||||
}
|
||||
|
||||
// Re-bind uniform buffer with the new range
|
||||
pipeline->BindBuffer(UTILITY_GROUP, 1, uniform_buffer, offset + used_bytes, sizeof(UniformData));
|
||||
|
||||
if (sync_fs || invalidate) {
|
||||
std::memcpy(uniforms.data() + used_bytes, &uniform_block_data.data, sizeof(UniformData));
|
||||
|
||||
// Re-bind uniform buffer with the new range
|
||||
pipeline->BindBuffer(UTILITY_GROUP, 1, uniform_buffer, offset + used_bytes, sizeof(UniformData));
|
||||
|
||||
uniform_block_data.dirty = false;
|
||||
used_bytes += uniform_size_aligned_fs;
|
||||
}
|
||||
|
||||
uniform_buffer->Commit(used_bytes);
|
||||
if (used_bytes > 0) {
|
||||
uniform_buffer->Commit(used_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@@ -236,7 +236,7 @@ private:
|
||||
std::array<Common::Vec4f, 256> proctex_diff_lut_data{};
|
||||
|
||||
// Texture unit sampler cache
|
||||
SamplerInfo texture_cube_sampler;
|
||||
SamplerHandle texture_cube_sampler;
|
||||
std::array<SamplerInfo, 3> texture_samplers;
|
||||
std::unordered_map<SamplerInfo, SamplerHandle> sampler_cache;
|
||||
|
||||
|
@@ -51,8 +51,7 @@ TextureFormat GetTextureFormat(PixelFormat pixel_format) {
|
||||
return depth_texture_formats[tuple_idx];
|
||||
}
|
||||
|
||||
LOG_ERROR(Render_Vulkan, "Unknown pixel format {}!", pixel_format);
|
||||
return TextureFormat::Undefined;
|
||||
return TextureFormat::RGBA8;
|
||||
}
|
||||
|
||||
template <typename Map, typename Interval>
|
||||
|
@@ -26,22 +26,27 @@ enum class ShaderOptimization : u32 {
|
||||
Debug = 1
|
||||
};
|
||||
|
||||
/// Compiles shader source to backend representation
|
||||
class ShaderBase : public IntrusivePtrEnabled<ShaderBase> {
|
||||
struct ShaderDeleter;
|
||||
|
||||
// Compiles shader source to backend representation
|
||||
class ShaderBase : public IntrusivePtrEnabled<ShaderBase, ShaderDeleter> {
|
||||
public:
|
||||
ShaderBase(ShaderStage stage, std::string_view name, std::string&& source) :
|
||||
name(name), stage(stage), source(source) {}
|
||||
virtual ~ShaderBase() = default;
|
||||
|
||||
/// Compiles the shader source code
|
||||
// This method is called by ShaderDeleter. Forward to the derived pool!
|
||||
virtual void Free() = 0;
|
||||
|
||||
// Compiles the shader source code
|
||||
virtual bool Compile(ShaderOptimization level) = 0;
|
||||
|
||||
/// Returns the name given the shader module
|
||||
// Returns the name given the shader module
|
||||
std::string_view GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Returns the pipeline stage the shader is assigned to
|
||||
// Returns the pipeline stage the shader is assigned to
|
||||
ShaderStage GetStage() const {
|
||||
return stage;
|
||||
}
|
||||
@@ -52,6 +57,13 @@ protected:
|
||||
std::string source = "";
|
||||
};
|
||||
|
||||
// Foward pointer to its parent pool
|
||||
struct ShaderDeleter {
|
||||
void operator()(ShaderBase* shader) {
|
||||
shader->Free();
|
||||
}
|
||||
};
|
||||
|
||||
using ShaderHandle = IntrusivePtr<ShaderBase>;
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@@ -16,34 +16,34 @@ constexpr u32 MAX_COLOR_FORMATS = 5;
|
||||
constexpr u32 MAX_DEPTH_FORMATS = 3;
|
||||
|
||||
enum class TextureFormat : u8 {
|
||||
RGBA8 = 0,
|
||||
RGB8 = 1,
|
||||
RGB5A1 = 2,
|
||||
RGB565 = 3,
|
||||
RGBA4 = 4,
|
||||
D16 = 5,
|
||||
D24 = 6,
|
||||
D24S8 = 7,
|
||||
PresentColor = 8, // Backend specific swapchain format
|
||||
Undefined = 255
|
||||
Undefined = 0,
|
||||
RGBA8 = 1,
|
||||
RGB8 = 2,
|
||||
RGB5A1 = 3,
|
||||
RGB565 = 4,
|
||||
RGBA4 = 5,
|
||||
D16 = 6,
|
||||
D24 = 7,
|
||||
D24S8 = 8,
|
||||
PresentColor = 9 // Backend specific swapchain format
|
||||
};
|
||||
|
||||
enum class TextureType : u8 {
|
||||
Texture1D = 0,
|
||||
Texture2D = 1,
|
||||
Texture3D = 2,
|
||||
Undefined = 255
|
||||
Undefined = 0,
|
||||
Texture1D = 1,
|
||||
Texture2D = 2,
|
||||
Texture3D = 3
|
||||
};
|
||||
|
||||
enum class TextureViewType : u8 {
|
||||
View1D = 0,
|
||||
View2D = 1,
|
||||
View3D = 2,
|
||||
ViewCube = 3,
|
||||
View1DArray = 4,
|
||||
View2DArray = 5,
|
||||
ViewCubeArray = 6,
|
||||
Undefined = 255
|
||||
Undefined = 0,
|
||||
View1D = 1,
|
||||
View2D = 2,
|
||||
View3D = 3,
|
||||
ViewCube = 4,
|
||||
View1DArray = 5,
|
||||
View2DArray = 6,
|
||||
ViewCubeArray = 7,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -64,9 +64,7 @@ struct Rect2D {
|
||||
u32 height = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about a texture packed to 8 bytes
|
||||
*/
|
||||
// Information about a texture packed to 8 bytes
|
||||
struct TextureInfo {
|
||||
u16 width = 0;
|
||||
u16 height = 0;
|
||||
@@ -92,12 +90,17 @@ static_assert(std::is_standard_layout_v<TextureInfo>, "TextureInfo is not a stan
|
||||
class TextureBase;
|
||||
using TextureHandle = IntrusivePtr<TextureBase>;
|
||||
|
||||
class TextureBase : public IntrusivePtrEnabled<TextureBase> {
|
||||
struct TextureDeleter;
|
||||
|
||||
class TextureBase : public IntrusivePtrEnabled<TextureBase, TextureDeleter> {
|
||||
public:
|
||||
TextureBase() = default;
|
||||
TextureBase(const TextureInfo& info) : info(info) {}
|
||||
virtual ~TextureBase() = default;
|
||||
|
||||
// This method is called by TextureDeleter. Forward to the derived pool!
|
||||
virtual void Free() = 0;
|
||||
|
||||
// Disable copy constructor
|
||||
TextureBase(const TextureBase&) = delete;
|
||||
TextureBase& operator=(const TextureBase&) = delete;
|
||||
@@ -111,9 +114,8 @@ public:
|
||||
u32 level = 0) {};
|
||||
|
||||
// Copies the rectangle area specified to the destionation texture
|
||||
virtual void BlitTo(TextureHandle dest, Rect2D source_rect, Rect2D dest_rect,
|
||||
u32 src_level = 0, u32 dest_level = 0,
|
||||
u32 src_layer = 0, u32 dest_layer = 0) {};
|
||||
virtual void BlitTo(TextureHandle dest, Common::Rectangle<u32> source_rect, Common::Rectangle<u32> dest_rect,
|
||||
u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) {};
|
||||
|
||||
// Copies texture data from the source texture
|
||||
virtual void CopyFrom(TextureHandle source) {};
|
||||
@@ -155,6 +157,14 @@ protected:
|
||||
TextureInfo info;
|
||||
};
|
||||
|
||||
// Foward pointer to its parent pool
|
||||
struct TextureDeleter {
|
||||
void operator()(TextureBase* texture) {
|
||||
texture->Free();
|
||||
}
|
||||
};
|
||||
|
||||
// Information about a sampler
|
||||
struct SamplerInfo {
|
||||
Pica::TextureFilter mag_filter;
|
||||
Pica::TextureFilter min_filter;
|
||||
@@ -173,11 +183,16 @@ struct SamplerInfo {
|
||||
}
|
||||
};
|
||||
|
||||
class SamplerBase : public IntrusivePtrEnabled<SamplerBase> {
|
||||
struct SamplerDeleter;
|
||||
|
||||
class SamplerBase : public IntrusivePtrEnabled<SamplerBase, SamplerDeleter> {
|
||||
public:
|
||||
SamplerBase(SamplerInfo info) : info(info) {}
|
||||
virtual ~SamplerBase() = default;
|
||||
|
||||
// This method is called by SamplerDeleter. Forward to the derived pool!
|
||||
virtual void Free() = 0;
|
||||
|
||||
// Disable copy constructor
|
||||
SamplerBase(const SamplerBase&) = delete;
|
||||
SamplerBase& operator=(const SamplerBase&) = delete;
|
||||
@@ -186,6 +201,13 @@ protected:
|
||||
SamplerInfo info{};
|
||||
};
|
||||
|
||||
// Foward pointer to its parent pool
|
||||
struct SamplerDeleter {
|
||||
void operator()(SamplerBase* sampler) {
|
||||
sampler->Free();
|
||||
}
|
||||
};
|
||||
|
||||
using SamplerHandle = IntrusivePtr<SamplerBase>;
|
||||
|
||||
} // namespace VideoCore
|
||||
|
@@ -3,9 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include <functional>
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "common/object_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_backend.h"
|
||||
#include "video_core/renderer_vulkan/vk_buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture.h"
|
||||
@@ -43,8 +43,8 @@ constexpr vk::IndexType ToVkIndexType(AttribType type) {
|
||||
}
|
||||
|
||||
Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
||||
instance(window), scheduler(instance), renderpass_cache(instance),
|
||||
swapchain(instance, scheduler, renderpass_cache, this, instance.GetSurface()) {
|
||||
instance(window), scheduler(instance, pool_manager), renderpass_cache(instance),
|
||||
swapchain(instance, scheduler, renderpass_cache, pool_manager, instance.GetSurface()) {
|
||||
|
||||
// TODO: Properly report GPU hardware
|
||||
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
|
||||
@@ -67,7 +67,7 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
||||
|
||||
const vk::DescriptorPoolCreateInfo pool_info = {
|
||||
.maxSets = 2048,
|
||||
.poolSizeCount = pool_sizes.size(),
|
||||
.poolSizeCount = static_cast<u32>(pool_sizes.size()),
|
||||
.pPoolSizes = pool_sizes.data()
|
||||
};
|
||||
|
||||
@@ -76,9 +76,8 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
||||
descriptor_pools[pool] = device.createDescriptorPool(pool_info);
|
||||
}
|
||||
|
||||
// Create swapchain with the initial window dimentions
|
||||
const auto& layout = window.GetFramebufferLayout();
|
||||
swapchain.Create(layout.width, layout.height, false);
|
||||
auto callback = std::bind(&Backend::OnCommandSwitch, this, std::placeholders::_1);
|
||||
scheduler.SetSwitchCallback(callback);
|
||||
}
|
||||
|
||||
Backend::~Backend() {
|
||||
@@ -90,13 +89,10 @@ Backend::~Backend() {
|
||||
}
|
||||
}
|
||||
|
||||
static bool first = true;
|
||||
|
||||
bool Backend::BeginPresent() {
|
||||
const auto& layout = window.GetFramebufferLayout();
|
||||
if (swapchain.NeedsRecreation() || first) {
|
||||
if (swapchain.NeedsRecreation()) {
|
||||
swapchain.Create(layout.width, layout.height, false);
|
||||
first = false;
|
||||
}
|
||||
|
||||
swapchain.AcquireNextImage();
|
||||
@@ -109,7 +105,11 @@ void Backend::EndPresent() {
|
||||
}
|
||||
|
||||
FramebufferHandle Backend::GetWindowFramebuffer() {
|
||||
return swapchain.GetCurrentFramebuffer();
|
||||
auto framebuffer = swapchain.GetCurrentFramebuffer();
|
||||
auto extent = swapchain.GetExtent();
|
||||
framebuffer->SetDrawRect({0, extent.height, extent.width, 0});
|
||||
|
||||
return framebuffer;
|
||||
}
|
||||
|
||||
u64 Backend::PipelineInfoHash(const PipelineInfo& info) {
|
||||
@@ -130,64 +130,58 @@ u64 Backend::PipelineInfoHash(const PipelineInfo& info) {
|
||||
* associated with it that batch-allocates memory.
|
||||
*/
|
||||
BufferHandle Backend::CreateBuffer(BufferInfo info) {
|
||||
static ObjectPool<Buffer> buffer_pool;
|
||||
return BufferHandle{buffer_pool.Allocate(instance, scheduler, info)};
|
||||
return pool_manager.Allocate<Buffer>(instance, scheduler, pool_manager, info);
|
||||
}
|
||||
|
||||
FramebufferHandle Backend::CreateFramebuffer(FramebufferInfo info) {
|
||||
static ObjectPool<Framebuffer> framebuffer_pool;
|
||||
|
||||
// Get renderpass
|
||||
TextureFormat color = info.color.IsValid() ? info.color->GetFormat() : TextureFormat::Undefined;
|
||||
TextureFormat depth = info.depth_stencil.IsValid() ? info.depth_stencil->GetFormat() : TextureFormat::Undefined;
|
||||
vk::RenderPass load_renderpass = GetRenderPass(color, depth, false);
|
||||
vk::RenderPass clear_renderpass = GetRenderPass(color, depth, true);
|
||||
|
||||
return FramebufferHandle{framebuffer_pool.Allocate(instance, scheduler, info, load_renderpass, clear_renderpass)};
|
||||
return pool_manager.Allocate<Framebuffer>(instance, scheduler, pool_manager, info,
|
||||
load_renderpass, clear_renderpass);
|
||||
}
|
||||
|
||||
TextureHandle Backend::CreateTexture(TextureInfo info) {
|
||||
static ObjectPool<Texture> texture_pool;
|
||||
return TextureHandle{texture_pool.Allocate(instance, scheduler, info)};
|
||||
return pool_manager.Allocate<Texture>(instance, scheduler, pool_manager, info);
|
||||
}
|
||||
|
||||
PipelineHandle Backend::CreatePipeline(PipelineType type, PipelineInfo info) {
|
||||
static ObjectPool<Pipeline> pipeline_pool;
|
||||
|
||||
// Get renderpass
|
||||
vk::RenderPass renderpass = GetRenderPass(info.color_attachment, info.depth_attachment);
|
||||
|
||||
// Find an owner first
|
||||
const u64 layout_hash = Common::ComputeHash64(&info.layout, sizeof(PipelineLayoutInfo));
|
||||
if (auto iter = pipeline_owners.find(layout_hash); iter != pipeline_owners.end()) {
|
||||
return PipelineHandle{pipeline_pool.Allocate(instance, scheduler, *iter->second.get(), type, info,
|
||||
renderpass, pipeline_cache)};
|
||||
return pool_manager.Allocate<Pipeline>(instance, scheduler, pool_manager, *iter->second.get(), type,
|
||||
info, renderpass, pipeline_cache);
|
||||
}
|
||||
|
||||
// Create the layout
|
||||
auto result = pipeline_owners.emplace(layout_hash, std::make_unique<PipelineOwner>(instance, info.layout));
|
||||
return PipelineHandle{pipeline_pool.Allocate(instance, scheduler, *result.first->second.get(), type, info,
|
||||
renderpass, pipeline_cache)};
|
||||
return pool_manager.Allocate<Pipeline>(instance, scheduler, pool_manager, *result.first->second.get(), type,
|
||||
info, renderpass, pipeline_cache);
|
||||
}
|
||||
|
||||
SamplerHandle Backend::CreateSampler(SamplerInfo info) {
|
||||
static ObjectPool<Sampler> sampler_pool;
|
||||
return SamplerHandle{sampler_pool.Allocate(instance, info)};
|
||||
return pool_manager.Allocate<Sampler>(instance, pool_manager, info);
|
||||
}
|
||||
|
||||
ShaderHandle Backend::CreateShader(ShaderStage stage, std::string_view name, std::string source) {
|
||||
static ObjectPool<Shader> shader_pool;
|
||||
return ShaderHandle{shader_pool.Allocate(instance, stage, name, std::move(source))};
|
||||
return pool_manager.Allocate<Shader>(instance, pool_manager, stage, name, std::move(source));
|
||||
}
|
||||
|
||||
void Backend::BindVertexBuffer(BufferHandle buffer, std::span<const u64> offsets) {
|
||||
const Buffer* vertex = static_cast<const Buffer*>(buffer.Get());
|
||||
|
||||
const u32 buffer_count = static_cast<u32>(offsets.size());
|
||||
std::array<vk::Buffer, 16> buffers;
|
||||
buffers.fill(vertex->GetHandle());
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.bindVertexBuffers(0, offsets.size(), buffers.data(), offsets.data());
|
||||
command_buffer.bindVertexBuffers(0, buffer_count, buffers.data(), offsets.data());
|
||||
}
|
||||
|
||||
void Backend::BindIndexBuffer(BufferHandle buffer, AttribType index_type, u64 offset) {
|
||||
@@ -253,7 +247,7 @@ void Backend::BindDescriptorSets(PipelineHandle handle) {
|
||||
|
||||
std::array<vk::DescriptorSet, MAX_BINDING_GROUPS> bound_sets;
|
||||
const u32 set_count = pipeline_owner.GetDescriptorSetLayoutCount();
|
||||
for (int i = 0; i < set_count; i++) {
|
||||
for (u32 i = 0; i < set_count; i++) {
|
||||
if (!pipeline_owner.descriptor_dirty[i]) {
|
||||
// Get the ready descriptor if it hasn't been modified
|
||||
bound_sets[i] = pipeline_owner.descriptor_bank[i];
|
||||
@@ -284,7 +278,7 @@ void Backend::BindDescriptorSets(PipelineHandle handle) {
|
||||
}
|
||||
|
||||
void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) {
|
||||
const Framebuffer* framebuffer = static_cast<const Framebuffer*>(draw_framebuffer.Get());
|
||||
Framebuffer* framebuffer = static_cast<Framebuffer*>(draw_framebuffer.Get());
|
||||
|
||||
u32 clear_value_count = 0;
|
||||
std::array<vk::ClearValue, 2> clear_values{};
|
||||
@@ -301,6 +295,9 @@ void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) {
|
||||
clear_values[clear_value_count++].depthStencil.stencil = framebuffer->clear_stencil_value;
|
||||
}
|
||||
|
||||
// Transition attachments to required layout
|
||||
framebuffer->PrepareAttachments();
|
||||
|
||||
// Use the clear renderpass if the framebuffer was configured so
|
||||
const vk::RenderPassBeginInfo renderpass_begin = {
|
||||
.renderPass = framebuffer->GetLoadOp() == LoadOp::Load ?
|
||||
@@ -316,4 +313,17 @@ void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) {
|
||||
command_buffer.beginRenderPass(renderpass_begin, vk::SubpassContents::eInline);
|
||||
}
|
||||
|
||||
void Backend::OnCommandSwitch(u32 new_slot) {
|
||||
// Reset the descriptor pool assigned to the new command slot. This is called
|
||||
// after Synchronize, so it's guaranteed that the descriptor sets are no longer
|
||||
// in use.
|
||||
vk::Device device = instance.GetDevice();
|
||||
device.resetDescriptorPool(descriptor_pools[new_slot]);
|
||||
|
||||
// Mark all descriptor sets as dirty
|
||||
for (auto& [key, owner] : pipeline_owners) {
|
||||
owner->descriptor_dirty.fill(true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCore::Vulkan
|
||||
|
@@ -66,6 +66,8 @@ private:
|
||||
// Begins the renderpass for the provided framebuffer
|
||||
void BeginRenderpass(FramebufferHandle framebuffer);
|
||||
|
||||
void OnCommandSwitch(u32 new_slot);
|
||||
|
||||
private:
|
||||
Instance instance;
|
||||
CommandScheduler scheduler;
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/common/pool_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
@@ -13,35 +14,45 @@
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
inline vk::BufferUsageFlags ToVkBufferUsage(BufferUsage usage) {
|
||||
constexpr std::array vk_buffer_usages = {
|
||||
vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
vk::BufferUsageFlagBits::eIndexBuffer,
|
||||
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||
vk::BufferUsageFlagBits::eUniformTexelBuffer,
|
||||
vk::BufferUsageFlagBits::eTransferSrc
|
||||
};
|
||||
|
||||
return vk::BufferUsageFlagBits::eTransferDst |
|
||||
vk_buffer_usages.at(static_cast<u32>(usage));
|
||||
switch (usage) {
|
||||
case BufferUsage::Vertex:
|
||||
return vk::BufferUsageFlagBits::eVertexBuffer;
|
||||
case BufferUsage::Index:
|
||||
return vk::BufferUsageFlagBits::eIndexBuffer;
|
||||
case BufferUsage::Uniform:
|
||||
return vk::BufferUsageFlagBits::eUniformBuffer;
|
||||
case BufferUsage::Texel:
|
||||
return vk::BufferUsageFlagBits::eUniformTexelBuffer;
|
||||
case BufferUsage::Staging:
|
||||
return vk::BufferUsageFlagBits::eTransferSrc;
|
||||
default:
|
||||
LOG_CRITICAL(Render_Vulkan, "Unknown buffer usage flag {}!", usage);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
inline vk::Format ToVkViewFormat(ViewFormat format) {
|
||||
constexpr std::array vk_view_formats = {
|
||||
vk::Format::eR32Sfloat,
|
||||
vk::Format::eR32G32Sfloat,
|
||||
vk::Format::eR32G32B32Sfloat,
|
||||
vk::Format::eR32G32B32A32Sfloat
|
||||
};
|
||||
|
||||
return vk_view_formats.at(static_cast<u32>(format));
|
||||
switch (format) {
|
||||
case ViewFormat::R32Float:
|
||||
return vk::Format::eR32Sfloat;
|
||||
case ViewFormat::R32G32Float:
|
||||
return vk::Format::eR32G32Sfloat;
|
||||
case ViewFormat::R32G32B32Float:
|
||||
return vk::Format::eR32G32B32Sfloat;
|
||||
case ViewFormat::R32G32B32A32Float:
|
||||
return vk::Format::eR32G32B32A32Sfloat;
|
||||
default:
|
||||
LOG_CRITICAL(Render_Vulkan, "Unknown buffer view format {}!", format);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo& info) :
|
||||
BufferBase(info), instance(instance), scheduler(scheduler) {
|
||||
Buffer::Buffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, const BufferInfo& info)
|
||||
: BufferBase(info), instance(instance), scheduler(scheduler), pool_manager(pool_manager) {
|
||||
|
||||
vk::BufferCreateInfo buffer_info = {
|
||||
.size = info.capacity,
|
||||
.usage = ToVkBufferUsage(info.usage)
|
||||
.usage = ToVkBufferUsage(info.usage) | vk::BufferUsageFlagBits::eTransferDst
|
||||
};
|
||||
|
||||
VmaAllocationCreateInfo alloc_create_info = {
|
||||
@@ -65,6 +76,7 @@ Buffer::Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo
|
||||
vk::Device device = instance.GetDevice();
|
||||
for (u32 view = 0; view < info.views.size(); view++) {
|
||||
if (info.views[view] == ViewFormat::Undefined) {
|
||||
view_count = view;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -107,6 +119,10 @@ Buffer::~Buffer() {
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::Free() {
|
||||
pool_manager.Free<Buffer>(this);
|
||||
}
|
||||
|
||||
std::span<u8> Buffer::Map(u32 size, u32 alignment) {
|
||||
ASSERT(size <= info.capacity && alignment <= info.capacity);
|
||||
|
||||
|
@@ -9,6 +9,10 @@
|
||||
#include "video_core/common/buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class PoolManager;
|
||||
}
|
||||
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
class Instance;
|
||||
@@ -16,20 +20,21 @@ class CommandScheduler;
|
||||
|
||||
class Buffer : public VideoCore::BufferBase {
|
||||
public:
|
||||
Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo& info);
|
||||
Buffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
|
||||
const BufferInfo& info);
|
||||
~Buffer() override;
|
||||
|
||||
std::span<u8> Map(u32 size, u32 alignment = 0) override;
|
||||
void Free() override;
|
||||
|
||||
/// Flushes write to buffer memory
|
||||
std::span<u8> Map(u32 size, u32 alignment = 0) override;
|
||||
void Commit(u32 size = 0) override;
|
||||
|
||||
/// Returns the Vulkan buffer handle
|
||||
// Returns the Vulkan buffer handle
|
||||
vk::Buffer GetHandle() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the requested buffer view
|
||||
// Returns an immutable reference to the requested buffer view
|
||||
const vk::BufferView& GetView(u32 index = 0) const {
|
||||
ASSERT(index < view_count);
|
||||
return views[index];
|
||||
@@ -38,6 +43,7 @@ public:
|
||||
protected:
|
||||
Instance& instance;
|
||||
CommandScheduler& scheduler;
|
||||
PoolManager& pool_manager;
|
||||
|
||||
// Vulkan buffer handle
|
||||
void* mapped_ptr = nullptr;
|
||||
|
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include "video_core/common/pool_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_framebuffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
@@ -17,10 +18,10 @@ inline vk::Rect2D ToVkRect2D(Rect2D rect) {
|
||||
};
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(Instance& instance, CommandScheduler& scheduler, const FramebufferInfo& info,
|
||||
vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass) :
|
||||
FramebufferBase(info), instance(instance), scheduler(scheduler), load_renderpass(load_renderpass),
|
||||
clear_renderpass(clear_renderpass) {
|
||||
Framebuffer::Framebuffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
|
||||
const FramebufferInfo& info, vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass) :
|
||||
FramebufferBase(info), instance(instance), scheduler(scheduler), pool_manager(pool_manager),
|
||||
load_renderpass(load_renderpass), clear_renderpass(clear_renderpass) {
|
||||
|
||||
const Texture* color = static_cast<const Texture*>(info.color.Get());
|
||||
const Texture* depth_stencil = static_cast<const Texture*>(info.depth_stencil.Get());
|
||||
@@ -57,9 +58,11 @@ Framebuffer::~Framebuffer() {
|
||||
device.destroyFramebuffer(framebuffer);
|
||||
}
|
||||
|
||||
void Framebuffer::DoClear() {
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
void Framebuffer::Free() {
|
||||
pool_manager.Free<Framebuffer>(this);
|
||||
}
|
||||
|
||||
void Framebuffer::DoClear() {
|
||||
u32 clear_value_count = 0;
|
||||
std::array<vk::ClearValue, 2> clear_values{};
|
||||
|
||||
@@ -81,6 +84,9 @@ void Framebuffer::DoClear() {
|
||||
};
|
||||
}
|
||||
|
||||
// Prepare attachments
|
||||
PrepareAttachments();
|
||||
|
||||
const vk::RenderPassBeginInfo begin_info = {
|
||||
.renderPass = clear_renderpass,
|
||||
.framebuffer = framebuffer,
|
||||
@@ -90,8 +96,22 @@ void Framebuffer::DoClear() {
|
||||
};
|
||||
|
||||
// Begin clear pass
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.beginRenderPass(begin_info, vk::SubpassContents::eInline);
|
||||
command_buffer.endRenderPass();
|
||||
}
|
||||
|
||||
void Framebuffer::PrepareAttachments() {
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
if (info.color.IsValid()) {
|
||||
Texture* color = static_cast<Texture*>(info.color.Get());
|
||||
color->Transition(command_buffer, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
}
|
||||
|
||||
if (info.depth_stencil.IsValid()) {
|
||||
Texture* depth_stencil = static_cast<Texture*>(info.depth_stencil.Get());
|
||||
depth_stencil->Transition(command_buffer, vk::ImageLayout::eDepthAttachmentOptimal);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCore::Vulkan
|
||||
|
@@ -7,6 +7,10 @@
|
||||
#include "video_core/common/framebuffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class PoolManager;
|
||||
}
|
||||
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
class Instance;
|
||||
@@ -15,37 +19,38 @@ class CommandScheduler;
|
||||
class Framebuffer : public VideoCore::FramebufferBase {
|
||||
friend class Backend;
|
||||
public:
|
||||
Framebuffer(Instance& instance, CommandScheduler& scheduler, const FramebufferInfo& info,
|
||||
vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass);
|
||||
Framebuffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
|
||||
const FramebufferInfo& info, vk::RenderPass load_renderpass,
|
||||
vk::RenderPass clear_renderpass);
|
||||
~Framebuffer() override;
|
||||
|
||||
void Free() override;
|
||||
void DoClear() override;
|
||||
|
||||
// Transitions the attachments to the required layout
|
||||
void PrepareAttachments();
|
||||
|
||||
// Returns the vulkan framebuffer handle
|
||||
vk::Framebuffer GetHandle() const {
|
||||
return framebuffer;
|
||||
}
|
||||
|
||||
// Returns the renderpass with VK_LOAD_OP_LOAD
|
||||
vk::RenderPass GetLoadRenderpass() const {
|
||||
return load_renderpass;
|
||||
}
|
||||
|
||||
// Returns the renderpass with VK_LOAD_OP_CLEAR (used for optimized GPU clear)
|
||||
vk::RenderPass GetClearRenderpass() const {
|
||||
return clear_renderpass;
|
||||
}
|
||||
|
||||
u32 GetAttachmentCount() const {
|
||||
if (info.color.IsValid() && info.depth_stencil.IsValid()) {
|
||||
return 2;
|
||||
} else if (!info.color.IsValid() && !info.depth_stencil.IsValid()) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Instance& instance;
|
||||
CommandScheduler& scheduler;
|
||||
PoolManager& pool_manager;
|
||||
|
||||
// Vulkan framebuffer
|
||||
vk::Framebuffer framebuffer;
|
||||
vk::RenderPass load_renderpass, clear_renderpass;
|
||||
};
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/common/pool_manager.h"
|
||||
#include "video_core/renderer_vulkan/pica_to_vulkan.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader.h"
|
||||
@@ -186,10 +187,9 @@ PipelineOwner::~PipelineOwner() {
|
||||
}
|
||||
}
|
||||
|
||||
Pipeline::Pipeline(Instance& instance, CommandScheduler& scheduler, PipelineOwner& owner,
|
||||
PipelineType type, PipelineInfo info,
|
||||
vk::RenderPass renderpass, vk::PipelineCache cache) : PipelineBase(type, info),
|
||||
instance(instance), scheduler(scheduler), owner(owner) {
|
||||
Pipeline::Pipeline(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, PipelineOwner& owner,
|
||||
PipelineType type, PipelineInfo info, vk::RenderPass renderpass, vk::PipelineCache cache) :
|
||||
PipelineBase(type, info), instance(instance), scheduler(scheduler), pool_manager(pool_manager), owner(owner) {
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
|
||||
@@ -382,6 +382,9 @@ Pipeline::~Pipeline() {
|
||||
device.destroyPipeline(pipeline);
|
||||
}
|
||||
|
||||
void Pipeline::Free() {
|
||||
pool_manager.Free<Pipeline>(this);
|
||||
}
|
||||
|
||||
void Pipeline::BindTexture(u32 group, u32 slot, TextureHandle handle) {
|
||||
Texture* texture = static_cast<Texture*>(handle.Get());
|
||||
|
@@ -8,6 +8,10 @@
|
||||
#include "video_core/common/pipeline.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class PoolManager;
|
||||
}
|
||||
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
class Instance;
|
||||
@@ -82,11 +86,12 @@ private:
|
||||
|
||||
class Pipeline : public VideoCore::PipelineBase {
|
||||
public:
|
||||
Pipeline(Instance& instance, CommandScheduler& scheduler, PipelineOwner& owner,
|
||||
PipelineType type, PipelineInfo info,
|
||||
vk::RenderPass renderpass, vk::PipelineCache cache);
|
||||
Pipeline(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, PipelineOwner& owner,
|
||||
PipelineType type, PipelineInfo info, vk::RenderPass renderpass, vk::PipelineCache cache);
|
||||
~Pipeline() override;
|
||||
|
||||
void Free() override;
|
||||
|
||||
void BindTexture(u32 group, u32 slot, TextureHandle handle) override;
|
||||
void BindBuffer(u32 group, u32 slot, BufferHandle handle,
|
||||
u32 offset = 0, u32 range = WHOLE_SIZE, u32 view = 0) override;
|
||||
@@ -111,6 +116,9 @@ public:
|
||||
private:
|
||||
Instance& instance;
|
||||
CommandScheduler& scheduler;
|
||||
PoolManager& pool_manager;
|
||||
|
||||
// The owner of this pipeline
|
||||
PipelineOwner& owner;
|
||||
vk::Pipeline pipeline;
|
||||
};
|
||||
|
@@ -37,18 +37,14 @@ RenderpassCache::RenderpassCache(Instance& instance) : instance(instance) {
|
||||
vk::Format color_format = instance.GetFormatAlternative(color_formats[color]);
|
||||
vk::Format depth_stencil_format = instance.GetFormatAlternative(depth_stencil_formats[depth]);
|
||||
|
||||
if (color_format == vk::Format::eA8B8G8R8SintPack32) {
|
||||
LOG_INFO(Render_Vulkan, "");
|
||||
}
|
||||
|
||||
// Construct both load and clear pass
|
||||
cached_renderpasses[color][depth][0] = CreateRenderPass(color_format, depth_stencil_format,
|
||||
vk::AttachmentLoadOp::eLoad,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
vk::ImageLayout::eColorAttachmentOptimal);
|
||||
cached_renderpasses[color][depth][1] = CreateRenderPass(color_format, depth_stencil_format,
|
||||
vk::AttachmentLoadOp::eClear,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
vk::ImageLayout::eColorAttachmentOptimal);
|
||||
}
|
||||
}
|
||||
@@ -85,7 +81,7 @@ void RenderpassCache::CreatePresentRenderpass(vk::Format format) {
|
||||
|
||||
vk::RenderPass RenderpassCache::GetRenderpass(TextureFormat color, TextureFormat depth, bool is_clear) const {
|
||||
const u32 color_index = static_cast<u32>(color);
|
||||
const u32 depth_index = static_cast<u32>(depth) - MAX_COLOR_FORMATS;
|
||||
const u32 depth_index = (depth == TextureFormat::Undefined ? 0 : (static_cast<u32>(depth) - MAX_COLOR_FORMATS));
|
||||
|
||||
ASSERT(color_index < MAX_COLOR_FORMATS && depth_index < MAX_DEPTH_FORMATS);
|
||||
return cached_renderpasses[color_index][depth_index][is_clear];
|
||||
@@ -127,7 +123,9 @@ vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format de
|
||||
.format = depth,
|
||||
.loadOp = load_op,
|
||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||
.initialLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
.stencilLoadOp = vk::AttachmentLoadOp::eLoad,
|
||||
.stencilStoreOp = vk::AttachmentStoreOp::eStore,
|
||||
.initialLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||
.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal
|
||||
};
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/common/pool_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include <glslang/Public/ShaderLang.h>
|
||||
@@ -154,9 +155,9 @@ bool InitializeCompiler() {
|
||||
return true;
|
||||
}
|
||||
|
||||
Shader::Shader(Instance& instance, ShaderStage stage, std::string_view name,
|
||||
std::string&& source) :
|
||||
ShaderBase(stage, name, std::move(source)), instance(instance) {
|
||||
Shader::Shader(Instance& instance, PoolManager& pool_manager, ShaderStage stage, std::string_view name,
|
||||
std::string&& source) : ShaderBase(stage, name, std::move(source)),
|
||||
instance(instance), pool_manager(pool_manager) {
|
||||
}
|
||||
|
||||
Shader::~Shader() {
|
||||
@@ -164,6 +165,10 @@ Shader::~Shader() {
|
||||
device.destroyShaderModule(module);
|
||||
}
|
||||
|
||||
void Shader::Free() {
|
||||
pool_manager.Free<Shader>(this);
|
||||
}
|
||||
|
||||
bool Shader::Compile(ShaderOptimization level) {
|
||||
if (!InitializeCompiler()) {
|
||||
return false;
|
||||
|
@@ -7,16 +7,22 @@
|
||||
#include "video_core/common/shader.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class PoolManager;
|
||||
}
|
||||
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
class Instance;
|
||||
|
||||
class Shader : public VideoCore::ShaderBase {
|
||||
public:
|
||||
Shader(Instance& instance, ShaderStage stage, std::string_view name,
|
||||
Shader(Instance& instance, PoolManager& pool_manager, ShaderStage stage, std::string_view name,
|
||||
std::string&& source);
|
||||
~Shader() override;
|
||||
|
||||
void Free() override;
|
||||
|
||||
bool Compile(ShaderOptimization level) override;
|
||||
|
||||
/// Returns the underlying vulkan shader module handle
|
||||
@@ -26,6 +32,7 @@ public:
|
||||
|
||||
private:
|
||||
Instance& instance;
|
||||
PoolManager& pool_manager;
|
||||
vk::ShaderModule module;
|
||||
};
|
||||
|
||||
|
@@ -3,22 +3,25 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include <array>
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/common/pool_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_backend.h"
|
||||
#include "video_core/renderer_vulkan/vk_framebuffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture.h"
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
Swapchain::Swapchain(Instance& instance, CommandScheduler& scheduler, RenderpassCache& renderpass_cache,
|
||||
Backend* backend, vk::SurfaceKHR surface) :
|
||||
backend(backend), instance(instance), scheduler(scheduler),
|
||||
renderpass_cache(renderpass_cache), surface(surface) {
|
||||
PoolManager& pool_manager, vk::SurfaceKHR surface) : instance(instance), scheduler(scheduler),
|
||||
renderpass_cache(renderpass_cache), pool_manager(pool_manager), surface(surface) {
|
||||
|
||||
// Set the surface format early for RenderpassCache to create the present renderpass
|
||||
Configure(0, 0);
|
||||
|
||||
// Create the present renderpass
|
||||
renderpass_cache.CreatePresentRenderpass(surface_format.format);
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain() {
|
||||
@@ -42,7 +45,7 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) {
|
||||
};
|
||||
|
||||
const bool exclusive = queue_family_indices[0] == queue_family_indices[1];
|
||||
const u32 queue_family_indices_count = exclusive ? 2u : 1u;
|
||||
const u32 queue_family_indices_count = exclusive ? 1u : 2u;
|
||||
const vk::SharingMode sharing_mode = exclusive ? vk::SharingMode::eExclusive :
|
||||
vk::SharingMode::eConcurrent;
|
||||
|
||||
@@ -81,9 +84,6 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) {
|
||||
render_finished = device.createSemaphore({});
|
||||
}
|
||||
|
||||
// Create the present renderpass
|
||||
renderpass_cache.CreatePresentRenderpass(surface_format.format);
|
||||
|
||||
// Create framebuffer and image views
|
||||
vk_images = device.getSwapchainImagesKHR(swapchain);
|
||||
|
||||
@@ -102,16 +102,16 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) {
|
||||
framebuffers.clear();
|
||||
framebuffers.resize(vk_images.size());
|
||||
for (int i = 0; i < vk_images.size(); i++) {
|
||||
textures[i] = MakeHandle<Texture>(instance, scheduler, vk_images[i],
|
||||
surface_format.format, image_info);
|
||||
textures[i] = pool_manager.Allocate<Texture>(instance, scheduler, pool_manager, vk_images[i],
|
||||
surface_format.format, image_info);
|
||||
|
||||
const FramebufferInfo framebuffer_info = {
|
||||
.color = textures[i]
|
||||
};
|
||||
|
||||
vk::RenderPass renderpass = renderpass_cache.GetPresentRenderpass();
|
||||
framebuffers[i] = MakeHandle<Framebuffer>(instance, scheduler, framebuffer_info,
|
||||
renderpass, renderpass);
|
||||
framebuffers[i] = pool_manager.Allocate<Framebuffer>(instance, scheduler, pool_manager, framebuffer_info,
|
||||
renderpass, renderpass);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,10 @@
|
||||
#include "video_core/common/framebuffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class PoolManager;
|
||||
}
|
||||
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
class Instance;
|
||||
@@ -20,59 +24,59 @@ class RenderpassCache;
|
||||
class Swapchain {
|
||||
public:
|
||||
Swapchain(Instance& instance, CommandScheduler& scheduler, RenderpassCache& renderpass_cache,
|
||||
Backend* backend, vk::SurfaceKHR surface);
|
||||
PoolManager& pool_manager, vk::SurfaceKHR surface);
|
||||
~Swapchain();
|
||||
|
||||
/// Creates (or recreates) the swapchain with a given size.
|
||||
// Creates (or recreates) the swapchain with a given size.
|
||||
void Create(u32 width, u32 height, bool vsync_enabled);
|
||||
|
||||
/// Acquire the next image in the swapchain.
|
||||
// Acquires the next image in the swapchain.
|
||||
void AcquireNextImage();
|
||||
|
||||
/// Present the current image and move to the next one
|
||||
// Presents the current image and move to the next one
|
||||
void Present();
|
||||
|
||||
FramebufferHandle GetCurrentFramebuffer() const {
|
||||
return framebuffers[current_image];
|
||||
}
|
||||
|
||||
/// Return current swapchain state
|
||||
inline vk::Extent2D GetExtent() const {
|
||||
// Returns current swapchain state
|
||||
vk::Extent2D GetExtent() const {
|
||||
return extent;
|
||||
}
|
||||
|
||||
/// Return the swapchain surface
|
||||
inline vk::SurfaceKHR GetSurface() const {
|
||||
// Returns the swapchain surface
|
||||
vk::SurfaceKHR GetSurface() const {
|
||||
return surface;
|
||||
}
|
||||
|
||||
/// Return the swapchain format
|
||||
inline vk::SurfaceFormatKHR GetSurfaceFormat() const {
|
||||
// Returns the swapchain format
|
||||
vk::SurfaceFormatKHR GetSurfaceFormat() const {
|
||||
return surface_format;
|
||||
}
|
||||
|
||||
/// Return the Vulkan swapchain handle
|
||||
inline vk::SwapchainKHR GetHandle() const {
|
||||
// Returns the Vulkan swapchain handle
|
||||
vk::SwapchainKHR GetHandle() const {
|
||||
return swapchain;
|
||||
}
|
||||
|
||||
/// Return the semaphore that will be signaled when vkAcquireNextImageKHR completes
|
||||
inline vk::Semaphore GetAvailableSemaphore() const {
|
||||
// Returns the semaphore that will be signaled when vkAcquireNextImageKHR completes
|
||||
vk::Semaphore GetAvailableSemaphore() const {
|
||||
return image_available;
|
||||
}
|
||||
|
||||
/// Return the semaphore that will signal when the current image will be presented
|
||||
inline vk::Semaphore GetPresentSemaphore() const {
|
||||
// Returns the semaphore that will signal when the current image will be presented
|
||||
vk::Semaphore GetPresentSemaphore() const {
|
||||
return render_finished;
|
||||
}
|
||||
|
||||
/// Return the current swapchain image
|
||||
inline vk::Image GetCurrentImage() {
|
||||
// Returns the current swapchain image
|
||||
vk::Image GetCurrentImage() {
|
||||
return vk_images[current_image];
|
||||
}
|
||||
|
||||
/// Returns true when the swapchain should be recreated
|
||||
inline bool NeedsRecreation() const {
|
||||
// Returns true when the swapchain should be recreated
|
||||
bool NeedsRecreation() const {
|
||||
return is_suboptimal || is_outdated;
|
||||
}
|
||||
|
||||
@@ -80,10 +84,10 @@ private:
|
||||
void Configure(u32 width, u32 height);
|
||||
|
||||
private:
|
||||
Backend* backend = nullptr;
|
||||
Instance& instance;
|
||||
CommandScheduler& scheduler;
|
||||
RenderpassCache& renderpass_cache;
|
||||
PoolManager& pool_manager;
|
||||
vk::SwapchainKHR swapchain = VK_NULL_HANDLE;
|
||||
vk::SurfaceKHR surface = VK_NULL_HANDLE;
|
||||
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/common/pool_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_buffer.h"
|
||||
@@ -16,7 +17,9 @@ constexpr BufferInfo STAGING_INFO = {
|
||||
.usage = BufferUsage::Staging
|
||||
};
|
||||
|
||||
CommandScheduler::CommandScheduler(Instance& instance) : instance(instance) {
|
||||
CommandScheduler::CommandScheduler(Instance& instance, PoolManager& pool_manager) : instance(instance),
|
||||
pool_manager(pool_manager) {
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
const vk::CommandPoolCreateInfo pool_info = {
|
||||
.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
|
||||
@@ -43,7 +46,7 @@ CommandScheduler::CommandScheduler(Instance& instance) : instance(instance) {
|
||||
.upload_command_buffer = command_buffers[2 * i + 1],
|
||||
};
|
||||
|
||||
commands[i].upload_buffer = std::make_unique<Buffer>(instance, *this, STAGING_INFO);
|
||||
commands[i].upload_buffer = pool_manager.Allocate<Buffer>(instance, *this, pool_manager, STAGING_INFO);
|
||||
}
|
||||
|
||||
const vk::CommandBufferBeginInfo begin_info = {
|
||||
@@ -103,9 +106,11 @@ void CommandScheduler::Synchronize() {
|
||||
completed_fence_counter = now_fence_counter;
|
||||
}
|
||||
|
||||
void CommandScheduler::Submit(bool wait_completion,
|
||||
vk::Semaphore wait_semaphore,
|
||||
vk::Semaphore signal_semaphore) {
|
||||
void CommandScheduler::SetSwitchCallback(std::function<void(u32)> callback) {
|
||||
switch_callback = callback;
|
||||
}
|
||||
|
||||
void CommandScheduler::Submit(bool wait_completion, vk::Semaphore wait_semaphore, vk::Semaphore signal_semaphore) {
|
||||
const CommandSlot& command = commands[current_command];
|
||||
|
||||
// End command buffers
|
||||
@@ -149,8 +154,8 @@ void CommandScheduler::Submit(bool wait_completion,
|
||||
SwitchSlot();
|
||||
}
|
||||
|
||||
void CommandScheduler::Schedule(Deleter&& func) {
|
||||
auto& command = commands[current_command];
|
||||
void CommandScheduler::Schedule(std::function<void(vk::Device, VmaAllocator)>&& func) {
|
||||
CommandSlot& command = commands[current_command];
|
||||
command.cleanups.push_back(func);
|
||||
}
|
||||
|
||||
@@ -175,6 +180,9 @@ void CommandScheduler::SwitchSlot() {
|
||||
// Wait for the GPU to finish with all resources for this command.
|
||||
Synchronize();
|
||||
|
||||
// Invoke the callback function
|
||||
switch_callback(current_command);
|
||||
|
||||
const vk::CommandBufferBeginInfo begin_info = {
|
||||
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
|
||||
};
|
||||
|
@@ -8,59 +8,66 @@
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_vulkan/vk_buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class PoolManager;
|
||||
}
|
||||
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
constexpr u32 SCHEDULER_COMMAND_COUNT = 4;
|
||||
|
||||
using Deleter = std::function<void(vk::Device, VmaAllocator)>;
|
||||
|
||||
class Buffer;
|
||||
class Instance;
|
||||
|
||||
class CommandScheduler {
|
||||
public:
|
||||
CommandScheduler(Instance& instance);
|
||||
CommandScheduler(Instance& instance, PoolManager& pool_manager);
|
||||
~CommandScheduler();
|
||||
|
||||
/// Block host until the current command completes execution
|
||||
// Blocks the host until the current command completes execution
|
||||
void Synchronize();
|
||||
|
||||
/// Defer operation until the current command completes execution
|
||||
void Schedule(Deleter&& func);
|
||||
// Sets a function to be called when the command slot is switched
|
||||
void SetSwitchCallback(std::function<void(u32)> callback);
|
||||
|
||||
/// Submits the current command to the graphics queue
|
||||
// Defers operation until the current command completes execution
|
||||
void Schedule(std::function<void(vk::Device, VmaAllocator)>&& func);
|
||||
|
||||
// Submits the current command to the graphics queue
|
||||
void Submit(bool wait_completion = false, vk::Semaphore wait = VK_NULL_HANDLE,
|
||||
vk::Semaphore signal = VK_NULL_HANDLE);
|
||||
|
||||
/// Returns the command buffer used for early upload operations.
|
||||
/// This is useful for vertex/uniform buffer uploads that happen once per frame
|
||||
// Returns the command buffer used for early upload operations.
|
||||
// This is useful for vertex/uniform buffer uploads that happen once per frame
|
||||
vk::CommandBuffer GetUploadCommandBuffer();
|
||||
|
||||
/// Returns the command buffer used for rendering
|
||||
inline vk::CommandBuffer GetRenderCommandBuffer() const {
|
||||
// Returns the command buffer used for rendering
|
||||
vk::CommandBuffer GetRenderCommandBuffer() const {
|
||||
const CommandSlot& command = commands[current_command];
|
||||
return command.render_command_buffer;
|
||||
}
|
||||
|
||||
/// Returns the upload buffer of the active command slot
|
||||
inline Buffer& GetCommandUploadBuffer() {
|
||||
// Returns the upload buffer of the active command slot
|
||||
Buffer& GetCommandUploadBuffer() {
|
||||
CommandSlot& command = commands[current_command];
|
||||
return *command.upload_buffer;
|
||||
return *static_cast<Buffer*>(command.upload_buffer.Get());
|
||||
}
|
||||
|
||||
/// Returns the index of the current command slot
|
||||
// Returns the index of the current command slot
|
||||
inline u32 GetCurrentSlotIndex() const {
|
||||
return current_command;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Activates the next command slot and optionally waits for its completion
|
||||
// Activates the next command slot and optionally waits for its completion
|
||||
void SwitchSlot();
|
||||
|
||||
private:
|
||||
Instance& instance;
|
||||
PoolManager& pool_manager;
|
||||
u64 next_fence_counter = 1;
|
||||
u64 completed_fence_counter = 0;
|
||||
|
||||
@@ -70,12 +77,13 @@ private:
|
||||
vk::Fence fence = VK_NULL_HANDLE;
|
||||
vk::CommandBuffer render_command_buffer;
|
||||
vk::CommandBuffer upload_command_buffer;
|
||||
std::unique_ptr<Buffer> upload_buffer;
|
||||
std::vector<Deleter> cleanups;
|
||||
BufferHandle upload_buffer;
|
||||
std::vector<std::function<void(vk::Device, VmaAllocator)>> cleanups;
|
||||
};
|
||||
|
||||
vk::CommandPool command_pool = VK_NULL_HANDLE;
|
||||
std::array<CommandSlot, SCHEDULER_COMMAND_COUNT> commands;
|
||||
std::function<void(u32)> switch_callback;
|
||||
u32 current_command = 0;
|
||||
};
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/common/pool_manager.h"
|
||||
#include "video_core/renderer_vulkan/pica_to_vulkan.h"
|
||||
#include "video_core/renderer_vulkan/vk_buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture.h"
|
||||
@@ -73,11 +74,9 @@ inline vk::ImageViewType ToVkImageViewType(TextureViewType view_type) {
|
||||
}
|
||||
}
|
||||
|
||||
Texture::Texture(Instance& instance, CommandScheduler& scheduler) :
|
||||
instance(instance), scheduler(scheduler) {}
|
||||
|
||||
Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info) : TextureBase(info),
|
||||
instance(instance), scheduler(scheduler) {
|
||||
Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
|
||||
const TextureInfo& info) : TextureBase(info), instance(instance), scheduler(scheduler),
|
||||
pool_manager(pool_manager) {
|
||||
|
||||
// Convert the input format to another that supports attachments
|
||||
advertised_format = ToVkFormat(info.format);
|
||||
@@ -118,7 +117,7 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureI
|
||||
.image = image,
|
||||
.viewType = ToVkImageViewType(info.view_type),
|
||||
.format = internal_format,
|
||||
.subresourceRange = {aspect, 0, info.levels, 0, 1}
|
||||
.subresourceRange = {aspect, 0, 1, 0, 1} // Should this include the levels?
|
||||
};
|
||||
|
||||
// Create image view
|
||||
@@ -130,9 +129,9 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureI
|
||||
Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal, 0, info.levels);
|
||||
}
|
||||
|
||||
Texture::Texture(Instance& instance, CommandScheduler& scheduler, vk::Image image, vk::Format format,
|
||||
const TextureInfo& info) : TextureBase(info), instance(instance),
|
||||
scheduler(scheduler), image(image), is_texture_owned(false) {
|
||||
Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
|
||||
vk::Image image, vk::Format format, const TextureInfo& info) : TextureBase(info),
|
||||
instance(instance), scheduler(scheduler), pool_manager(pool_manager), image(image), is_texture_owned(false) {
|
||||
|
||||
advertised_format = internal_format = format;
|
||||
aspect = vk::ImageAspectFlagBits::eColor;
|
||||
@@ -167,6 +166,10 @@ Texture::~Texture() {
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::Free() {
|
||||
pool_manager.Free<Texture>(this);
|
||||
}
|
||||
|
||||
void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, u32 level, u32 level_count) {
|
||||
ASSERT(level + level_count < TEXTURE_MAX_LEVELS);
|
||||
|
||||
@@ -458,8 +461,8 @@ void Texture::Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 lev
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::BlitTo(TextureHandle dest, Rect2D source_rect, Rect2D dest_rect, u32 src_level, u32 dest_level,
|
||||
u32 src_layer, u32 dest_layer) {
|
||||
void Texture::BlitTo(TextureHandle dest, Common::Rectangle<u32> source_rect, Common::Rectangle<u32> dest_rect,
|
||||
u32 src_level, u32 dest_level, u32 src_layer, u32 dest_layer) {
|
||||
|
||||
Texture* dest_texture = static_cast<Texture*>(dest.Get());
|
||||
|
||||
@@ -469,15 +472,13 @@ void Texture::BlitTo(TextureHandle dest, Rect2D source_rect, Rect2D dest_rect, u
|
||||
dest_texture->Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal);
|
||||
|
||||
const std::array source_offsets = {
|
||||
vk::Offset3D{source_rect.x, source_rect.y, 0},
|
||||
vk::Offset3D{static_cast<s32>(source_rect.x + source_rect.width),
|
||||
static_cast<s32>(source_rect.y + source_rect.height), 1}
|
||||
vk::Offset3D{static_cast<s32>(source_rect.left), static_cast<s32>(source_rect.bottom), 0},
|
||||
vk::Offset3D{static_cast<s32>(source_rect.right), static_cast<s32>(source_rect.top), 1}
|
||||
};
|
||||
|
||||
const std::array dest_offsets = {
|
||||
vk::Offset3D{dest_rect.x, dest_rect.y, 0},
|
||||
vk::Offset3D{static_cast<s32>(dest_rect.x + dest_rect.width),
|
||||
static_cast<s32>(dest_rect.y + dest_rect.height), 1}
|
||||
vk::Offset3D{static_cast<s32>(dest_rect.left), static_cast<s32>(dest_rect.bottom), 0},
|
||||
vk::Offset3D{static_cast<s32>(dest_rect.right), static_cast<s32>(dest_rect.top), 1}
|
||||
};
|
||||
|
||||
const vk::ImageBlit blit_area = {
|
||||
@@ -665,8 +666,8 @@ void StagingTexture::Commit(u32 size) {
|
||||
vmaFlushAllocation(allocator, allocation, 0, size);
|
||||
}
|
||||
|
||||
Sampler::Sampler(Instance& instance, SamplerInfo info) :
|
||||
SamplerBase(info), instance(instance) {
|
||||
Sampler::Sampler(Instance& instance, PoolManager& pool_manager, SamplerInfo info) :
|
||||
SamplerBase(info), instance(instance), pool_manager(pool_manager) {
|
||||
|
||||
auto properties = instance.GetPhysicalDevice().getProperties();
|
||||
const auto filtering = PicaToVK::TextureFilterMode(info.mag_filter,
|
||||
@@ -695,4 +696,8 @@ Sampler::~Sampler() {
|
||||
device.destroySampler(sampler);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
void Sampler::Free() {
|
||||
pool_manager.Free<Sampler>(this);
|
||||
}
|
||||
|
||||
} // namespace VideoCore::Vulkan
|
||||
|
@@ -7,10 +7,14 @@
|
||||
#include "video_core/common/texture.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class PoolManager;
|
||||
}
|
||||
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
// PICA texture have at most 8 mipmap levels
|
||||
constexpr u32 TEXTURE_MAX_LEVELS = 10;
|
||||
constexpr u32 TEXTURE_MAX_LEVELS = 12;
|
||||
|
||||
class Instance;
|
||||
class CommandScheduler;
|
||||
@@ -20,24 +24,24 @@ class CommandScheduler;
|
||||
*/
|
||||
class Texture : public VideoCore::TextureBase {
|
||||
public:
|
||||
// Default constructor
|
||||
Texture(Instance& instance, CommandScheduler& scheduler);
|
||||
|
||||
// Constructor for texture creation
|
||||
Texture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info);
|
||||
|
||||
// Constructor for swapchain images
|
||||
Texture(Instance& instance, CommandScheduler& scheduler, vk::Image image, vk::Format format,
|
||||
Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
|
||||
const TextureInfo& info);
|
||||
|
||||
// Constructor for swapchain images
|
||||
Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
|
||||
vk::Image image, vk::Format format, const TextureInfo& info);
|
||||
|
||||
~Texture() override;
|
||||
|
||||
void Free() override;
|
||||
|
||||
void Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32 level = 0) override;
|
||||
|
||||
void Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 level = 0) override;
|
||||
|
||||
void BlitTo(TextureHandle dest, Rect2D src_rectangle, Rect2D dest_rect, u32 src_level = 0,
|
||||
u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override;
|
||||
void BlitTo(TextureHandle dest, Common::Rectangle<u32> src_rectangle, Common::Rectangle<u32> dest_rect,
|
||||
u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override;
|
||||
|
||||
void CopyFrom(TextureHandle source) override;
|
||||
|
||||
@@ -83,6 +87,7 @@ public:
|
||||
private:
|
||||
Instance& instance;
|
||||
CommandScheduler& scheduler;
|
||||
PoolManager& pool_manager;
|
||||
|
||||
// Vulkan texture handle
|
||||
vk::Image image = VK_NULL_HANDLE;
|
||||
@@ -103,19 +108,20 @@ private:
|
||||
*/
|
||||
class StagingTexture : public VideoCore::TextureBase {
|
||||
public:
|
||||
StagingTexture(Instance& instance, CommandScheduler& scheduler,
|
||||
const TextureInfo& info);
|
||||
StagingTexture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info);
|
||||
~StagingTexture();
|
||||
|
||||
/// Flushes any writes made to texture memory
|
||||
void Free() override {}
|
||||
|
||||
// Flushes any writes made to texture memory
|
||||
void Commit(u32 size);
|
||||
|
||||
/// Returns a span of the mapped texture memory
|
||||
// Returns a span of the mapped texture memory
|
||||
void* GetMappedPtr() {
|
||||
return mapped_ptr;
|
||||
}
|
||||
|
||||
/// Returns the staging image handle
|
||||
// Returns the staging image handle
|
||||
vk::Image GetHandle() const {
|
||||
return image;
|
||||
}
|
||||
@@ -136,16 +142,19 @@ private:
|
||||
*/
|
||||
class Sampler : public VideoCore::SamplerBase {
|
||||
public:
|
||||
Sampler(Instance& instance, SamplerInfo info);
|
||||
Sampler(Instance& instance, PoolManager& pool_manager, SamplerInfo info);
|
||||
~Sampler() override;
|
||||
|
||||
/// Returns the underlying vulkan sampler handle
|
||||
void Free() override;
|
||||
|
||||
// Returns the underlying vulkan sampler handle
|
||||
vk::Sampler GetHandle() const {
|
||||
return sampler;
|
||||
}
|
||||
|
||||
private:
|
||||
Instance& instance;
|
||||
PoolManager& pool_manager;
|
||||
vk::Sampler sampler;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user