video_core: Even more fixes, close to building now

This commit is contained in:
emufan4568
2022-08-10 19:47:40 +03:00
parent dade6e6797
commit 9200731b56
16 changed files with 394 additions and 208 deletions

View File

@@ -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; }

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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;
}; };

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
} }

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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
} }
}; };
} }

View File

@@ -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;

View File

@@ -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();

View 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

View 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