renderer_vulkan: Abstract descriptor management

* The pipeline cache was starting to get cluttered
This commit is contained in:
emufan4568
2022-10-17 19:36:03 +03:00
committed by GPUCode
parent 8faa7a6e02
commit c78847b2b6
6 changed files with 265 additions and 207 deletions

View File

@ -88,6 +88,8 @@ add_library(video_core STATIC
renderer_vulkan/vk_blit_helper.h renderer_vulkan/vk_blit_helper.h
renderer_vulkan/vk_common.cpp renderer_vulkan/vk_common.cpp
renderer_vulkan/vk_common.h renderer_vulkan/vk_common.h
renderer_vulkan/vk_descriptor_manager.cpp
renderer_vulkan/vk_descriptor_manager.h
renderer_vulkan/vk_format_reinterpreter.cpp renderer_vulkan/vk_format_reinterpreter.cpp
renderer_vulkan/vk_format_reinterpreter.h renderer_vulkan/vk_format_reinterpreter.h
renderer_vulkan/vk_layout_tracker.h renderer_vulkan/vk_layout_tracker.h

View File

@ -10,6 +10,8 @@
#define VK_NO_PROTOTYPES 1 #define VK_NO_PROTOTYPES 1
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
#define VULKAN_HPP_NO_CONSTRUCTORS #define VULKAN_HPP_NO_CONSTRUCTORS
#define VULKAN_HPP_NO_STRUCT_SETTERS
#define VULKAN_HPP_NO_SMART_HANDLE
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
#define VMA_STATIC_VULKAN_FUNCTIONS 0 #define VMA_STATIC_VULKAN_FUNCTIONS 0

View File

@ -0,0 +1,176 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "video_core/renderer_vulkan/vk_descriptor_manager.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
namespace Vulkan {
struct Bindings {
std::array<vk::DescriptorType, MAX_DESCRIPTORS> bindings;
u32 binding_count;
};
constexpr u32 DESCRIPTOR_BATCH_SIZE = 8;
constexpr u32 RASTERIZER_SET_COUNT = 4;
constexpr static std::array RASTERIZER_SETS = {
Bindings{// Utility set
.bindings = {vk::DescriptorType::eUniformBuffer, vk::DescriptorType::eUniformBuffer,
vk::DescriptorType::eUniformTexelBuffer,
vk::DescriptorType::eUniformTexelBuffer,
vk::DescriptorType::eUniformTexelBuffer},
.binding_count = 5},
Bindings{// Texture set
.bindings = {vk::DescriptorType::eSampledImage, vk::DescriptorType::eSampledImage,
vk::DescriptorType::eSampledImage, vk::DescriptorType::eSampledImage},
.binding_count = 4},
Bindings{// Sampler set
.bindings = {vk::DescriptorType::eSampler, vk::DescriptorType::eSampler,
vk::DescriptorType::eSampler, vk::DescriptorType::eSampler},
.binding_count = 4},
Bindings{// Shadow set
.bindings = {vk::DescriptorType::eStorageImage, vk::DescriptorType::eStorageImage,
vk::DescriptorType::eStorageImage, vk::DescriptorType::eStorageImage,
vk::DescriptorType::eStorageImage, vk::DescriptorType::eStorageImage,
vk::DescriptorType::eStorageImage},
.binding_count = 7}};
constexpr vk::ShaderStageFlags ToVkStageFlags(vk::DescriptorType type) {
vk::ShaderStageFlags flags;
switch (type) {
case vk::DescriptorType::eSampler:
case vk::DescriptorType::eSampledImage:
case vk::DescriptorType::eUniformTexelBuffer:
case vk::DescriptorType::eStorageImage:
flags = vk::ShaderStageFlagBits::eFragment;
break;
case vk::DescriptorType::eUniformBuffer:
case vk::DescriptorType::eUniformBufferDynamic:
flags = vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex |
vk::ShaderStageFlagBits::eGeometry | vk::ShaderStageFlagBits::eCompute;
break;
default:
LOG_ERROR(Render_Vulkan, "Unknown descriptor type!");
}
return flags;
}
DescriptorManager::DescriptorManager(const Instance& instance, TaskScheduler& scheduler)
: instance{instance}, scheduler{scheduler} {
descriptor_dirty.fill(true);
BuildLayouts();
}
DescriptorManager::~DescriptorManager() {
vk::Device device = instance.GetDevice();
device.destroyPipelineLayout(layout);
for (std::size_t i = 0; i < MAX_DESCRIPTOR_SETS; i++) {
device.destroyDescriptorSetLayout(descriptor_set_layouts[i]);
device.destroyDescriptorUpdateTemplate(update_templates[i]);
}
}
void DescriptorManager::SetBinding(u32 set, u32 binding, DescriptorData data) {
if (update_data[set][binding] != data) {
update_data[set][binding] = data;
descriptor_dirty[set] = true;
}
}
void DescriptorManager::BindDescriptorSets() {
vk::Device device = instance.GetDevice();
std::array<vk::DescriptorSetLayout, DESCRIPTOR_BATCH_SIZE> layouts;
for (u32 i = 0; i < RASTERIZER_SET_COUNT; i++) {
if (descriptor_dirty[i] || !descriptor_sets[i]) {
auto& batch = descriptor_batch[i];
if (batch.empty()) {
layouts.fill(descriptor_set_layouts[i]);
const vk::DescriptorSetAllocateInfo alloc_info = {
.descriptorPool = scheduler.GetDescriptorPool(),
.descriptorSetCount = DESCRIPTOR_BATCH_SIZE,
.pSetLayouts = layouts.data()};
try {
batch = device.allocateDescriptorSets(alloc_info);
} catch (vk::OutOfPoolMemoryError& err) {
LOG_CRITICAL(Render_Vulkan, "Run out of pool memory for layout {}: {}", i,
err.what());
UNREACHABLE();
}
}
vk::DescriptorSet set = batch.back();
device.updateDescriptorSetWithTemplate(set, update_templates[i], update_data[i][0]);
descriptor_sets[i] = set;
descriptor_dirty[i] = false;
batch.pop_back();
}
}
// Bind the descriptor sets
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0,
RASTERIZER_SET_COUNT, descriptor_sets.data(), 0, nullptr);
}
void DescriptorManager::MarkDirty() {
descriptor_dirty.fill(true);
for (auto& batch : descriptor_batch) {
batch.clear();
}
}
void DescriptorManager::BuildLayouts() {
std::array<vk::DescriptorSetLayoutBinding, MAX_DESCRIPTORS> set_bindings;
std::array<vk::DescriptorUpdateTemplateEntry, MAX_DESCRIPTORS> update_entries;
vk::Device device = instance.GetDevice();
for (u32 i = 0; i < RASTERIZER_SET_COUNT; i++) {
const auto& set = RASTERIZER_SETS[i];
for (u32 j = 0; j < set.binding_count; j++) {
vk::DescriptorType type = set.bindings[j];
set_bindings[j] = vk::DescriptorSetLayoutBinding{.binding = j,
.descriptorType = type,
.descriptorCount = 1,
.stageFlags = ToVkStageFlags(type)};
update_entries[j] =
vk::DescriptorUpdateTemplateEntry{.dstBinding = j,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = type,
.offset = j * sizeof(DescriptorData),
.stride = 0};
}
const vk::DescriptorSetLayoutCreateInfo layout_info = {.bindingCount = set.binding_count,
.pBindings = set_bindings.data()};
// Create descriptor set layout
descriptor_set_layouts[i] = device.createDescriptorSetLayout(layout_info);
const vk::DescriptorUpdateTemplateCreateInfo template_info = {
.descriptorUpdateEntryCount = set.binding_count,
.pDescriptorUpdateEntries = update_entries.data(),
.templateType = vk::DescriptorUpdateTemplateType::eDescriptorSet,
.descriptorSetLayout = descriptor_set_layouts[i]};
// Create descriptor set update template
update_templates[i] = device.createDescriptorUpdateTemplate(template_info);
}
const vk::PipelineLayoutCreateInfo layout_info = {.setLayoutCount = RASTERIZER_SET_COUNT,
.pSetLayouts = descriptor_set_layouts.data(),
.pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr};
layout = device.createPipelineLayout(layout_info);
}
} // namespace Vulkan

View File

@ -0,0 +1,68 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "video_core/renderer_vulkan/vk_common.h"
namespace Vulkan {
constexpr u32 MAX_DESCRIPTORS = 8;
constexpr u32 MAX_DESCRIPTOR_SETS = 6;
union DescriptorData {
vk::DescriptorImageInfo image_info;
vk::DescriptorBufferInfo buffer_info;
vk::BufferView buffer_view;
bool operator!=(const DescriptorData& other) const {
return std::memcmp(this, &other, sizeof(DescriptorData)) != 0;
}
};
using DescriptorSetData = std::array<DescriptorData, MAX_DESCRIPTORS>;
class Instance;
class TaskScheduler;
class DescriptorManager {
public:
DescriptorManager(const Instance& instance, TaskScheduler& scheduler);
~DescriptorManager();
/// Binds a resource to the provided binding
void SetBinding(u32 set, u32 binding, DescriptorData data);
/// Builds descriptor sets that reference the currently bound resources
void BindDescriptorSets();
/// Marks cached descriptor state dirty
void MarkDirty();
/// Returns the rasterizer pipeline layout
vk::PipelineLayout GetPipelineLayout() const {
return layout;
}
private:
/// Builds the rasterizer pipeline layout objects
void BuildLayouts();
private:
const Instance& instance;
TaskScheduler& scheduler;
// Cached layouts for the rasterizer pipelines
vk::PipelineLayout layout;
std::array<vk::DescriptorSetLayout, MAX_DESCRIPTOR_SETS> descriptor_set_layouts;
std::array<vk::DescriptorUpdateTemplate, MAX_DESCRIPTOR_SETS> update_templates;
// Current data for the descriptor sets
std::array<DescriptorSetData, MAX_DESCRIPTOR_SETS> update_data{};
std::array<bool, MAX_DESCRIPTOR_SETS> descriptor_dirty{};
std::array<vk::DescriptorSet, MAX_DESCRIPTOR_SETS> descriptor_sets;
std::array<std::vector<vk::DescriptorSet>, MAX_DESCRIPTOR_SETS> descriptor_batch;
};
} // namespace Vulkan

View File

@ -15,56 +15,6 @@
namespace Vulkan { namespace Vulkan {
struct Bindings {
std::array<vk::DescriptorType, MAX_DESCRIPTORS> bindings;
u32 binding_count;
};
constexpr u32 DESCRIPTOR_BATCH_SIZE = 8;
constexpr u32 RASTERIZER_SET_COUNT = 4;
constexpr static std::array RASTERIZER_SETS = {
Bindings{// Utility set
.bindings = {vk::DescriptorType::eUniformBuffer, vk::DescriptorType::eUniformBuffer,
vk::DescriptorType::eUniformTexelBuffer,
vk::DescriptorType::eUniformTexelBuffer,
vk::DescriptorType::eUniformTexelBuffer},
.binding_count = 5},
Bindings{// Texture set
.bindings = {vk::DescriptorType::eSampledImage, vk::DescriptorType::eSampledImage,
vk::DescriptorType::eSampledImage, vk::DescriptorType::eSampledImage},
.binding_count = 4},
Bindings{// Sampler set
.bindings = {vk::DescriptorType::eSampler, vk::DescriptorType::eSampler,
vk::DescriptorType::eSampler, vk::DescriptorType::eSampler},
.binding_count = 4},
Bindings{// Shadow set
.bindings = {vk::DescriptorType::eStorageImage, vk::DescriptorType::eStorageImage,
vk::DescriptorType::eStorageImage, vk::DescriptorType::eStorageImage,
vk::DescriptorType::eStorageImage, vk::DescriptorType::eStorageImage,
vk::DescriptorType::eStorageImage},
.binding_count = 7}};
constexpr vk::ShaderStageFlags ToVkStageFlags(vk::DescriptorType type) {
vk::ShaderStageFlags flags;
switch (type) {
case vk::DescriptorType::eSampler:
case vk::DescriptorType::eSampledImage:
case vk::DescriptorType::eUniformTexelBuffer:
case vk::DescriptorType::eStorageImage:
flags = vk::ShaderStageFlagBits::eFragment;
break;
case vk::DescriptorType::eUniformBuffer:
case vk::DescriptorType::eUniformBufferDynamic:
flags = vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex |
vk::ShaderStageFlagBits::eGeometry | vk::ShaderStageFlagBits::eCompute;
break;
default:
LOG_ERROR(Render_Vulkan, "Unknown descriptor type!");
}
return flags;
}
u32 AttribBytes(VertexAttribute attrib) { u32 AttribBytes(VertexAttribute attrib) {
switch (attrib.type) { switch (attrib.type) {
case AttribType::Float: case AttribType::Float:
@ -116,10 +66,9 @@ vk::ShaderStageFlagBits ToVkShaderStage(std::size_t index) {
PipelineCache::PipelineCache(const Instance& instance, TaskScheduler& scheduler, PipelineCache::PipelineCache(const Instance& instance, TaskScheduler& scheduler,
RenderpassCache& renderpass_cache) RenderpassCache& renderpass_cache)
: instance{instance}, scheduler{scheduler}, renderpass_cache{renderpass_cache} { : instance{instance}, scheduler{scheduler}, renderpass_cache{renderpass_cache}, desc_manager{
descriptor_dirty.fill(true); instance,
scheduler} {
BuildLayout();
trivial_vertex_shader = Compile(GenerateTrivialVertexShader(), vk::ShaderStageFlagBits::eVertex, trivial_vertex_shader = Compile(GenerateTrivialVertexShader(), vk::ShaderStageFlagBits::eVertex,
instance.GetDevice(), ShaderOptimization::Debug); instance.GetDevice(), ShaderOptimization::Debug);
} }
@ -129,13 +78,8 @@ PipelineCache::~PipelineCache() {
SaveDiskCache(); SaveDiskCache();
device.destroyPipelineLayout(layout);
device.destroyPipelineCache(pipeline_cache); device.destroyPipelineCache(pipeline_cache);
device.destroyShaderModule(trivial_vertex_shader); device.destroyShaderModule(trivial_vertex_shader);
for (std::size_t i = 0; i < MAX_DESCRIPTOR_SETS; i++) {
device.destroyDescriptorSetLayout(descriptor_set_layouts[i]);
device.destroyDescriptorUpdateTemplate(update_templates[i]);
}
for (auto& [key, module] : programmable_vertex_shaders.shader_cache) { for (auto& [key, module] : programmable_vertex_shaders.shader_cache) {
device.destroyShaderModule(module); device.destroyShaderModule(module);
@ -238,7 +182,7 @@ void PipelineCache::BindPipeline(const PipelineInfo& info) {
current_pipeline = it->second; current_pipeline = it->second;
} }
BindDescriptorSets(); desc_manager.BindDescriptorSets();
} }
bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs, bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs,
@ -291,34 +235,29 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs) {
void PipelineCache::BindTexture(u32 binding, vk::ImageView image_view) { void PipelineCache::BindTexture(u32 binding, vk::ImageView image_view) {
const vk::DescriptorImageInfo image_info = { const vk::DescriptorImageInfo image_info = {
.imageView = image_view, .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal}; .imageView = image_view, .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal};
desc_manager.SetBinding(1, binding, DescriptorData{image_info});
SetBinding(1, binding, DescriptorData{image_info});
} }
void PipelineCache::BindStorageImage(u32 binding, vk::ImageView image_view) { void PipelineCache::BindStorageImage(u32 binding, vk::ImageView image_view) {
const vk::DescriptorImageInfo image_info = {.imageView = image_view, const vk::DescriptorImageInfo image_info = {.imageView = image_view,
.imageLayout = vk::ImageLayout::eGeneral}; .imageLayout = vk::ImageLayout::eGeneral};
desc_manager.SetBinding(3, binding, DescriptorData{image_info});
SetBinding(3, binding, DescriptorData{image_info});
} }
void PipelineCache::BindBuffer(u32 binding, vk::Buffer buffer, u32 offset, u32 size) { void PipelineCache::BindBuffer(u32 binding, vk::Buffer buffer, u32 offset, u32 size) {
const DescriptorData data = { const DescriptorData data = {
.buffer_info = vk::DescriptorBufferInfo{.buffer = buffer, .offset = offset, .range = size}}; .buffer_info = vk::DescriptorBufferInfo{.buffer = buffer, .offset = offset, .range = size}};
desc_manager.SetBinding(0, binding, data);
SetBinding(0, binding, data);
} }
void PipelineCache::BindTexelBuffer(u32 binding, vk::BufferView buffer_view) { void PipelineCache::BindTexelBuffer(u32 binding, vk::BufferView buffer_view) {
const DescriptorData data = {.buffer_view = buffer_view}; const DescriptorData data = {.buffer_view = buffer_view};
desc_manager.SetBinding(0, binding, data);
SetBinding(0, binding, data);
} }
void PipelineCache::BindSampler(u32 binding, vk::Sampler sampler) { void PipelineCache::BindSampler(u32 binding, vk::Sampler sampler) {
const DescriptorData data = {.image_info = vk::DescriptorImageInfo{.sampler = sampler}}; const DescriptorData data = {.image_info = vk::DescriptorImageInfo{.sampler = sampler}};
desc_manager.SetBinding(2, binding, data);
SetBinding(2, binding, data);
} }
void PipelineCache::SetViewport(float x, float y, float width, float height) { void PipelineCache::SetViewport(float x, float y, float width, float height) {
@ -342,12 +281,9 @@ void PipelineCache::SetScissor(s32 x, s32 y, u32 width, u32 height) {
} }
void PipelineCache::MarkDirty() { void PipelineCache::MarkDirty() {
descriptor_dirty.fill(true); desc_manager.MarkDirty();
current_pipeline = VK_NULL_HANDLE; current_pipeline = VK_NULL_HANDLE;
state_dirty = true; state_dirty = true;
for (auto& batch : descriptor_batch) {
batch.clear();
}
} }
void PipelineCache::ApplyDynamic(const PipelineInfo& info) { void PipelineCache::ApplyDynamic(const PipelineInfo& info) {
@ -426,60 +362,6 @@ void PipelineCache::ApplyDynamic(const PipelineInfo& info) {
state_dirty = false; state_dirty = false;
} }
void PipelineCache::SetBinding(u32 set, u32 binding, DescriptorData data) {
if (update_data[set][binding] != data) {
update_data[set][binding] = data;
descriptor_dirty[set] = true;
}
}
void PipelineCache::BuildLayout() {
std::array<vk::DescriptorSetLayoutBinding, MAX_DESCRIPTORS> set_bindings;
std::array<vk::DescriptorUpdateTemplateEntry, MAX_DESCRIPTORS> update_entries;
vk::Device device = instance.GetDevice();
for (u32 i = 0; i < RASTERIZER_SET_COUNT; i++) {
const auto& set = RASTERIZER_SETS[i];
for (u32 j = 0; j < set.binding_count; j++) {
vk::DescriptorType type = set.bindings[j];
set_bindings[j] = vk::DescriptorSetLayoutBinding{.binding = j,
.descriptorType = type,
.descriptorCount = 1,
.stageFlags = ToVkStageFlags(type)};
update_entries[j] =
vk::DescriptorUpdateTemplateEntry{.dstBinding = j,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = type,
.offset = j * sizeof(DescriptorData),
.stride = 0};
}
const vk::DescriptorSetLayoutCreateInfo layout_info = {.bindingCount = set.binding_count,
.pBindings = set_bindings.data()};
// Create descriptor set layout
descriptor_set_layouts[i] = device.createDescriptorSetLayout(layout_info);
const vk::DescriptorUpdateTemplateCreateInfo template_info = {
.descriptorUpdateEntryCount = set.binding_count,
.pDescriptorUpdateEntries = update_entries.data(),
.templateType = vk::DescriptorUpdateTemplateType::eDescriptorSet,
.descriptorSetLayout = descriptor_set_layouts[i]};
// Create descriptor set update template
update_templates[i] = device.createDescriptorUpdateTemplate(template_info);
}
const vk::PipelineLayoutCreateInfo layout_info = {.setLayoutCount = RASTERIZER_SET_COUNT,
.pSetLayouts = descriptor_set_layouts.data(),
.pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr};
layout = device.createPipelineLayout(layout_info);
}
vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) { vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
vk::Device device = instance.GetDevice(); vk::Device device = instance.GetDevice();
@ -495,12 +377,10 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
.stage = ToVkShaderStage(i), .module = shader, .pName = "main"}; .stage = ToVkShaderStage(i), .module = shader, .pName = "main"};
} }
/** // Vulkan doesn't intuitively support fixed attributes. To avoid duplicating the data and
* Vulkan doesn't intuitively support fixed attributes. To avoid duplicating the data and // increasing data upload, when the fixed flag is true, we specify VK_VERTEX_INPUT_RATE_INSTANCE
* increasing data upload, when the fixed flag is true, we specify VK_VERTEX_INPUT_RATE_INSTANCE // as the input rate. Since one instance is all we render, the shader will always read the
* as the input rate. Since one instance is all we render, the shader will always read the // single attribute.
* single attribute.
*/
std::array<vk::VertexInputBindingDescription, MAX_VERTEX_BINDINGS> bindings; std::array<vk::VertexInputBindingDescription, MAX_VERTEX_BINDINGS> bindings;
for (u32 i = 0; i < info.vertex_layout.binding_count; i++) { for (u32 i = 0; i < info.vertex_layout.binding_count; i++) {
const auto& binding = info.vertex_layout.bindings[i]; const auto& binding = info.vertex_layout.bindings[i];
@ -624,7 +504,7 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
.pDepthStencilState = &depth_info, .pDepthStencilState = &depth_info,
.pColorBlendState = &color_blending, .pColorBlendState = &color_blending,
.pDynamicState = &dynamic_info, .pDynamicState = &dynamic_info,
.layout = layout, .layout = desc_manager.GetPipelineLayout(),
.renderPass = .renderPass =
renderpass_cache.GetRenderpass(info.color_attachment, info.depth_attachment, false)}; renderpass_cache.GetRenderpass(info.color_attachment, info.depth_attachment, false)};
@ -639,46 +519,6 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
return VK_NULL_HANDLE; return VK_NULL_HANDLE;
} }
static_assert(sizeof(vk::DescriptorBufferInfo) == sizeof(VkDescriptorBufferInfo));
void PipelineCache::BindDescriptorSets() {
vk::Device device = instance.GetDevice();
std::array<vk::DescriptorSetLayout, DESCRIPTOR_BATCH_SIZE> layouts;
for (u32 i = 0; i < RASTERIZER_SET_COUNT; i++) {
if (descriptor_dirty[i] || !descriptor_sets[i]) {
auto& batch = descriptor_batch[i];
if (batch.empty()) {
layouts.fill(descriptor_set_layouts[i]);
const vk::DescriptorSetAllocateInfo alloc_info = {
.descriptorPool = scheduler.GetDescriptorPool(),
.descriptorSetCount = DESCRIPTOR_BATCH_SIZE,
.pSetLayouts = layouts.data()};
try {
batch = device.allocateDescriptorSets(alloc_info);
} catch (vk::OutOfPoolMemoryError& err) {
LOG_CRITICAL(Render_Vulkan, "Run out of pool memory for layout {}: {}", i,
err.what());
UNREACHABLE();
}
}
vk::DescriptorSet set = batch.back();
device.updateDescriptorSetWithTemplate(set, update_templates[i], update_data[i][0]);
descriptor_sets[i] = set;
descriptor_dirty[i] = false;
batch.pop_back();
}
}
// Bind the descriptor sets
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0,
RASTERIZER_SET_COUNT, descriptor_sets.data(), 0, nullptr);
}
bool PipelineCache::IsCacheValid(const u8* data, u64 size) const { bool PipelineCache::IsCacheValid(const u8* data, u64 size) const {
if (size < sizeof(vk::PipelineCacheHeaderVersionOne)) { if (size < sizeof(vk::PipelineCacheHeaderVersionOne)) {
LOG_ERROR(Render_Vulkan, "Pipeline cache failed validation: Invalid header"); LOG_ERROR(Render_Vulkan, "Pipeline cache failed validation: Invalid header");

View File

@ -9,7 +9,7 @@
#include "common/hash.h" #include "common/hash.h"
#include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/regs.h" #include "video_core/regs.h"
#include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_descriptor_manager.h"
#include "video_core/renderer_vulkan/vk_shader.h" #include "video_core/renderer_vulkan/vk_shader.h"
#include "video_core/renderer_vulkan/vk_shader_gen.h" #include "video_core/renderer_vulkan/vk_shader_gen.h"
#include "video_core/shader/shader_cache.h" #include "video_core/shader/shader_cache.h"
@ -19,8 +19,6 @@ namespace Vulkan {
constexpr u32 MAX_SHADER_STAGES = 3; constexpr u32 MAX_SHADER_STAGES = 3;
constexpr u32 MAX_VERTEX_ATTRIBUTES = 16; constexpr u32 MAX_VERTEX_ATTRIBUTES = 16;
constexpr u32 MAX_VERTEX_BINDINGS = 16; constexpr u32 MAX_VERTEX_BINDINGS = 16;
constexpr u32 MAX_DESCRIPTORS = 8;
constexpr u32 MAX_DESCRIPTOR_SETS = 6;
/** /**
* The pipeline state is tightly packed with bitfields to reduce * The pipeline state is tightly packed with bitfields to reduce
@ -109,18 +107,6 @@ struct PipelineInfo {
} }
}; };
union DescriptorData {
vk::DescriptorImageInfo image_info;
vk::DescriptorBufferInfo buffer_info;
vk::BufferView buffer_view;
bool operator!=(const DescriptorData& other) const {
return std::memcmp(this, &other, sizeof(DescriptorData)) != 0;
}
};
using DescriptorSetData = std::array<DescriptorData, MAX_DESCRIPTORS>;
/** /**
* Vulkan specialized PICA shader caches * Vulkan specialized PICA shader caches
*/ */
@ -197,9 +183,6 @@ public:
void MarkDirty(); void MarkDirty();
private: private:
/// Binds a resource to the provided binding
void SetBinding(u32 set, u32 binding, DescriptorData data);
/// Applies dynamic pipeline state to the current command buffer /// Applies dynamic pipeline state to the current command buffer
void ApplyDynamic(const PipelineInfo& info); void ApplyDynamic(const PipelineInfo& info);
@ -209,9 +192,6 @@ private:
/// Builds a rasterizer pipeline using the PipelineInfo struct /// Builds a rasterizer pipeline using the PipelineInfo struct
vk::Pipeline BuildPipeline(const PipelineInfo& info); vk::Pipeline BuildPipeline(const PipelineInfo& info);
/// Builds descriptor sets that reference the currently bound resources
void BindDescriptorSets();
/// Returns true when the disk data can be used by the current driver /// Returns true when the disk data can be used by the current driver
bool IsCacheValid(const u8* data, u64 size) const; bool IsCacheValid(const u8* data, u64 size) const;
@ -225,6 +205,7 @@ private:
const Instance& instance; const Instance& instance;
TaskScheduler& scheduler; TaskScheduler& scheduler;
RenderpassCache& renderpass_cache; RenderpassCache& renderpass_cache;
DescriptorManager desc_manager;
// Cached pipelines // Cached pipelines
vk::PipelineCache pipeline_cache; vk::PipelineCache pipeline_cache;
@ -233,17 +214,6 @@ private:
PipelineInfo current_info{}; PipelineInfo current_info{};
vk::Viewport current_viewport{}; vk::Viewport current_viewport{};
vk::Rect2D current_scissor{}; vk::Rect2D current_scissor{};
// Cached layouts for the rasterizer pipelines
vk::PipelineLayout layout;
std::array<vk::DescriptorSetLayout, MAX_DESCRIPTOR_SETS> descriptor_set_layouts;
std::array<vk::DescriptorUpdateTemplate, MAX_DESCRIPTOR_SETS> update_templates;
// Current data for the descriptor sets
std::array<DescriptorSetData, MAX_DESCRIPTOR_SETS> update_data{};
std::array<bool, MAX_DESCRIPTOR_SETS> descriptor_dirty{};
std::array<vk::DescriptorSet, MAX_DESCRIPTOR_SETS> descriptor_sets;
std::array<std::vector<vk::DescriptorSet>, MAX_DESCRIPTOR_SETS> descriptor_batch;
bool state_dirty = true; bool state_dirty = true;
// Bound shader modules // Bound shader modules