video_core: Even more fixes, close to building now
This commit is contained in:
@@ -50,14 +50,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We explicitly delete the copy assignment operator here, because the
|
|
||||||
// default copy assignment would copy the full storage value, rather than
|
|
||||||
// just the bits relevant to this particular bit field.
|
|
||||||
// Ideally, we would just implement the copy assignment to copy only the
|
|
||||||
// relevant bits, but we're prevented from doing that because the savestate
|
|
||||||
// code expects that this class is trivially copyable.
|
|
||||||
BitFieldArray& operator=(const BitFieldArray&) = delete;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr bool IsSigned() const { return std::is_signed<T>(); }
|
constexpr bool IsSigned() const { return std::is_signed<T>(); }
|
||||||
constexpr std::size_t StartBit() const { return position; }
|
constexpr std::size_t StartBit() const { return position; }
|
||||||
|
@@ -62,6 +62,10 @@ struct AlignedAllocation {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
class ObjectPool {
|
class ObjectPool {
|
||||||
public:
|
public:
|
||||||
|
ObjectPool() {
|
||||||
|
vacants.reserve(32);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... P>
|
template<typename... P>
|
||||||
T* Allocate(P&&... p) {
|
T* Allocate(P&&... p) {
|
||||||
#ifndef OBJECT_POOL_DEBUG
|
#ifndef OBJECT_POOL_DEBUG
|
||||||
|
@@ -112,6 +112,8 @@ add_library(video_core STATIC
|
|||||||
renderer_vulkan/vk_pipeline.cpp
|
renderer_vulkan/vk_pipeline.cpp
|
||||||
renderer_vulkan/vk_pipeline.h
|
renderer_vulkan/vk_pipeline.h
|
||||||
renderer_vulkan/vk_platform.h
|
renderer_vulkan/vk_platform.h
|
||||||
|
renderer_vulkan/vk_renderpass_cache.cpp
|
||||||
|
renderer_vulkan/vk_renderpass_cache.h
|
||||||
renderer_vulkan/vk_shader_gen.cpp
|
renderer_vulkan/vk_shader_gen.cpp
|
||||||
renderer_vulkan/vk_shader_gen.h
|
renderer_vulkan/vk_shader_gen.h
|
||||||
renderer_vulkan/vk_shader.cpp
|
renderer_vulkan/vk_shader.cpp
|
||||||
|
@@ -59,10 +59,10 @@ public:
|
|||||||
virtual ShaderHandle CreateShader(ShaderStage stage, std::string_view name, std::string source) = 0;
|
virtual ShaderHandle CreateShader(ShaderStage stage, std::string_view name, std::string source) = 0;
|
||||||
|
|
||||||
// Binds a vertex buffer at a provided offset
|
// Binds a vertex buffer at a provided offset
|
||||||
virtual void BindVertexBuffer(BufferHandle buffer, std::span<const u32> offsets) = 0;
|
virtual void BindVertexBuffer(BufferHandle buffer, std::span<const u64> offsets) = 0;
|
||||||
|
|
||||||
// Binds an index buffer at provided offset
|
// Binds an index buffer at provided offset
|
||||||
virtual void BindIndexBuffer(BufferHandle buffer, AttribType index_type, u32 offset) = 0;
|
virtual void BindIndexBuffer(BufferHandle buffer, AttribType index_type, u64 offset) = 0;
|
||||||
|
|
||||||
// Start a draw operation
|
// Start a draw operation
|
||||||
virtual void Draw(PipelineHandle pipeline, FramebufferHandle draw_framebuffer, u32 base_vertex,
|
virtual void Draw(PipelineHandle pipeline, FramebufferHandle draw_framebuffer, u32 base_vertex,
|
||||||
|
@@ -16,6 +16,12 @@ enum class MSAASamples : u32 {
|
|||||||
x8
|
x8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// States which operation to perform on the framebuffer attachment during rendering
|
||||||
|
enum class LoadOp : u8 {
|
||||||
|
Load = 0,
|
||||||
|
Clear = 1
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about a framebuffer
|
* Information about a framebuffer
|
||||||
*/
|
*/
|
||||||
@@ -31,9 +37,7 @@ struct FramebufferInfo {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// 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> {
|
||||||
public:
|
public:
|
||||||
FramebufferBase(const FramebufferInfo& info) : info(info) {}
|
FramebufferBase(const FramebufferInfo& info) : info(info) {}
|
||||||
@@ -43,8 +47,8 @@ public:
|
|||||||
FramebufferBase(const FramebufferBase&) = delete;
|
FramebufferBase(const FramebufferBase&) = delete;
|
||||||
FramebufferBase& operator=(const FramebufferBase&) = delete;
|
FramebufferBase& operator=(const FramebufferBase&) = delete;
|
||||||
|
|
||||||
// Clears the attachments bound to the framebuffer
|
// Clears the attachments bound to the framebuffer using the last stored clear value
|
||||||
virtual void DoClear(Common::Vec4f color, float depth, u8 stencil) = 0;
|
virtual void DoClear() = 0;
|
||||||
|
|
||||||
// Returns an immutable reference to the color attachment
|
// Returns an immutable reference to the color attachment
|
||||||
TextureHandle GetColorAttachment() const {
|
TextureHandle GetColorAttachment() const {
|
||||||
@@ -61,8 +65,22 @@ public:
|
|||||||
draw_rect = rect;
|
draw_rect = rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadOp GetLoadOp() const {
|
||||||
|
return load_op;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLoadOp(LoadOp op) {
|
||||||
|
load_op = op;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetClearValues(Common::Vec4f color, float depth, u8 stencil) {
|
||||||
|
clear_color_value = color;
|
||||||
|
clear_depth_value = depth;
|
||||||
|
clear_stencil_value = stencil;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the area of the framebuffer affected by draw operations
|
// Returns the area of the framebuffer affected by draw operations
|
||||||
Common::Rectangle<u32> GetDrawRect() {
|
Common::Rectangle<u32> GetDrawRect() const {
|
||||||
return draw_rect;
|
return draw_rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +90,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
LoadOp load_op = LoadOp::Load;
|
||||||
|
Common::Vec4f clear_color_value{};
|
||||||
|
float clear_depth_value = 0.f;
|
||||||
|
u8 clear_stencil_value = 0;
|
||||||
Common::Rectangle<u32> draw_rect;
|
Common::Rectangle<u32> draw_rect;
|
||||||
FramebufferInfo info;
|
FramebufferInfo info;
|
||||||
};
|
};
|
||||||
|
@@ -39,8 +39,6 @@ enum class BindingType : u32 {
|
|||||||
|
|
||||||
using BindingGroup = BitFieldArray<0, 3, MAX_BINDINGS_IN_GROUP, BindingType>;
|
using BindingGroup = BitFieldArray<0, 3, MAX_BINDINGS_IN_GROUP, BindingType>;
|
||||||
|
|
||||||
static_assert(sizeof(BindingGroup));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes all the resources used in the pipeline
|
* Describes all the resources used in the pipeline
|
||||||
*/
|
*/
|
||||||
@@ -50,8 +48,6 @@ struct PipelineLayoutInfo {
|
|||||||
u8 push_constant_block_size = 0;
|
u8 push_constant_block_size = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(PipelineLayoutInfo));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pipeline state is tightly packed with bitfields to reduce
|
* The pipeline state is tightly packed with bitfields to reduce
|
||||||
* the overhead of hashing as much as possible
|
* the overhead of hashing as much as possible
|
||||||
|
@@ -96,7 +96,7 @@ constexpr u32 UTILITY_GROUP = 0;
|
|||||||
constexpr u32 TEXTURE_GROUP = 1;
|
constexpr u32 TEXTURE_GROUP = 1;
|
||||||
|
|
||||||
// Rasterizer pipeline layout
|
// Rasterizer pipeline layout
|
||||||
static constexpr PipelineLayoutInfo RASTERIZER_PIPELINE_INFO = {
|
constexpr PipelineLayoutInfo RASTERIZER_PIPELINE_LAYOUT = {
|
||||||
.group_count = 3,
|
.group_count = 3,
|
||||||
.binding_groups = {
|
.binding_groups = {
|
||||||
// Uniform + LUT set
|
// Uniform + LUT set
|
||||||
@@ -192,6 +192,10 @@ Rasterizer::Rasterizer(Frontend::EmuWindow& emu_window, std::unique_ptr<BackendB
|
|||||||
// Create pipeline cache
|
// Create pipeline cache
|
||||||
pipeline_cache = std::make_unique<PipelineCache>(emu_window, backend);
|
pipeline_cache = std::make_unique<PipelineCache>(emu_window, backend);
|
||||||
|
|
||||||
|
// Initialize the rasterization pipeline info
|
||||||
|
raster_info.vertex_layout = HardwareVertex::GetVertexLayout();
|
||||||
|
raster_info.layout = RASTERIZER_PIPELINE_LAYOUT;
|
||||||
|
|
||||||
// Synchronize pica state
|
// Synchronize pica state
|
||||||
SyncEntireState();
|
SyncEntireState();
|
||||||
}
|
}
|
||||||
@@ -348,7 +352,7 @@ void Rasterizer::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_min, u32
|
|||||||
|
|
||||||
VertexLayout layout{};
|
VertexLayout layout{};
|
||||||
std::array<bool, 16> enable_attributes{};
|
std::array<bool, 16> enable_attributes{};
|
||||||
std::array<u32, 16> binding_offsets{};
|
std::array<u64, 16> binding_offsets{};
|
||||||
|
|
||||||
u32 buffer_offset = 0;
|
u32 buffer_offset = 0;
|
||||||
for (const auto& loader : vertex_attributes.attribute_loaders) {
|
for (const auto& loader : vertex_attributes.attribute_loaders) {
|
||||||
@@ -448,7 +452,7 @@ void Rasterizer::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_min, u32
|
|||||||
vertex_buffer->Commit(vs_input_size);
|
vertex_buffer->Commit(vs_input_size);
|
||||||
|
|
||||||
// Bind the vertex buffers with all the bindings
|
// Bind the vertex buffers with all the bindings
|
||||||
auto offsets = std::span<u32>{binding_offsets.data(), layout.binding_count};
|
auto offsets = std::span<u64>{binding_offsets.data(), layout.binding_count};
|
||||||
backend->BindVertexBuffer(vertex_buffer, offsets);
|
backend->BindVertexBuffer(vertex_buffer, offsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,6 +591,8 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) {
|
|||||||
|
|
||||||
// Retrieve the framebuffer assigned to the surfaces and update raster_info
|
// Retrieve the framebuffer assigned to the surfaces and update raster_info
|
||||||
FramebufferHandle framebuffer = res_cache.GetFramebuffer(color_surface, depth_surface);
|
FramebufferHandle framebuffer = res_cache.GetFramebuffer(color_surface, depth_surface);
|
||||||
|
framebuffer->SetLoadOp(LoadOp::Load);
|
||||||
|
|
||||||
raster_info.color_attachment = framebuffer->GetColorAttachment().IsValid() ?
|
raster_info.color_attachment = framebuffer->GetColorAttachment().IsValid() ?
|
||||||
framebuffer->GetColorAttachment()->GetFormat() :
|
framebuffer->GetColorAttachment()->GetFormat() :
|
||||||
TextureFormat::Undefined;
|
TextureFormat::Undefined;
|
||||||
@@ -815,7 +821,7 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) {
|
|||||||
// Bind the vertex buffer at the current mapped offset. This effectively means
|
// Bind the vertex buffer at the current mapped offset. This effectively means
|
||||||
// that when base_vertex is zero the GPU will start drawing from the current mapped
|
// that when base_vertex is zero the GPU will start drawing from the current mapped
|
||||||
// offset not the start of the buffer.
|
// offset not the start of the buffer.
|
||||||
const std::array mapped_offset = {vertex_buffer->GetCurrentOffset()};
|
const std::array<u64, 1> mapped_offset = {vertex_buffer->GetCurrentOffset()};
|
||||||
backend->BindVertexBuffer(vertex_buffer, mapped_offset);
|
backend->BindVertexBuffer(vertex_buffer, mapped_offset);
|
||||||
|
|
||||||
const std::size_t max_vertices = VERTEX_BUFFER_INFO.capacity / sizeof(HardwareVertex);
|
const std::size_t max_vertices = VERTEX_BUFFER_INFO.capacity / sizeof(HardwareVertex);
|
||||||
|
@@ -262,7 +262,7 @@ bool RasterizerCache::FillSurface(const Surface& surface, const u8* fill_data, C
|
|||||||
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(surface->pixel_format);
|
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(surface->pixel_format);
|
||||||
const auto color_values = Pica::Texture::LookupTexture(fill_data, 0, 0, tex_info) / 255.f;
|
const auto color_values = Pica::Texture::LookupTexture(fill_data, 0, 0, tex_info) / 255.f;
|
||||||
|
|
||||||
framebuffer->DoClear(color_values, 0.0f, 0);
|
framebuffer->SetClearValues(color_values, 0.0f, 0);
|
||||||
} else if (surface->type == SurfaceType::Depth) {
|
} else if (surface->type == SurfaceType::Depth) {
|
||||||
u32 depth_32bit = 0;
|
u32 depth_32bit = 0;
|
||||||
float depth_float;
|
float depth_float;
|
||||||
@@ -278,7 +278,7 @@ bool RasterizerCache::FillSurface(const Surface& surface, const u8* fill_data, C
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
framebuffer->DoClear({}, depth_float, 0);
|
framebuffer->SetClearValues({}, depth_float, 0);
|
||||||
} else if (surface->type == SurfaceType::DepthStencil) {
|
} else if (surface->type == SurfaceType::DepthStencil) {
|
||||||
u32 value_32bit;
|
u32 value_32bit;
|
||||||
std::memcpy(&value_32bit, fill_data, sizeof(u32));
|
std::memcpy(&value_32bit, fill_data, sizeof(u32));
|
||||||
@@ -286,9 +286,10 @@ bool RasterizerCache::FillSurface(const Surface& surface, const u8* fill_data, C
|
|||||||
float depth_float = (value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
|
float depth_float = (value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
|
||||||
u8 stencil_int = (value_32bit >> 24);
|
u8 stencil_int = (value_32bit >> 24);
|
||||||
|
|
||||||
framebuffer->DoClear({}, depth_float, stencil_int);
|
framebuffer->SetClearValues({}, depth_float, stencil_int);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
framebuffer->DoClear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -134,7 +134,7 @@ constexpr VertexLayout ScreenRectVertex::GetVertexLayout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Renderer pipeline layout
|
// Renderer pipeline layout
|
||||||
static constexpr PipelineLayoutInfo RENDERER_PIPELINE_INFO = {
|
static constexpr PipelineLayoutInfo RENDERER_PIPELINE_LAYOUT = {
|
||||||
.group_count = 2,
|
.group_count = 2,
|
||||||
.binding_groups = {
|
.binding_groups = {
|
||||||
BindingGroup{
|
BindingGroup{
|
||||||
@@ -167,7 +167,7 @@ DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(wi
|
|||||||
|
|
||||||
PipelineInfo present_pipeline_info = {
|
PipelineInfo present_pipeline_info = {
|
||||||
.vertex_layout = ScreenRectVertex::GetVertexLayout(),
|
.vertex_layout = ScreenRectVertex::GetVertexLayout(),
|
||||||
.layout = RENDERER_PIPELINE_INFO,
|
.layout = RENDERER_PIPELINE_LAYOUT,
|
||||||
.color_attachment = TextureFormat::PresentColor,
|
.color_attachment = TextureFormat::PresentColor,
|
||||||
.depth_attachment = TextureFormat::Undefined
|
.depth_attachment = TextureFormat::Undefined
|
||||||
};
|
};
|
||||||
@@ -333,9 +333,10 @@ void DisplayRenderer::DrawSingleScreen(u32 screen, bool rotate, float x, float y
|
|||||||
const ScreenInfo& screen_info = screen_infos[screen];
|
const ScreenInfo& screen_info = screen_infos[screen];
|
||||||
const auto& texcoords = screen_info.display_texcoords;
|
const auto& texcoords = screen_info.display_texcoords;
|
||||||
|
|
||||||
// Clear the swapchain framebuffer
|
// Set the swapchain framebuffer to clear mode
|
||||||
FramebufferHandle display = backend->GetWindowFramebuffer();
|
FramebufferHandle display = backend->GetWindowFramebuffer();
|
||||||
display->DoClear(clear_color, 0.f, 0);
|
display->SetLoadOp(LoadOp::Clear);
|
||||||
|
display->SetClearValues(clear_color, 0.f, 0);
|
||||||
|
|
||||||
// Update viewport and scissor
|
// Update viewport and scissor
|
||||||
const auto& color_surface = display->GetColorAttachment();
|
const auto& color_surface = display->GetColorAttachment();
|
||||||
@@ -360,7 +361,7 @@ void DisplayRenderer::DrawSingleScreen(u32 screen, bool rotate, float x, float y
|
|||||||
}
|
}
|
||||||
|
|
||||||
const u32 size = sizeof(ScreenRectVertex) * vertices.size();
|
const u32 size = sizeof(ScreenRectVertex) * vertices.size();
|
||||||
const u32 mapped_offset = vertex_buffer->GetCurrentOffset();
|
const u64 mapped_offset = vertex_buffer->GetCurrentOffset();
|
||||||
auto vertex_data = vertex_buffer->Map(size);
|
auto vertex_data = vertex_buffer->Map(size);
|
||||||
|
|
||||||
// Copy vertex data
|
// Copy vertex data
|
||||||
|
@@ -42,7 +42,7 @@ constexpr vk::IndexType ToVkIndexType(AttribType type) {
|
|||||||
|
|
||||||
Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
||||||
instance(window), swapchain(instance, instance.GetSurface()),
|
instance(window), swapchain(instance, instance.GetSurface()),
|
||||||
scheduler(instance) {
|
scheduler(instance), renderpass_cache(instance, swapchain) {
|
||||||
|
|
||||||
// TODO: Properly report GPU hardware
|
// TODO: Properly report GPU hardware
|
||||||
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
|
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
|
||||||
@@ -51,32 +51,9 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
|||||||
telemetry_session.AddField(user_system, "GPU_Model", "GTX 1650");
|
telemetry_session.AddField(user_system, "GPU_Model", "GTX 1650");
|
||||||
telemetry_session.AddField(user_system, "GPU_Vulkan_Version", "Vulkan 1.3");
|
telemetry_session.AddField(user_system, "GPU_Vulkan_Version", "Vulkan 1.3");
|
||||||
|
|
||||||
// Pre-create all needed renderpasses by the renderer
|
// Create pipeline cache object
|
||||||
constexpr std::array color_formats = {
|
vk::Device device = instance.GetDevice();
|
||||||
vk::Format::eUndefined,
|
pipeline_cache = device.createPipelineCache({});
|
||||||
vk::Format::eR8G8B8A8Unorm,
|
|
||||||
vk::Format::eR8G8B8Unorm,
|
|
||||||
vk::Format::eR5G5B5A1UnormPack16,
|
|
||||||
vk::Format::eR5G6B5UnormPack16,
|
|
||||||
vk::Format::eR4G4B4A4UnormPack16
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr std::array depth_stencil_formats = {
|
|
||||||
vk::Format::eUndefined,
|
|
||||||
vk::Format::eD16Unorm,
|
|
||||||
vk::Format::eX8D24UnormPack32,
|
|
||||||
vk::Format::eD24UnormS8Uint,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create all required renderpasses
|
|
||||||
for (u32 color = 0; color <= MAX_COLOR_FORMATS; color++) {
|
|
||||||
for (u32 depth = 0; depth <= MAX_DEPTH_FORMATS; depth++) {
|
|
||||||
if (color == 0 && depth == 0) continue;
|
|
||||||
|
|
||||||
u32 index = color * MAX_COLOR_FORMATS + depth;
|
|
||||||
renderpass_cache[index] = CreateRenderPass(color_formats[color], depth_stencil_formats[depth]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::array pool_sizes = {
|
constexpr std::array pool_sizes = {
|
||||||
vk::DescriptorPoolSize{vk::DescriptorType::eUniformBuffer, 1024},
|
vk::DescriptorPoolSize{vk::DescriptorType::eUniformBuffer, 1024},
|
||||||
@@ -93,7 +70,6 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create descriptor pools
|
// Create descriptor pools
|
||||||
vk::Device device = instance.GetDevice();
|
|
||||||
for (u32 pool = 0; pool < SCHEDULER_COMMAND_COUNT; pool++) {
|
for (u32 pool = 0; pool < SCHEDULER_COMMAND_COUNT; pool++) {
|
||||||
descriptor_pools[pool] = device.createDescriptorPool(pool_info);
|
descriptor_pools[pool] = device.createDescriptorPool(pool_info);
|
||||||
}
|
}
|
||||||
@@ -101,8 +77,10 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
|||||||
|
|
||||||
Backend::~Backend() {
|
Backend::~Backend() {
|
||||||
vk::Device device = instance.GetDevice();
|
vk::Device device = instance.GetDevice();
|
||||||
for (auto& renderpass : renderpass_cache) {
|
device.destroyPipelineCache(pipeline_cache);
|
||||||
device.destroyRenderPass(renderpass);
|
|
||||||
|
for (u32 pool = 0; pool < SCHEDULER_COMMAND_COUNT; pool++) {
|
||||||
|
device.destroyDescriptorPool(descriptor_pools[pool]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,9 +112,10 @@ FramebufferHandle Backend::CreateFramebuffer(FramebufferInfo info) {
|
|||||||
// 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 renderpass = GetRenderPass(color, depth);
|
vk::RenderPass load_renderpass = GetRenderPass(color, depth, false);
|
||||||
|
vk::RenderPass clear_renderpass = GetRenderPass(color, depth, true);
|
||||||
|
|
||||||
return FramebufferHandle{framebuffer_pool.Allocate(instance, info, renderpass)};
|
return FramebufferHandle{framebuffer_pool.Allocate(instance, info, load_renderpass, clear_renderpass)};
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureHandle Backend::CreateTexture(TextureInfo info) {
|
TextureHandle Backend::CreateTexture(TextureInfo info) {
|
||||||
@@ -152,12 +131,14 @@ PipelineHandle Backend::CreatePipeline(PipelineType type, PipelineInfo info) {
|
|||||||
|
|
||||||
// Find an owner first
|
// Find an owner first
|
||||||
if (auto iter = pipeline_owners.find(info.layout); iter != pipeline_owners.end()) {
|
if (auto iter = pipeline_owners.find(info.layout); iter != pipeline_owners.end()) {
|
||||||
return PipelineHandle{pipeline_pool.Allocate(instance, iter->second, type, info, renderpass, cache)};
|
return PipelineHandle{pipeline_pool.Allocate(instance, iter->second, type, info,
|
||||||
|
renderpass, pipeline_cache)};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the layout
|
// Create the layout
|
||||||
auto result = pipeline_owners.emplace(info.layout, PipelineOwner{instance, info.layout});
|
auto result = pipeline_owners.emplace(info.layout, PipelineOwner{instance, info.layout});
|
||||||
return PipelineHandle{pipeline_pool.Allocate(instance, result.first->second, type, info, renderpass, cache)};
|
return PipelineHandle{pipeline_pool.Allocate(instance, result.first->second, type, info,
|
||||||
|
renderpass, pipeline_cache)};
|
||||||
}
|
}
|
||||||
|
|
||||||
SamplerHandle Backend::CreateSampler(SamplerInfo info) {
|
SamplerHandle Backend::CreateSampler(SamplerInfo info) {
|
||||||
@@ -165,30 +146,35 @@ SamplerHandle Backend::CreateSampler(SamplerInfo info) {
|
|||||||
return SamplerHandle{sampler_pool.Allocate(info)};
|
return SamplerHandle{sampler_pool.Allocate(info)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Backend::BindVertexBuffer(BufferHandle buffer, std::span<const u64> offsets) {
|
||||||
|
const Buffer* vertex = static_cast<const Buffer*>(buffer.Get());
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::BindIndexBuffer(BufferHandle buffer, AttribType index_type, u64 offset) {
|
||||||
|
const Buffer* index = static_cast<const Buffer*>(buffer.Get());
|
||||||
|
|
||||||
|
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||||
|
command_buffer.bindIndexBuffer(index->GetHandle(), 0, ToVkIndexType(index_type));
|
||||||
|
}
|
||||||
|
|
||||||
void Backend::Draw(PipelineHandle pipeline_handle, FramebufferHandle draw_framebuffer,
|
void Backend::Draw(PipelineHandle pipeline_handle, FramebufferHandle draw_framebuffer,
|
||||||
u32 base_vertex, u32 num_vertices) {
|
u32 base_vertex, u32 num_vertices) {
|
||||||
|
|
||||||
// Bind descriptor sets
|
// Bind descriptor sets
|
||||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
|
||||||
BindDescriptorSets(pipeline_handle);
|
BindDescriptorSets(pipeline_handle);
|
||||||
|
|
||||||
// Bind vertex buffer
|
|
||||||
const Buffer* vertex = static_cast<const Buffer*>(vertex_buffer.Get());
|
|
||||||
command_buffer.bindVertexBuffers(0, vertex->GetHandle(), vertex->GetBindOffset());
|
|
||||||
|
|
||||||
// Begin renderpass
|
// Begin renderpass
|
||||||
const Framebuffer* framebuffer = static_cast<const Framebuffer*>(draw_framebuffer.Get());
|
BeginRenderpass(draw_framebuffer);
|
||||||
const vk::RenderPassBeginInfo renderpass_begin = {
|
|
||||||
.renderPass = framebuffer->GetRenderpass(),
|
|
||||||
.framebuffer = framebuffer->GetHandle(),
|
|
||||||
.renderArea = ToVkRect2D(framebuffer->GetDrawRectangle()),
|
|
||||||
.clearValueCount = 0,
|
|
||||||
.pClearValues = nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
command_buffer.beginRenderPass(renderpass_begin, vk::SubpassContents::eInline);
|
|
||||||
|
|
||||||
// Bind pipeline
|
// Bind pipeline
|
||||||
const Pipeline* pipeline = static_cast<const Pipeline*>(pipeline_handle.Get());
|
const Pipeline* pipeline = static_cast<const Pipeline*>(pipeline_handle.Get());
|
||||||
|
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||||
command_buffer.bindPipeline(ToVkPipelineBindPoint(pipeline->GetType()), pipeline->GetHandle());
|
command_buffer.bindPipeline(ToVkPipelineBindPoint(pipeline->GetType()), pipeline->GetHandle());
|
||||||
|
|
||||||
// Submit draw
|
// Submit draw
|
||||||
@@ -199,34 +185,16 @@ void Backend::Draw(PipelineHandle pipeline_handle, FramebufferHandle draw_frameb
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Backend::DrawIndexed(PipelineHandle pipeline_handle, FramebufferHandle draw_framebuffer,
|
void Backend::DrawIndexed(PipelineHandle pipeline_handle, FramebufferHandle draw_framebuffer,
|
||||||
BufferHandle vertex_buffer, BufferHandle index_buffer, AttribType index_type,
|
|
||||||
u32 base_index, u32 num_indices, u32 base_vertex) {
|
u32 base_index, u32 num_indices, u32 base_vertex) {
|
||||||
// Bind descriptor sets
|
// Bind descriptor sets
|
||||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
|
||||||
BindDescriptorSets(pipeline_handle);
|
BindDescriptorSets(pipeline_handle);
|
||||||
|
|
||||||
// Bind vertex buffer
|
|
||||||
const Buffer* vertex = static_cast<const Buffer*>(vertex_buffer.Get());
|
|
||||||
command_buffer.bindVertexBuffers(0, vertex->GetHandle(), vertex->GetBindOffset());
|
|
||||||
|
|
||||||
// Bind index buffer
|
|
||||||
const Buffer* index = static_cast<const Buffer*>(index_buffer.Get());
|
|
||||||
command_buffer.bindIndexBuffer(index->GetHandle(), index->GetBindOffset(), ToVkIndexType(index_type));
|
|
||||||
|
|
||||||
// Begin renderpass
|
// Begin renderpass
|
||||||
const Framebuffer* framebuffer = static_cast<const Framebuffer*>(draw_framebuffer.Get());
|
BeginRenderpass(draw_framebuffer);
|
||||||
const vk::RenderPassBeginInfo renderpass_begin = {
|
|
||||||
.renderPass = framebuffer->GetRenderpass(),
|
|
||||||
.framebuffer = framebuffer->GetHandle(),
|
|
||||||
.renderArea = ToVkRect2D(framebuffer->GetDrawRectangle()),
|
|
||||||
.clearValueCount = 0,
|
|
||||||
.pClearValues = nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
command_buffer.beginRenderPass(renderpass_begin, vk::SubpassContents::eInline);
|
|
||||||
|
|
||||||
// Bind pipeline
|
// Bind pipeline
|
||||||
const Pipeline* pipeline = static_cast<const Pipeline*>(pipeline_handle.Get());
|
const Pipeline* pipeline = static_cast<const Pipeline*>(pipeline_handle.Get());
|
||||||
|
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||||
command_buffer.bindPipeline(ToVkPipelineBindPoint(pipeline->GetType()), pipeline->GetHandle());
|
command_buffer.bindPipeline(ToVkPipelineBindPoint(pipeline->GetType()), pipeline->GetHandle());
|
||||||
|
|
||||||
// Submit draw
|
// Submit draw
|
||||||
@@ -237,106 +205,80 @@ void Backend::DrawIndexed(PipelineHandle pipeline_handle, FramebufferHandle draw
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vk::RenderPass Backend::GetRenderPass(TextureFormat color, TextureFormat depth, bool is_clear) const {
|
||||||
vk::RenderPass Backend::CreateRenderPass(vk::Format color, vk::Format depth) const {
|
if (color == TextureFormat::PresentColor) {
|
||||||
// Define attachments
|
return renderpass_cache.GetPresentRenderpass();
|
||||||
const std::array attachments = {
|
} else {
|
||||||
vk::AttachmentDescription{
|
return renderpass_cache.GetRenderpass(color, depth, is_clear);
|
||||||
.format = color,
|
}
|
||||||
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
|
|
||||||
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
|
|
||||||
.initialLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
|
||||||
.finalLayout = vk::ImageLayout::eColorAttachmentOptimal
|
|
||||||
},
|
|
||||||
vk::AttachmentDescription{
|
|
||||||
.format = depth,
|
|
||||||
.initialLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
|
||||||
.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Our renderpasses only defines one color and depth attachment
|
|
||||||
const vk::AttachmentReference color_attachment_ref = {
|
|
||||||
.attachment = 0,
|
|
||||||
.layout = vk::ImageLayout::eColorAttachmentOptimal
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::AttachmentReference depth_attachment_ref = {
|
|
||||||
.attachment = 1,
|
|
||||||
.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::SubpassDependency subpass_dependency = {
|
|
||||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
|
||||||
.dstSubpass = 0,
|
|
||||||
.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput |
|
|
||||||
vk::PipelineStageFlagBits::eEarlyFragmentTests,
|
|
||||||
.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput |
|
|
||||||
vk::PipelineStageFlagBits::eEarlyFragmentTests,
|
|
||||||
.srcAccessMask = vk::AccessFlagBits::eNone,
|
|
||||||
.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite |
|
|
||||||
vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
|
||||||
.dependencyFlags = vk::DependencyFlagBits::eByRegion
|
|
||||||
};
|
|
||||||
|
|
||||||
// We also require only one subpass
|
|
||||||
const vk::SubpassDescription subpass = {
|
|
||||||
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
|
|
||||||
.inputAttachmentCount = 0,
|
|
||||||
.pInputAttachments = nullptr,
|
|
||||||
.colorAttachmentCount = 1,
|
|
||||||
.pColorAttachments = &color_attachment_ref,
|
|
||||||
.pResolveAttachments = 0,
|
|
||||||
.pDepthStencilAttachment = &depth_attachment_ref
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::RenderPassCreateInfo renderpass_info = {
|
|
||||||
.attachmentCount = 2,
|
|
||||||
.pAttachments = attachments.data(),
|
|
||||||
.subpassCount = 1,
|
|
||||||
.pSubpasses = &subpass,
|
|
||||||
.dependencyCount = 1,
|
|
||||||
.pDependencies = &subpass_dependency
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the renderpass
|
|
||||||
vk::Device device = instance.GetDevice();
|
|
||||||
return device.createRenderPass(renderpass_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::RenderPass Backend::GetRenderPass(TextureFormat color, TextureFormat depth) const {
|
|
||||||
u32 color_index = color != TextureFormat::Undefined ? static_cast<u32>(color) + 1 : 0;
|
|
||||||
u32 depth_index = depth != TextureFormat::Undefined ? static_cast<u32>(depth) - 4 : 0;
|
|
||||||
return renderpass_cache[color_index * MAX_COLOR_FORMATS + depth_index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Backend::BindDescriptorSets(PipelineHandle handle) {
|
void Backend::BindDescriptorSets(PipelineHandle handle) {
|
||||||
Pipeline* pipeline = static_cast<Pipeline*>(handle.Get());
|
Pipeline* pipeline = static_cast<Pipeline*>(handle.Get());
|
||||||
PipelineLayout& pipeline_layout = pipeline->GetOwner();
|
PipelineOwner& pipeline_owner = pipeline->GetOwner();
|
||||||
|
|
||||||
// Allocate required descriptor sets
|
std::array<vk::DescriptorSet, MAX_BINDING_GROUPS> bound_sets;
|
||||||
// TODO: Maybe cache them?
|
const u32 set_count = pipeline_owner.GetDescriptorSetLayoutCount();
|
||||||
u32 pool_index = scheduler.GetCurrentSlotIndex();
|
for (int i = 0; i < set_count; i++) {
|
||||||
const vk::DescriptorSetAllocateInfo alloc_info = {
|
if (!pipeline_owner.descriptor_dirty[i]) {
|
||||||
.descriptorPool = descriptor_pools[pool_index],
|
// Get the ready descriptor if it hasn't been modified
|
||||||
.descriptorSetCount = pipeline_layout.GetDescriptorSetLayoutCount(),
|
bound_sets[i] = pipeline_owner.descriptor_bank[i];
|
||||||
.pSetLayouts = pipeline_layout.GetDescriptorSetLayouts()
|
} else {
|
||||||
};
|
// Otherwise allocate a new set and update it with the needed data
|
||||||
|
u32 pool_index = scheduler.GetCurrentSlotIndex();
|
||||||
|
const vk::DescriptorSetAllocateInfo alloc_info = {
|
||||||
|
.descriptorPool = descriptor_pools[pool_index],
|
||||||
|
.descriptorSetCount = 1,
|
||||||
|
.pSetLayouts = &pipeline_owner.GetDescriptorSetLayouts()[i]
|
||||||
|
};
|
||||||
|
|
||||||
vk::Device device = instance.GetDevice();
|
vk::Device device = instance.GetDevice();
|
||||||
auto descriptor_sets = device.allocateDescriptorSets(alloc_info);
|
vk::DescriptorSet set = device.allocateDescriptorSets(alloc_info)[0];
|
||||||
|
device.updateDescriptorSetWithTemplate(set, pipeline_owner.GetUpdateTemplate(i),
|
||||||
|
pipeline_owner.GetData(i));
|
||||||
|
|
||||||
// Write data to the descriptor sets
|
bound_sets[i] = set;
|
||||||
for (u32 set = 0; set < descriptor_sets.size(); set++) {
|
pipeline_owner.descriptor_bank[i] = set;
|
||||||
device.updateDescriptorSetWithTemplate(descriptor_sets[set],
|
pipeline_owner.descriptor_dirty[i] = false;
|
||||||
pipeline_layout.GetUpdateTemplate(set),
|
}
|
||||||
pipeline_layout.GetData(set));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind the descriptor sets
|
// Bind the descriptor sets
|
||||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||||
command_buffer.bindDescriptorSets(ToVkPipelineBindPoint(handle->GetType()), pipeline_layout.GetLayout(),
|
command_buffer.bindDescriptorSets(ToVkPipelineBindPoint(handle->GetType()), pipeline_owner.GetLayout(),
|
||||||
0, descriptor_sets, {});
|
0, set_count, bound_sets.data(), 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) {
|
||||||
|
const Framebuffer* framebuffer = static_cast<const Framebuffer*>(draw_framebuffer.Get());
|
||||||
|
|
||||||
|
u32 clear_value_count = 0;
|
||||||
|
std::array<vk::ClearValue, 2> clear_values{};
|
||||||
|
|
||||||
|
if (framebuffer->GetColorAttachment().IsValid()) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
clear_values[clear_value_count++].color.float32[i] = framebuffer->clear_color_value[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (framebuffer->GetDepthStencilAttachment().IsValid()) {
|
||||||
|
clear_values[clear_value_count].depthStencil.depth = framebuffer->clear_depth_value;
|
||||||
|
clear_values[clear_value_count++].depthStencil.stencil = framebuffer->clear_stencil_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the clear renderpass if the framebuffer was configured so
|
||||||
|
const vk::RenderPassBeginInfo renderpass_begin = {
|
||||||
|
.renderPass = framebuffer->GetLoadOp() == LoadOp::Load ?
|
||||||
|
framebuffer->GetLoadRenderpass() :
|
||||||
|
framebuffer->GetClearRenderpass(),
|
||||||
|
.framebuffer = framebuffer->GetHandle(),
|
||||||
|
.renderArea = ToVkRect2D(framebuffer->GetDrawRect()),
|
||||||
|
.clearValueCount = clear_value_count,
|
||||||
|
.pClearValues = clear_values.data()
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||||
|
command_buffer.beginRenderPass(renderpass_begin, vk::SubpassContents::eInline);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace VideoCore::Vulkan
|
} // namespace VideoCore::Vulkan
|
||||||
|
@@ -10,13 +10,10 @@
|
|||||||
#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_pipeline.h"
|
#include "video_core/renderer_vulkan/vk_pipeline.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||||
|
|
||||||
namespace VideoCore::Vulkan {
|
namespace VideoCore::Vulkan {
|
||||||
|
|
||||||
class Texture;
|
|
||||||
|
|
||||||
constexpr u32 RENDERPASS_COUNT = (MAX_COLOR_FORMATS + 1) * (MAX_DEPTH_FORMATS + 1);
|
|
||||||
|
|
||||||
class Backend final : public VideoCore::BackendBase {
|
class Backend final : public VideoCore::BackendBase {
|
||||||
public:
|
public:
|
||||||
Backend(Frontend::EmuWindow& window);
|
Backend(Frontend::EmuWindow& window);
|
||||||
@@ -38,8 +35,8 @@ public:
|
|||||||
SamplerHandle CreateSampler(SamplerInfo info) override;
|
SamplerHandle CreateSampler(SamplerInfo info) override;
|
||||||
ShaderHandle CreateShader(ShaderStage stage, std::string_view name, std::string source) override;
|
ShaderHandle CreateShader(ShaderStage stage, std::string_view name, std::string source) override;
|
||||||
|
|
||||||
void BindVertexBuffer(BufferHandle buffer, std::span<const u32> offsets) override;
|
void BindVertexBuffer(BufferHandle buffer, std::span<const u64> offsets) override;
|
||||||
void BindIndexBuffer(BufferHandle buffer, AttribType index_type, u32 offset) override;
|
void BindIndexBuffer(BufferHandle buffer, AttribType index_type, u64 offset) override;
|
||||||
|
|
||||||
void Draw(PipelineHandle pipeline, FramebufferHandle draw_framebuffer,
|
void Draw(PipelineHandle pipeline, FramebufferHandle draw_framebuffer,
|
||||||
u32 base_vertex, u32 num_vertices) override;
|
u32 base_vertex, u32 num_vertices) override;
|
||||||
@@ -61,21 +58,20 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth) const;
|
vk::RenderPass GetRenderPass(TextureFormat color, TextureFormat depth, bool is_clear = false) const;
|
||||||
vk::RenderPass GetRenderPass(TextureFormat color, TextureFormat depth) const;
|
|
||||||
|
|
||||||
// Allocates and binds descriptor sets for the provided pipeline
|
// Allocates and binds descriptor sets for the provided pipeline
|
||||||
void BindDescriptorSets(PipelineHandle pipeline);
|
void BindDescriptorSets(PipelineHandle pipeline);
|
||||||
|
|
||||||
|
// Begins the renderpass for the provided framebuffer
|
||||||
|
void BeginRenderpass(FramebufferHandle framebuffer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Instance instance;
|
Instance instance;
|
||||||
Swapchain swapchain;
|
Swapchain swapchain;
|
||||||
CommandScheduler scheduler;
|
CommandScheduler scheduler;
|
||||||
|
RenderpassCache renderpass_cache;
|
||||||
// The formats Citra uses are limited so we can pre-create
|
vk::PipelineCache pipeline_cache;
|
||||||
// all the renderpasses we will need
|
|
||||||
std::array<vk::RenderPass, RENDERPASS_COUNT> renderpass_cache;
|
|
||||||
vk::PipelineCache cache;
|
|
||||||
|
|
||||||
// A cache of pipeline owners
|
// A cache of pipeline owners
|
||||||
std::unordered_map<PipelineLayoutInfo, PipelineOwner> pipeline_owners;
|
std::unordered_map<PipelineLayoutInfo, PipelineOwner> pipeline_owners;
|
||||||
|
@@ -57,7 +57,7 @@ Framebuffer::~Framebuffer() {
|
|||||||
device.destroyFramebuffer(framebuffer);
|
device.destroyFramebuffer(framebuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Framebuffer::DoClear(Common::Vec4f color, float depth, u8 stencil) {
|
void Framebuffer::DoClear() {
|
||||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||||
|
|
||||||
u32 clear_value_count = 0;
|
u32 clear_value_count = 0;
|
||||||
@@ -65,7 +65,7 @@ void Framebuffer::DoClear(Common::Vec4f color, float depth, u8 stencil) {
|
|||||||
|
|
||||||
if (info.color.IsValid()) {
|
if (info.color.IsValid()) {
|
||||||
vk::ClearColorValue clear_color{};
|
vk::ClearColorValue clear_color{};
|
||||||
std::memcpy(clear_color.float32.data(), color.AsArray(), sizeof(float) * 4);
|
std::memcpy(clear_color.float32.data(), clear_value.color.AsArray(), sizeof(float) * 4);
|
||||||
|
|
||||||
clear_values[clear_value_count++] = vk::ClearValue {
|
clear_values[clear_value_count++] = vk::ClearValue {
|
||||||
.color = clear_color
|
.color = clear_color
|
||||||
@@ -75,8 +75,8 @@ void Framebuffer::DoClear(Common::Vec4f color, float depth, u8 stencil) {
|
|||||||
if (info.depth_stencil.IsValid()) {
|
if (info.depth_stencil.IsValid()) {
|
||||||
clear_values[clear_value_count++] = vk::ClearValue {
|
clear_values[clear_value_count++] = vk::ClearValue {
|
||||||
.depthStencil = vk::ClearDepthStencilValue {
|
.depthStencil = vk::ClearDepthStencilValue {
|
||||||
.depth = depth,
|
.depth = clear_value.depth,
|
||||||
.stencil = stencil
|
.stencil = clear_value.stencil
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -13,12 +13,13 @@ class Instance;
|
|||||||
class CommandScheduler;
|
class CommandScheduler;
|
||||||
|
|
||||||
class Framebuffer : public VideoCore::FramebufferBase {
|
class Framebuffer : public VideoCore::FramebufferBase {
|
||||||
|
friend class Backend;
|
||||||
public:
|
public:
|
||||||
Framebuffer(Instance& instance, CommandScheduler& scheduler, const FramebufferInfo& info,
|
Framebuffer(Instance& instance, CommandScheduler& scheduler, const FramebufferInfo& info,
|
||||||
vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass);
|
vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass);
|
||||||
~Framebuffer() override;
|
~Framebuffer() override;
|
||||||
|
|
||||||
void DoClear(Common::Vec4f color, float depth, u8 stencil) override;
|
void DoClear() override;
|
||||||
|
|
||||||
vk::Framebuffer GetHandle() const {
|
vk::Framebuffer GetHandle() const {
|
||||||
return framebuffer;
|
return framebuffer;
|
||||||
@@ -28,6 +29,20 @@ public:
|
|||||||
return load_renderpass;
|
return load_renderpass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
private:
|
||||||
Instance& instance;
|
Instance& instance;
|
||||||
CommandScheduler& scheduler;
|
CommandScheduler& scheduler;
|
||||||
|
@@ -25,6 +25,7 @@ union DescriptorData {
|
|||||||
* Functions as the "parent" to a group of pipelines that share the same layout
|
* Functions as the "parent" to a group of pipelines that share the same layout
|
||||||
*/
|
*/
|
||||||
class PipelineOwner {
|
class PipelineOwner {
|
||||||
|
friend class Backend;
|
||||||
public:
|
public:
|
||||||
PipelineOwner(Instance& instance, PipelineLayoutInfo info);
|
PipelineOwner(Instance& instance, PipelineLayoutInfo info);
|
||||||
~PipelineOwner();
|
~PipelineOwner();
|
||||||
|
169
src/video_core/renderer_vulkan/vk_renderpass_cache.cpp
Normal file
169
src/video_core/renderer_vulkan/vk_renderpass_cache.cpp
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
// Copyright 2022 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||||
|
|
||||||
|
namespace VideoCore::Vulkan {
|
||||||
|
|
||||||
|
static constexpr std::array color_formats = {
|
||||||
|
vk::Format::eUndefined,
|
||||||
|
vk::Format::eR8G8B8A8Unorm,
|
||||||
|
vk::Format::eR8G8B8Unorm,
|
||||||
|
vk::Format::eR5G5B5A1UnormPack16,
|
||||||
|
vk::Format::eR5G6B5UnormPack16,
|
||||||
|
vk::Format::eR4G4B4A4UnormPack16
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::array depth_stencil_formats = {
|
||||||
|
vk::Format::eUndefined,
|
||||||
|
vk::Format::eD16Unorm,
|
||||||
|
vk::Format::eX8D24UnormPack32,
|
||||||
|
vk::Format::eD24UnormS8Uint,
|
||||||
|
};
|
||||||
|
|
||||||
|
RenderpassCache::RenderpassCache(Instance& instance, Swapchain& swapchain) : instance(instance) {
|
||||||
|
// Pre-create all needed renderpasses by the renderer
|
||||||
|
for (u32 color = 0; color <= MAX_COLOR_FORMATS; color++) {
|
||||||
|
for (u32 depth = 0; depth <= MAX_DEPTH_FORMATS; depth++) {
|
||||||
|
if (color == 0 && depth == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Format color_format = instance.GetFormatAlternative(color_formats[color]);
|
||||||
|
vk::Format depth_stencil_format = instance.GetFormatAlternative(depth_stencil_formats[depth]);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
cached_renderpasses[color][depth][1] = CreateRenderPass(color_format, depth_stencil_format,
|
||||||
|
vk::AttachmentLoadOp::eClear,
|
||||||
|
vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
vk::ImageLayout::eColorAttachmentOptimal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the present renderpass
|
||||||
|
present_renderpass = CreateRenderPass(swapchain.GetSurfaceFormat().format, vk::Format::eUndefined,
|
||||||
|
vk::AttachmentLoadOp::eClear,
|
||||||
|
vk::ImageLayout::eUndefined,
|
||||||
|
vk::ImageLayout::ePresentSrcKHR);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderpassCache::~RenderpassCache() {
|
||||||
|
vk::Device device = instance.GetDevice();
|
||||||
|
for (u32 color = 0; color <= MAX_COLOR_FORMATS; color++) {
|
||||||
|
for (u32 depth = 0; depth <= MAX_DEPTH_FORMATS; depth++) {
|
||||||
|
if (color == 0 && depth == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& load_pass = cached_renderpasses[color][depth][0];
|
||||||
|
auto& clear_pass = cached_renderpasses[color][depth][1];
|
||||||
|
|
||||||
|
// Destroy renderpasses
|
||||||
|
device.destroyRenderPass(load_pass);
|
||||||
|
device.destroyRenderPass(clear_pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device.destroyRenderPass(present_renderpass);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
ASSERT(color_index < MAX_COLOR_FORMATS && depth_index < MAX_DEPTH_FORMATS);
|
||||||
|
return cached_renderpasses[color_index][depth_index][is_clear];
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format depth, vk::AttachmentLoadOp load_op,
|
||||||
|
vk::ImageLayout initial_layout, vk::ImageLayout final_layout) const {
|
||||||
|
// Define attachments
|
||||||
|
|
||||||
|
u32 attachment_count = 0;
|
||||||
|
std::array<vk::AttachmentDescription, 2> attachments;
|
||||||
|
|
||||||
|
bool use_color = false;
|
||||||
|
vk::AttachmentReference color_attachment_ref;
|
||||||
|
bool use_depth = false;
|
||||||
|
vk::AttachmentReference depth_attachment_ref;
|
||||||
|
|
||||||
|
if (color != vk::Format::eUndefined) {
|
||||||
|
attachments[attachment_count++] = vk::AttachmentDescription{
|
||||||
|
.format = color,
|
||||||
|
.loadOp = load_op,
|
||||||
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||||
|
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
|
||||||
|
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
|
||||||
|
.initialLayout = initial_layout,
|
||||||
|
.finalLayout = final_layout
|
||||||
|
};
|
||||||
|
|
||||||
|
color_attachment_ref = vk::AttachmentReference{
|
||||||
|
.attachment = 0,
|
||||||
|
.layout = vk::ImageLayout::eColorAttachmentOptimal
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth != vk::Format::eUndefined) {
|
||||||
|
attachments[attachment_count++] = vk::AttachmentDescription{
|
||||||
|
.format = depth,
|
||||||
|
.loadOp = load_op,
|
||||||
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||||
|
.initialLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal
|
||||||
|
};
|
||||||
|
|
||||||
|
depth_attachment_ref = vk::AttachmentReference{
|
||||||
|
.attachment = 1,
|
||||||
|
.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const vk::SubpassDependency subpass_dependency = {
|
||||||
|
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||||
|
.dstSubpass = 0,
|
||||||
|
.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput |
|
||||||
|
vk::PipelineStageFlagBits::eEarlyFragmentTests,
|
||||||
|
.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput |
|
||||||
|
vk::PipelineStageFlagBits::eEarlyFragmentTests,
|
||||||
|
.srcAccessMask = vk::AccessFlagBits::eNone,
|
||||||
|
.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite |
|
||||||
|
vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||||
|
.dependencyFlags = vk::DependencyFlagBits::eByRegion
|
||||||
|
};
|
||||||
|
|
||||||
|
// We also require only one subpass
|
||||||
|
const vk::SubpassDescription subpass = {
|
||||||
|
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
|
||||||
|
.inputAttachmentCount = 0,
|
||||||
|
.pInputAttachments = nullptr,
|
||||||
|
.colorAttachmentCount = use_color ? 1u : 0u,
|
||||||
|
.pColorAttachments = &color_attachment_ref,
|
||||||
|
.pResolveAttachments = 0,
|
||||||
|
.pDepthStencilAttachment = use_depth ? &depth_attachment_ref : nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::RenderPassCreateInfo renderpass_info = {
|
||||||
|
.attachmentCount = attachment_count,
|
||||||
|
.pAttachments = attachments.data(),
|
||||||
|
.subpassCount = 1,
|
||||||
|
.pSubpasses = &subpass,
|
||||||
|
.dependencyCount = 1,
|
||||||
|
.pDependencies = &subpass_dependency
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the renderpass
|
||||||
|
vk::Device device = instance.GetDevice();
|
||||||
|
return device.createRenderPass(renderpass_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace VideoCore::Vulkan
|
39
src/video_core/renderer_vulkan/vk_renderpass_cache.h
Normal file
39
src/video_core/renderer_vulkan/vk_renderpass_cache.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2022 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "video_core/common/texture.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_common.h"
|
||||||
|
|
||||||
|
namespace VideoCore::Vulkan {
|
||||||
|
|
||||||
|
class Instance;
|
||||||
|
class Swapchain;
|
||||||
|
|
||||||
|
class RenderpassCache {
|
||||||
|
public:
|
||||||
|
RenderpassCache(Instance& instance, Swapchain& swapchain);
|
||||||
|
~RenderpassCache();
|
||||||
|
|
||||||
|
vk::RenderPass GetRenderpass(TextureFormat color, TextureFormat depth, bool is_clear) const;
|
||||||
|
|
||||||
|
// Returns the special swapchain renderpass
|
||||||
|
vk::RenderPass GetPresentRenderpass() const {
|
||||||
|
return present_renderpass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth, vk::AttachmentLoadOp load_op,
|
||||||
|
vk::ImageLayout initial_layout, vk::ImageLayout final_layout) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Instance& instance;
|
||||||
|
|
||||||
|
// Special renderpass used for rendering to the swapchain
|
||||||
|
vk::RenderPass present_renderpass;
|
||||||
|
// [color_format][depth_format][is_clear_pass]
|
||||||
|
vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS][MAX_DEPTH_FORMATS][2];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCore::Vulkan
|
Reference in New Issue
Block a user