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:
|
||||
constexpr bool IsSigned() const { return std::is_signed<T>(); }
|
||||
constexpr std::size_t StartBit() const { return position; }
|
||||
|
@@ -62,6 +62,10 @@ struct AlignedAllocation {
|
||||
template<typename T>
|
||||
class ObjectPool {
|
||||
public:
|
||||
ObjectPool() {
|
||||
vacants.reserve(32);
|
||||
}
|
||||
|
||||
template<typename... P>
|
||||
T* Allocate(P&&... p) {
|
||||
#ifndef OBJECT_POOL_DEBUG
|
||||
|
@@ -112,6 +112,8 @@ add_library(video_core STATIC
|
||||
renderer_vulkan/vk_pipeline.cpp
|
||||
renderer_vulkan/vk_pipeline.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.h
|
||||
renderer_vulkan/vk_shader.cpp
|
||||
|
@@ -59,10 +59,10 @@ public:
|
||||
virtual ShaderHandle CreateShader(ShaderStage stage, std::string_view name, std::string source) = 0;
|
||||
|
||||
// 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
|
||||
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
|
||||
virtual void Draw(PipelineHandle pipeline, FramebufferHandle draw_framebuffer, u32 base_vertex,
|
||||
|
@@ -16,6 +16,12 @@ enum class MSAASamples : u32 {
|
||||
x8
|
||||
};
|
||||
|
||||
// States which operation to perform on the framebuffer attachment during rendering
|
||||
enum class LoadOp : u8 {
|
||||
Load = 0,
|
||||
Clear = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
public:
|
||||
FramebufferBase(const FramebufferInfo& info) : info(info) {}
|
||||
@@ -43,8 +47,8 @@ public:
|
||||
FramebufferBase(const FramebufferBase&) = delete;
|
||||
FramebufferBase& operator=(const FramebufferBase&) = delete;
|
||||
|
||||
// Clears the attachments bound to the framebuffer
|
||||
virtual void DoClear(Common::Vec4f color, float depth, u8 stencil) = 0;
|
||||
// Clears the attachments bound to the framebuffer using the last stored clear value
|
||||
virtual void DoClear() = 0;
|
||||
|
||||
// Returns an immutable reference to the color attachment
|
||||
TextureHandle GetColorAttachment() const {
|
||||
@@ -61,8 +65,22 @@ public:
|
||||
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
|
||||
Common::Rectangle<u32> GetDrawRect() {
|
||||
Common::Rectangle<u32> GetDrawRect() const {
|
||||
return draw_rect;
|
||||
}
|
||||
|
||||
@@ -72,6 +90,10 @@ public:
|
||||
}
|
||||
|
||||
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;
|
||||
FramebufferInfo info;
|
||||
};
|
||||
|
@@ -39,8 +39,6 @@ enum class BindingType : u32 {
|
||||
|
||||
using BindingGroup = BitFieldArray<0, 3, MAX_BINDINGS_IN_GROUP, BindingType>;
|
||||
|
||||
static_assert(sizeof(BindingGroup));
|
||||
|
||||
/**
|
||||
* Describes all the resources used in the pipeline
|
||||
*/
|
||||
@@ -50,8 +48,6 @@ struct PipelineLayoutInfo {
|
||||
u8 push_constant_block_size = 0;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PipelineLayoutInfo));
|
||||
|
||||
/**
|
||||
* The pipeline state is tightly packed with bitfields to reduce
|
||||
* the overhead of hashing as much as possible
|
||||
|
@@ -96,7 +96,7 @@ constexpr u32 UTILITY_GROUP = 0;
|
||||
constexpr u32 TEXTURE_GROUP = 1;
|
||||
|
||||
// Rasterizer pipeline layout
|
||||
static constexpr PipelineLayoutInfo RASTERIZER_PIPELINE_INFO = {
|
||||
constexpr PipelineLayoutInfo RASTERIZER_PIPELINE_LAYOUT = {
|
||||
.group_count = 3,
|
||||
.binding_groups = {
|
||||
// Uniform + LUT set
|
||||
@@ -192,6 +192,10 @@ Rasterizer::Rasterizer(Frontend::EmuWindow& emu_window, std::unique_ptr<BackendB
|
||||
// Create pipeline cache
|
||||
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
|
||||
SyncEntireState();
|
||||
}
|
||||
@@ -348,7 +352,7 @@ void Rasterizer::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_min, u32
|
||||
|
||||
VertexLayout layout{};
|
||||
std::array<bool, 16> enable_attributes{};
|
||||
std::array<u32, 16> binding_offsets{};
|
||||
std::array<u64, 16> binding_offsets{};
|
||||
|
||||
u32 buffer_offset = 0;
|
||||
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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -587,6 +591,8 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
// Retrieve the framebuffer assigned to the surfaces and update raster_info
|
||||
FramebufferHandle framebuffer = res_cache.GetFramebuffer(color_surface, depth_surface);
|
||||
framebuffer->SetLoadOp(LoadOp::Load);
|
||||
|
||||
raster_info.color_attachment = framebuffer->GetColorAttachment().IsValid() ?
|
||||
framebuffer->GetColorAttachment()->GetFormat() :
|
||||
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
|
||||
// that when base_vertex is zero the GPU will start drawing from the current mapped
|
||||
// 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);
|
||||
|
||||
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);
|
||||
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) {
|
||||
u32 depth_32bit = 0;
|
||||
float depth_float;
|
||||
@@ -278,7 +278,7 @@ bool RasterizerCache::FillSurface(const Surface& surface, const u8* fill_data, C
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
framebuffer->DoClear({}, depth_float, 0);
|
||||
framebuffer->SetClearValues({}, depth_float, 0);
|
||||
} else if (surface->type == SurfaceType::DepthStencil) {
|
||||
u32 value_32bit;
|
||||
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
|
||||
u8 stencil_int = (value_32bit >> 24);
|
||||
|
||||
framebuffer->DoClear({}, depth_float, stencil_int);
|
||||
framebuffer->SetClearValues({}, depth_float, stencil_int);
|
||||
}
|
||||
|
||||
framebuffer->DoClear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -134,7 +134,7 @@ constexpr VertexLayout ScreenRectVertex::GetVertexLayout() {
|
||||
}
|
||||
|
||||
// Renderer pipeline layout
|
||||
static constexpr PipelineLayoutInfo RENDERER_PIPELINE_INFO = {
|
||||
static constexpr PipelineLayoutInfo RENDERER_PIPELINE_LAYOUT = {
|
||||
.group_count = 2,
|
||||
.binding_groups = {
|
||||
BindingGroup{
|
||||
@@ -167,7 +167,7 @@ DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(wi
|
||||
|
||||
PipelineInfo present_pipeline_info = {
|
||||
.vertex_layout = ScreenRectVertex::GetVertexLayout(),
|
||||
.layout = RENDERER_PIPELINE_INFO,
|
||||
.layout = RENDERER_PIPELINE_LAYOUT,
|
||||
.color_attachment = TextureFormat::PresentColor,
|
||||
.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 auto& texcoords = screen_info.display_texcoords;
|
||||
|
||||
// Clear the swapchain framebuffer
|
||||
// Set the swapchain framebuffer to clear mode
|
||||
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
|
||||
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 mapped_offset = vertex_buffer->GetCurrentOffset();
|
||||
const u64 mapped_offset = vertex_buffer->GetCurrentOffset();
|
||||
auto vertex_data = vertex_buffer->Map(size);
|
||||
|
||||
// Copy vertex data
|
||||
|
@@ -42,7 +42,7 @@ constexpr vk::IndexType ToVkIndexType(AttribType type) {
|
||||
|
||||
Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
||||
instance(window), swapchain(instance, instance.GetSurface()),
|
||||
scheduler(instance) {
|
||||
scheduler(instance), renderpass_cache(instance, swapchain) {
|
||||
|
||||
// TODO: Properly report GPU hardware
|
||||
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_Vulkan_Version", "Vulkan 1.3");
|
||||
|
||||
// Pre-create all needed renderpasses by the renderer
|
||||
constexpr std::array color_formats = {
|
||||
vk::Format::eUndefined,
|
||||
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]);
|
||||
}
|
||||
}
|
||||
// Create pipeline cache object
|
||||
vk::Device device = instance.GetDevice();
|
||||
pipeline_cache = device.createPipelineCache({});
|
||||
|
||||
constexpr std::array pool_sizes = {
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eUniformBuffer, 1024},
|
||||
@@ -93,7 +70,6 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
||||
};
|
||||
|
||||
// Create descriptor pools
|
||||
vk::Device device = instance.GetDevice();
|
||||
for (u32 pool = 0; pool < SCHEDULER_COMMAND_COUNT; pool++) {
|
||||
descriptor_pools[pool] = device.createDescriptorPool(pool_info);
|
||||
}
|
||||
@@ -101,8 +77,10 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
||||
|
||||
Backend::~Backend() {
|
||||
vk::Device device = instance.GetDevice();
|
||||
for (auto& renderpass : renderpass_cache) {
|
||||
device.destroyRenderPass(renderpass);
|
||||
device.destroyPipelineCache(pipeline_cache);
|
||||
|
||||
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
|
||||
TextureFormat color = info.color.IsValid() ? info.color->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) {
|
||||
@@ -152,12 +131,14 @@ PipelineHandle Backend::CreatePipeline(PipelineType type, PipelineInfo info) {
|
||||
|
||||
// Find an owner first
|
||||
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
|
||||
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) {
|
||||
@@ -165,30 +146,35 @@ SamplerHandle Backend::CreateSampler(SamplerInfo 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,
|
||||
u32 base_vertex, u32 num_vertices) {
|
||||
|
||||
// Bind descriptor sets
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
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
|
||||
const Framebuffer* framebuffer = static_cast<const Framebuffer*>(draw_framebuffer.Get());
|
||||
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);
|
||||
BeginRenderpass(draw_framebuffer);
|
||||
|
||||
// Bind pipeline
|
||||
const Pipeline* pipeline = static_cast<const Pipeline*>(pipeline_handle.Get());
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.bindPipeline(ToVkPipelineBindPoint(pipeline->GetType()), pipeline->GetHandle());
|
||||
|
||||
// Submit draw
|
||||
@@ -199,34 +185,16 @@ void Backend::Draw(PipelineHandle pipeline_handle, FramebufferHandle draw_frameb
|
||||
}
|
||||
|
||||
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) {
|
||||
// Bind descriptor sets
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
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
|
||||
const Framebuffer* framebuffer = static_cast<const Framebuffer*>(draw_framebuffer.Get());
|
||||
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);
|
||||
BeginRenderpass(draw_framebuffer);
|
||||
|
||||
// Bind pipeline
|
||||
const Pipeline* pipeline = static_cast<const Pipeline*>(pipeline_handle.Get());
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.bindPipeline(ToVkPipelineBindPoint(pipeline->GetType()), pipeline->GetHandle());
|
||||
|
||||
// Submit draw
|
||||
@@ -237,106 +205,80 @@ void Backend::DrawIndexed(PipelineHandle pipeline_handle, FramebufferHandle draw
|
||||
|
||||
}
|
||||
|
||||
|
||||
vk::RenderPass Backend::CreateRenderPass(vk::Format color, vk::Format depth) const {
|
||||
// Define attachments
|
||||
const std::array attachments = {
|
||||
vk::AttachmentDescription{
|
||||
.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];
|
||||
vk::RenderPass Backend::GetRenderPass(TextureFormat color, TextureFormat depth, bool is_clear) const {
|
||||
if (color == TextureFormat::PresentColor) {
|
||||
return renderpass_cache.GetPresentRenderpass();
|
||||
} else {
|
||||
return renderpass_cache.GetRenderpass(color, depth, is_clear);
|
||||
}
|
||||
}
|
||||
|
||||
void Backend::BindDescriptorSets(PipelineHandle handle) {
|
||||
Pipeline* pipeline = static_cast<Pipeline*>(handle.Get());
|
||||
PipelineLayout& pipeline_layout = pipeline->GetOwner();
|
||||
PipelineOwner& pipeline_owner = pipeline->GetOwner();
|
||||
|
||||
// Allocate required descriptor sets
|
||||
// TODO: Maybe cache them?
|
||||
u32 pool_index = scheduler.GetCurrentSlotIndex();
|
||||
const vk::DescriptorSetAllocateInfo alloc_info = {
|
||||
.descriptorPool = descriptor_pools[pool_index],
|
||||
.descriptorSetCount = pipeline_layout.GetDescriptorSetLayoutCount(),
|
||||
.pSetLayouts = pipeline_layout.GetDescriptorSetLayouts()
|
||||
};
|
||||
std::array<vk::DescriptorSet, MAX_BINDING_GROUPS> bound_sets;
|
||||
const u32 set_count = pipeline_owner.GetDescriptorSetLayoutCount();
|
||||
for (int i = 0; i < set_count; i++) {
|
||||
if (!pipeline_owner.descriptor_dirty[i]) {
|
||||
// Get the ready descriptor if it hasn't been modified
|
||||
bound_sets[i] = pipeline_owner.descriptor_bank[i];
|
||||
} 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();
|
||||
auto descriptor_sets = device.allocateDescriptorSets(alloc_info);
|
||||
vk::Device device = instance.GetDevice();
|
||||
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
|
||||
for (u32 set = 0; set < descriptor_sets.size(); set++) {
|
||||
device.updateDescriptorSetWithTemplate(descriptor_sets[set],
|
||||
pipeline_layout.GetUpdateTemplate(set),
|
||||
pipeline_layout.GetData(set));
|
||||
bound_sets[i] = set;
|
||||
pipeline_owner.descriptor_bank[i] = set;
|
||||
pipeline_owner.descriptor_dirty[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the descriptor sets
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
command_buffer.bindDescriptorSets(ToVkPipelineBindPoint(handle->GetType()), pipeline_layout.GetLayout(),
|
||||
0, descriptor_sets, {});
|
||||
command_buffer.bindDescriptorSets(ToVkPipelineBindPoint(handle->GetType()), pipeline_owner.GetLayout(),
|
||||
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
|
||||
|
@@ -10,13 +10,10 @@
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline.h"
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
|
||||
namespace VideoCore::Vulkan {
|
||||
|
||||
class Texture;
|
||||
|
||||
constexpr u32 RENDERPASS_COUNT = (MAX_COLOR_FORMATS + 1) * (MAX_DEPTH_FORMATS + 1);
|
||||
|
||||
class Backend final : public VideoCore::BackendBase {
|
||||
public:
|
||||
Backend(Frontend::EmuWindow& window);
|
||||
@@ -38,8 +35,8 @@ public:
|
||||
SamplerHandle CreateSampler(SamplerInfo info) override;
|
||||
ShaderHandle CreateShader(ShaderStage stage, std::string_view name, std::string source) override;
|
||||
|
||||
void BindVertexBuffer(BufferHandle buffer, std::span<const u32> offsets) override;
|
||||
void BindIndexBuffer(BufferHandle buffer, AttribType index_type, u32 offset) override;
|
||||
void BindVertexBuffer(BufferHandle buffer, std::span<const u64> offsets) override;
|
||||
void BindIndexBuffer(BufferHandle buffer, AttribType index_type, u64 offset) override;
|
||||
|
||||
void Draw(PipelineHandle pipeline, FramebufferHandle draw_framebuffer,
|
||||
u32 base_vertex, u32 num_vertices) override;
|
||||
@@ -61,21 +58,20 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth) const;
|
||||
vk::RenderPass GetRenderPass(TextureFormat color, TextureFormat depth) const;
|
||||
vk::RenderPass GetRenderPass(TextureFormat color, TextureFormat depth, bool is_clear = false) const;
|
||||
|
||||
// Allocates and binds descriptor sets for the provided pipeline
|
||||
void BindDescriptorSets(PipelineHandle pipeline);
|
||||
|
||||
// Begins the renderpass for the provided framebuffer
|
||||
void BeginRenderpass(FramebufferHandle framebuffer);
|
||||
|
||||
private:
|
||||
Instance instance;
|
||||
Swapchain swapchain;
|
||||
CommandScheduler scheduler;
|
||||
|
||||
// The formats Citra uses are limited so we can pre-create
|
||||
// all the renderpasses we will need
|
||||
std::array<vk::RenderPass, RENDERPASS_COUNT> renderpass_cache;
|
||||
vk::PipelineCache cache;
|
||||
RenderpassCache renderpass_cache;
|
||||
vk::PipelineCache pipeline_cache;
|
||||
|
||||
// A cache of pipeline owners
|
||||
std::unordered_map<PipelineLayoutInfo, PipelineOwner> pipeline_owners;
|
||||
|
@@ -57,7 +57,7 @@ Framebuffer::~Framebuffer() {
|
||||
device.destroyFramebuffer(framebuffer);
|
||||
}
|
||||
|
||||
void Framebuffer::DoClear(Common::Vec4f color, float depth, u8 stencil) {
|
||||
void Framebuffer::DoClear() {
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
|
||||
u32 clear_value_count = 0;
|
||||
@@ -65,7 +65,7 @@ void Framebuffer::DoClear(Common::Vec4f color, float depth, u8 stencil) {
|
||||
|
||||
if (info.color.IsValid()) {
|
||||
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 {
|
||||
.color = clear_color
|
||||
@@ -75,8 +75,8 @@ void Framebuffer::DoClear(Common::Vec4f color, float depth, u8 stencil) {
|
||||
if (info.depth_stencil.IsValid()) {
|
||||
clear_values[clear_value_count++] = vk::ClearValue {
|
||||
.depthStencil = vk::ClearDepthStencilValue {
|
||||
.depth = depth,
|
||||
.stencil = stencil
|
||||
.depth = clear_value.depth,
|
||||
.stencil = clear_value.stencil
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -13,12 +13,13 @@ class Instance;
|
||||
class CommandScheduler;
|
||||
|
||||
class Framebuffer : public VideoCore::FramebufferBase {
|
||||
friend class Backend;
|
||||
public:
|
||||
Framebuffer(Instance& instance, CommandScheduler& scheduler, const FramebufferInfo& info,
|
||||
vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass);
|
||||
~Framebuffer() override;
|
||||
|
||||
void DoClear(Common::Vec4f color, float depth, u8 stencil) override;
|
||||
void DoClear() override;
|
||||
|
||||
vk::Framebuffer GetHandle() const {
|
||||
return framebuffer;
|
||||
@@ -28,6 +29,20 @@ public:
|
||||
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:
|
||||
Instance& instance;
|
||||
CommandScheduler& scheduler;
|
||||
|
@@ -25,6 +25,7 @@ union DescriptorData {
|
||||
* Functions as the "parent" to a group of pipelines that share the same layout
|
||||
*/
|
||||
class PipelineOwner {
|
||||
friend class Backend;
|
||||
public:
|
||||
PipelineOwner(Instance& instance, PipelineLayoutInfo info);
|
||||
~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