renderer_vulkan: Abstract descriptor management
* The pipeline cache was starting to get cluttered
This commit is contained in:
@ -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
|
||||||
|
@ -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
|
||||||
|
176
src/video_core/renderer_vulkan/vk_descriptor_manager.cpp
Normal file
176
src/video_core/renderer_vulkan/vk_descriptor_manager.cpp
Normal 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
|
68
src/video_core/renderer_vulkan/vk_descriptor_manager.h
Normal file
68
src/video_core/renderer_vulkan/vk_descriptor_manager.h
Normal 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
|
@ -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");
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user