renderer_vulkan: Remove DescriptorSetProvider

This commit is contained in:
GPUCode 2023-11-15 20:27:59 +02:00
parent e28c2a390c
commit ed699f1e2b
5 changed files with 156 additions and 104 deletions

View File

@ -7,7 +7,6 @@
#include "common/memory_detect.h"
#include "common/microprofile.h"
#include "common/settings.h"
#include "common/texture.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
#include "core/hw/gpu.h"
@ -21,7 +20,6 @@
#include "video_core/host_shaders/vulkan_present_frag_spv.h"
#include "video_core/host_shaders/vulkan_present_interlaced_frag_spv.h"
#include "video_core/host_shaders/vulkan_present_vert_spv.h"
#include "vulkan/vulkan_format_traits.hpp"
#include <vk_mem_alloc.h>
@ -49,10 +47,6 @@ constexpr std::array<f32, 4 * 4> MakeOrthographicMatrix(u32 width, u32 height) {
// clang-format on
}
constexpr static std::array<vk::DescriptorSetLayoutBinding, 1> PRESENT_BINDINGS = {{
{0, vk::DescriptorType::eCombinedImageSampler, 3, vk::ShaderStageFlagBits::eFragment},
}};
RendererVulkan::RendererVulkan(Core::System& system, Frontend::EmuWindow& window,
Frontend::EmuWindow* secondary_window)
: RendererBase{system, window, secondary_window}, memory{system.Memory()},
@ -69,10 +63,9 @@ RendererVulkan::RendererVulkan(Core::System& system, Frontend::EmuWindow& window
scheduler,
pool,
renderpass_cache,
main_window.ImageCount()},
present_set_provider{instance, pool, PRESENT_BINDINGS} {
main_window.ImageCount()} {
CompileShaders();
BuildLayouts();
BuildLayoutsAndDescriptors();
BuildPipelines();
if (secondary_window) {
second_window = std::make_unique<PresentWindow>(*secondary_window, instance, scheduler);
@ -80,17 +73,22 @@ RendererVulkan::RendererVulkan(Core::System& system, Frontend::EmuWindow& window
}
RendererVulkan::~RendererVulkan() {
vk::Device device = instance.GetDevice();
const vk::Device device = instance.GetDevice();
scheduler.Finish();
device.waitIdle();
device.destroyShaderModule(present_vertex_shader);
for (u32 i = 0; i < PRESENT_PIPELINES; i++) {
device.destroyPipeline(present_pipelines[i]);
device.destroyShaderModule(present_shaders[i]);
device.destroyPipelineLayout(pipeline_layout);
device.destroyDescriptorPool(descriptor_pool);
device.destroyDescriptorSetLayout(descriptor_set_layout);
device.destroyDescriptorUpdateTemplate(update_template);
device.destroyShaderModule(vert_shader);
for (u32 i = 0; i < NUM_PIPELINES; i++) {
device.destroyPipeline(pipelines[i]);
device.destroyShaderModule(frag_shaders[i]);
}
for (auto& sampler : present_samplers) {
for (auto sampler : samplers) {
device.destroySampler(sampler);
}
@ -105,9 +103,10 @@ void RendererVulkan::Sync() {
}
void RendererVulkan::PrepareRendertarget() {
for (u32 i = 0; i < 3; i++) {
for (u32 i = 0; i < NUM_SCREENS; i++) {
const u32 fb_id = i == 2 ? 1 : 0;
const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id];
auto& texture = screen_infos[i].texture;
// Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04
u32 lcd_color_addr =
@ -118,37 +117,31 @@ void RendererVulkan::PrepareRendertarget() {
if (color_fill.is_enabled) {
LoadColorToActiveVkTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b,
screen_infos[i].texture);
} else {
TextureInfo& texture = screen_infos[i].texture;
if (texture.width != framebuffer.width || texture.height != framebuffer.height ||
texture.format != framebuffer.color_format) {
// Reallocate texture if the framebuffer size has changed.
// This is expected to not happen very often and hence should not be a
// performance problem.
ConfigureFramebufferTexture(texture, framebuffer);
}
LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1);
// Resize the texture in case the framebuffer size has changed
texture.width = framebuffer.width;
texture.height = framebuffer.height;
texture);
continue;
}
if (texture.width != framebuffer.width || texture.height != framebuffer.height ||
texture.format != framebuffer.color_format) {
ConfigureFramebufferTexture(texture, framebuffer);
}
LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1);
}
}
void RendererVulkan::PrepareDraw(Frame* frame, const Layout::FramebufferLayout& layout) {
const auto sampler = present_samplers[!Settings::values.filter_mode.GetValue()];
std::transform(screen_infos.begin(), screen_infos.end(), present_textures.begin(),
[&](auto& info) {
return DescriptorData{vk::DescriptorImageInfo{sampler, info.image_view,
vk::ImageLayout::eGeneral}};
});
const auto sampler = samplers[!Settings::values.filter_mode.GetValue()];
std::transform(screen_infos.begin(), screen_infos.end(), image_infos.begin(), [&](auto& info) {
return vk::DescriptorImageInfo{sampler, info.image_view, vk::ImageLayout::eGeneral};
});
const auto descriptor_set = present_set_provider.Acquire(present_textures);
// Prepare the descriptor set with presentation images.
const auto descriptor_set = present_sets[frame->index];
const vk::Device device = instance.GetDevice();
device.updateDescriptorSetWithTemplate(descriptor_set, update_template, image_infos);
// Bind presentation pipeline, enter renderpass.
renderpass_cache.EndRendering();
scheduler.Record([this, layout, frame, descriptor_set, renderpass = main_window.Renderpass(),
index = current_pipeline](vk::CommandBuffer cmdbuf) {
@ -170,12 +163,11 @@ void RendererVulkan::PrepareDraw(Frame* frame, const Layout::FramebufferLayout&
cmdbuf.setScissor(0, scissor);
const vk::ClearValue clear{.color = clear_color};
const vk::PipelineLayout layout{*present_pipeline_layout};
const vk::RenderPassBeginInfo renderpass_begin_info = {
.renderPass = renderpass,
.framebuffer = frame->framebuffer,
.renderArea =
vk::Rect2D{
{
.offset = {0, 0},
.extent = {frame->width, frame->height},
},
@ -184,8 +176,9 @@ void RendererVulkan::PrepareDraw(Frame* frame, const Layout::FramebufferLayout&
};
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, present_pipelines[index]);
cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, descriptor_set, {});
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipelines[index]);
cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0,
descriptor_set, {});
});
}
@ -239,13 +232,13 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
void RendererVulkan::CompileShaders() {
vk::Device device = instance.GetDevice();
present_vertex_shader = CompileSPV(VULKAN_PRESENT_VERT_SPV, device);
present_shaders[0] = CompileSPV(VULKAN_PRESENT_FRAG_SPV, device);
present_shaders[1] = CompileSPV(VULKAN_PRESENT_ANAGLYPH_FRAG_SPV, device);
present_shaders[2] = CompileSPV(VULKAN_PRESENT_INTERLACED_FRAG_SPV, device);
vert_shader = CompileSPV(VULKAN_PRESENT_VERT_SPV, device);
frag_shaders[0] = CompileSPV(VULKAN_PRESENT_FRAG_SPV, device);
frag_shaders[1] = CompileSPV(VULKAN_PRESENT_ANAGLYPH_FRAG_SPV, device);
frag_shaders[2] = CompileSPV(VULKAN_PRESENT_INTERLACED_FRAG_SPV, device);
auto properties = instance.GetPhysicalDevice().getProperties();
for (std::size_t i = 0; i < present_samplers.size(); i++) {
const auto properties = instance.GetPhysicalDevice().getProperties();
for (std::size_t i = 0; i < samplers.size(); i++) {
const vk::Filter filter_mode = i == 0 ? vk::Filter::eLinear : vk::Filter::eNearest;
const vk::SamplerCreateInfo sampler_info = {
.magFilter = filter_mode,
@ -261,25 +254,81 @@ void RendererVulkan::CompileShaders() {
.unnormalizedCoordinates = false,
};
present_samplers[i] = device.createSampler(sampler_info);
samplers[i] = device.createSampler(sampler_info);
}
}
void RendererVulkan::BuildLayouts() {
void RendererVulkan::BuildLayoutsAndDescriptors() {
const vk::PushConstantRange push_range = {
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
.offset = 0,
.size = sizeof(PresentUniformData),
};
const auto descriptor_set_layout = present_set_provider.Layout();
const vk::PipelineLayoutCreateInfo layout_info = {
const vk::DescriptorSetLayoutBinding binding = {
.binding = 0,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 3,
.stageFlags = vk::ShaderStageFlagBits::eFragment,
};
const vk::DescriptorUpdateTemplateEntry update_entry = {
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 3,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.offset = 0,
.stride = sizeof(vk::DescriptorImageInfo),
};
const vk::DescriptorSetLayoutCreateInfo layout_info = {
.bindingCount = 1,
.pBindings = &binding,
};
const vk::Device device = instance.GetDevice();
descriptor_set_layout = device.createDescriptorSetLayout(layout_info);
const vk::DescriptorUpdateTemplateCreateInfo template_info = {
.descriptorUpdateEntryCount = 1,
.pDescriptorUpdateEntries = &update_entry,
.templateType = vk::DescriptorUpdateTemplateType::eDescriptorSet,
.descriptorSetLayout = descriptor_set_layout,
};
update_template = device.createDescriptorUpdateTemplate(template_info);
const vk::PipelineLayoutCreateInfo pipeline_layout_info = {
.setLayoutCount = 1,
.pSetLayouts = &descriptor_set_layout,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &push_range,
};
present_pipeline_layout = instance.GetDevice().createPipelineLayoutUnique(layout_info);
pipeline_layout = device.createPipelineLayout(pipeline_layout_info);
const u32 image_count = main_window.ImageCount();
const vk::DescriptorPoolSize pool_size = {
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = binding.descriptorCount * image_count,
};
const vk::DescriptorPoolCreateInfo descriptor_pool_info = {
.maxSets = image_count,
.poolSizeCount = 1,
.pPoolSizes = &pool_size,
};
descriptor_pool = device.createDescriptorPool(descriptor_pool_info);
std::array<vk::DescriptorSetLayout, 3> layouts;
layouts.fill(descriptor_set_layout);
const vk::DescriptorSetAllocateInfo alloc_info = {
.descriptorPool = descriptor_pool,
.descriptorSetCount = image_count,
.pSetLayouts = layouts.data(),
};
present_sets = device.allocateDescriptorSets(alloc_info);
}
void RendererVulkan::BuildPipelines() {
@ -370,16 +419,16 @@ void RendererVulkan::BuildPipelines() {
.stencilTestEnable = false,
};
for (u32 i = 0; i < PRESENT_PIPELINES; i++) {
for (u32 i = 0; i < NUM_PIPELINES; i++) {
const std::array shader_stages = {
vk::PipelineShaderStageCreateInfo{
.stage = vk::ShaderStageFlagBits::eVertex,
.module = present_vertex_shader,
.module = vert_shader,
.pName = "main",
},
vk::PipelineShaderStageCreateInfo{
.stage = vk::ShaderStageFlagBits::eFragment,
.module = present_shaders[i],
.module = frag_shaders[i],
.pName = "main",
},
};
@ -395,14 +444,14 @@ void RendererVulkan::BuildPipelines() {
.pDepthStencilState = &depth_info,
.pColorBlendState = &color_blending,
.pDynamicState = &dynamic_info,
.layout = *present_pipeline_layout,
.layout = pipeline_layout,
.renderPass = main_window.Renderpass(),
};
const auto [result, pipeline] =
instance.GetDevice().createGraphicsPipeline({}, pipeline_info);
ASSERT_MSG(result == vk::Result::eSuccess, "Unable to build present pipelines");
present_pipelines[i] = pipeline;
pipelines[i] = pipeline;
}
}
@ -416,8 +465,7 @@ void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture,
vmaDestroyImage(instance.GetAllocator(), texture.image, texture.allocation);
}
const VideoCore::PixelFormat pixel_format =
VideoCore::PixelFormatFromGPUPixelFormat(framebuffer.color_format);
const auto pixel_format = VideoCore::PixelFormatFromGPUPixelFormat(framebuffer.color_format);
const vk::Format format = instance.GetTraits(pixel_format).native;
const vk::ImageCreateInfo image_info = {
.imageType = vk::ImageType::e2D,
@ -603,7 +651,7 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float x, float y, float w,
scheduler.Record([this, offset = offset, info = draw_info](vk::CommandBuffer cmdbuf) {
const u32 first_vertex = static_cast<u32>(offset) / sizeof(ScreenRectVertex);
cmdbuf.pushConstants(*present_pipeline_layout,
cmdbuf.pushConstants(pipeline_layout,
vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex,
0, sizeof(info), &info);
@ -676,7 +724,7 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, fl
scheduler.Record([this, offset = offset, info = draw_info](vk::CommandBuffer cmdbuf) {
const u32 first_vertex = static_cast<u32>(offset) / sizeof(ScreenRectVertex);
cmdbuf.pushConstants(*present_pipeline_layout,
cmdbuf.pushConstants(pipeline_layout,
vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex,
0, sizeof(info), &info);
@ -1032,9 +1080,8 @@ bool RendererVulkan::TryRenderScreenshotWithHostMemory() {
.imageExtent = {width, height, 1},
};
const vk::MemoryHostPointerPropertiesEXT import_properties =
device.getMemoryHostPointerPropertiesEXT(
vk::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT, aligned_pointer);
const auto import_properties = device.getMemoryHostPointerPropertiesEXT(
vk::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT, aligned_pointer);
if (!import_properties.memoryTypeBits) {
// Could not import memory
@ -1051,33 +1098,31 @@ bool RendererVulkan::TryRenderScreenshotWithHostMemory() {
return false;
}
const vk::StructureChain<vk::MemoryAllocateInfo, vk::ImportMemoryHostPointerInfoEXT>
allocation_chain = {
vk::MemoryAllocateInfo{
.allocationSize = aligned_size,
.memoryTypeIndex = memory_type_index.value(),
},
vk::ImportMemoryHostPointerInfoEXT{
.handleType = vk::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT,
.pHostPointer = aligned_pointer,
},
};
const vk::StructureChain allocation_chain = {
vk::MemoryAllocateInfo{
.allocationSize = aligned_size,
.memoryTypeIndex = memory_type_index.value(),
},
vk::ImportMemoryHostPointerInfoEXT{
.handleType = vk::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT,
.pHostPointer = aligned_pointer,
},
};
// Import host memory
const vk::UniqueDeviceMemory imported_memory =
device.allocateMemoryUnique(allocation_chain.get());
const vk::StructureChain<vk::BufferCreateInfo, vk::ExternalMemoryBufferCreateInfo> buffer_info =
{
vk::BufferCreateInfo{
.size = aligned_size,
.usage = vk::BufferUsageFlagBits::eTransferDst,
.sharingMode = vk::SharingMode::eExclusive,
},
vk::ExternalMemoryBufferCreateInfo{
.handleTypes = vk::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT,
},
};
const vk::StructureChain buffer_info = {
vk::BufferCreateInfo{
.size = aligned_size,
.usage = vk::BufferUsageFlagBits::eTransferDst,
.sharingMode = vk::SharingMode::eExclusive,
},
vk::ExternalMemoryBufferCreateInfo{
.handleTypes = vk::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT,
},
};
// Bind imported memory to buffer
const vk::UniqueBuffer imported_buffer = device.createBufferUnique(buffer_info.get());

View File

@ -61,7 +61,8 @@ static_assert(sizeof(PresentUniformData) == 112,
"PresentUniformData does not structure in shader!");
class RendererVulkan : public VideoCore::RendererBase {
static constexpr std::size_t PRESENT_PIPELINES = 3;
static constexpr std::size_t NUM_PIPELINES = 3;
static constexpr std::size_t NUM_SCREENS = 3;
public:
explicit RendererVulkan(Core::System& system, Frontend::EmuWindow& window,
@ -83,7 +84,7 @@ public:
private:
void ReloadPipeline();
void CompileShaders();
void BuildLayouts();
void BuildLayoutsAndDescriptors();
void BuildPipelines();
void ConfigureFramebufferTexture(TextureInfo& texture,
const GPU::Regs::FramebufferConfig& framebuffer);
@ -121,16 +122,19 @@ private:
RasterizerVulkan rasterizer;
std::unique_ptr<PresentWindow> second_window;
vk::UniquePipelineLayout present_pipeline_layout;
DescriptorSetProvider present_set_provider;
std::array<vk::Pipeline, PRESENT_PIPELINES> present_pipelines;
std::array<vk::ShaderModule, PRESENT_PIPELINES> present_shaders;
std::array<vk::Sampler, 2> present_samplers;
vk::ShaderModule present_vertex_shader;
vk::PipelineLayout pipeline_layout;
vk::DescriptorPool descriptor_pool;
vk::DescriptorSetLayout descriptor_set_layout;
vk::DescriptorUpdateTemplate update_template;
std::vector<vk::DescriptorSet> present_sets;
std::array<vk::Pipeline, NUM_PIPELINES> pipelines;
std::array<vk::ShaderModule, NUM_PIPELINES> frag_shaders;
std::array<vk::Sampler, 2> samplers;
vk::ShaderModule vert_shader;
u32 current_pipeline = 0;
std::array<ScreenInfo, 3> screen_infos{};
std::array<DescriptorData, 3> present_textures{};
std::array<ScreenInfo, NUM_SCREENS> screen_infos{};
std::array<vk::DescriptorImageInfo, NUM_SCREENS> image_infos{};
PresentUniformData draw_info{};
vk::ClearColorValue clear_color{};
};

View File

@ -130,6 +130,7 @@ PresentWindow::PresentWindow(Frontend::EmuWindow& emu_window_, const Instance& i
for (u32 i = 0; i < num_images; i++) {
Frame& frame = swap_chain[i];
frame.cmdbuf = command_buffers[i];
frame.index = i;
frame.render_ready = device.createSemaphore({});
frame.present_done = device.createFence({.flags = vk::FenceCreateFlagBits::eSignaled});
free_queue.push(&frame);

View File

@ -4,7 +4,6 @@
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include "common/polyfill_thread.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
@ -25,6 +24,7 @@ class RenderpassCache;
struct Frame {
u32 width;
u32 height;
u32 index;
VmaAllocation allocation;
vk::Framebuffer framebuffer;
vk::Image image;
@ -55,19 +55,17 @@ public:
/// This is called to notify the rendering backend of a surface change
void NotifySurfaceChanged();
[[nodiscard]] vk::RenderPass Renderpass() const noexcept {
vk::RenderPass Renderpass() const noexcept {
return present_renderpass;
}
u32 ImageCount() const noexcept {
return swapchain.GetImageCount();
return swap_chain.size();
}
private:
void PresentThread(std::stop_token token);
void CopyToSwapchain(Frame* frame);
vk::RenderPass CreateRenderpass();
private:

View File

@ -56,6 +56,10 @@ public:
return image_count;
}
u32 GetImageIndex() const {
return image_index;
}
vk::Extent2D GetExtent() const {
return extent;
}