[Vulkan] Basic texture descriptor set allocation/binding
This commit is contained in:
parent
3381d679b4
commit
46202dd27a
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/gpu/spirv_shader.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
SpirvShader::SpirvShader(xenos::ShaderType shader_type,
|
||||
uint64_t ucode_data_hash, const uint32_t* ucode_dwords,
|
||||
size_t ucode_dword_count,
|
||||
std::endian ucode_source_endian)
|
||||
: Shader(shader_type, ucode_data_hash, ucode_dwords, ucode_dword_count,
|
||||
ucode_source_endian) {}
|
||||
|
||||
Shader::Translation* SpirvShader::CreateTranslationInstance(
|
||||
uint64_t modification) {
|
||||
return new SpirvTranslation(*this, modification);
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_GPU_SPIRV_SHADER_H_
|
||||
#define XENIA_GPU_SPIRV_SHADER_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/spirv_shader_translator.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
class SpirvShader : public Shader {
|
||||
public:
|
||||
class SpirvTranslation : public Translation {
|
||||
public:
|
||||
explicit SpirvTranslation(SpirvShader& shader, uint64_t modification)
|
||||
: Translation(shader, modification) {}
|
||||
};
|
||||
|
||||
explicit SpirvShader(xenos::ShaderType shader_type, uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords, size_t ucode_dword_count,
|
||||
std::endian ucode_source_endian = std::endian::big);
|
||||
|
||||
// Resource bindings are gathered after the successful translation of any
|
||||
// modification for simplicity of translation (and they don't depend on
|
||||
// modification bits).
|
||||
|
||||
struct TextureBinding {
|
||||
uint32_t fetch_constant : 5;
|
||||
// Stacked and 3D are separate TextureBindings.
|
||||
xenos::FetchOpDimension dimension : 2;
|
||||
uint32_t is_signed : 1;
|
||||
};
|
||||
// Safe to hash and compare with memcmp for layout hashing.
|
||||
const std::vector<TextureBinding>& GetTextureBindingsAfterTranslation()
|
||||
const {
|
||||
return texture_bindings_;
|
||||
}
|
||||
const uint32_t GetUsedTextureMaskAfterTranslation() const {
|
||||
return used_texture_mask_;
|
||||
}
|
||||
|
||||
struct SamplerBinding {
|
||||
uint32_t fetch_constant : 5;
|
||||
xenos::TextureFilter mag_filter : 2;
|
||||
xenos::TextureFilter min_filter : 2;
|
||||
xenos::TextureFilter mip_filter : 2;
|
||||
xenos::AnisoFilter aniso_filter : 3;
|
||||
};
|
||||
const std::vector<SamplerBinding>& GetSamplerBindingsAfterTranslation()
|
||||
const {
|
||||
return sampler_bindings_;
|
||||
}
|
||||
|
||||
protected:
|
||||
Translation* CreateTranslationInstance(uint64_t modification) override;
|
||||
|
||||
private:
|
||||
friend class SpirvShaderTranslator;
|
||||
|
||||
std::atomic_flag bindings_setup_entered_ = ATOMIC_FLAG_INIT;
|
||||
std::vector<TextureBinding> texture_bindings_;
|
||||
std::vector<SamplerBinding> sampler_bindings_;
|
||||
uint32_t used_texture_mask_ = 0;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_GPU_SPIRV_SHADER_H_
|
|
@ -20,6 +20,7 @@
|
|||
#include "third_party/glslang/SPIRV/GLSL.std.450.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/gpu/spirv_shader.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
@ -95,6 +96,9 @@ void SpirvShaderTranslator::Reset() {
|
|||
|
||||
uniform_float_constants_ = spv::NoResult;
|
||||
|
||||
sampler_bindings_.clear();
|
||||
texture_bindings_.clear();
|
||||
|
||||
main_interface_.clear();
|
||||
var_main_registers_ = spv::NoResult;
|
||||
|
||||
|
@ -595,6 +599,41 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
|||
return module_bytes;
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::PostTranslation() {
|
||||
Shader::Translation& translation = current_translation();
|
||||
if (!translation.is_valid()) {
|
||||
return;
|
||||
}
|
||||
SpirvShader* spirv_shader = dynamic_cast<SpirvShader*>(&translation.shader());
|
||||
if (spirv_shader && !spirv_shader->bindings_setup_entered_.test_and_set(
|
||||
std::memory_order_relaxed)) {
|
||||
spirv_shader->texture_bindings_.clear();
|
||||
spirv_shader->texture_bindings_.reserve(texture_bindings_.size());
|
||||
for (const TextureBinding& translator_binding : texture_bindings_) {
|
||||
SpirvShader::TextureBinding& shader_binding =
|
||||
spirv_shader->texture_bindings_.emplace_back();
|
||||
// For a stable hash.
|
||||
std::memset(&shader_binding, 0, sizeof(shader_binding));
|
||||
shader_binding.fetch_constant = translator_binding.fetch_constant;
|
||||
shader_binding.dimension = translator_binding.dimension;
|
||||
shader_binding.is_signed = translator_binding.is_signed;
|
||||
spirv_shader->used_texture_mask_ |= UINT32_C(1)
|
||||
<< translator_binding.fetch_constant;
|
||||
}
|
||||
spirv_shader->sampler_bindings_.clear();
|
||||
spirv_shader->sampler_bindings_.reserve(sampler_bindings_.size());
|
||||
for (const SamplerBinding& translator_binding : sampler_bindings_) {
|
||||
SpirvShader::SamplerBinding& shader_binding =
|
||||
spirv_shader->sampler_bindings_.emplace_back();
|
||||
shader_binding.fetch_constant = translator_binding.fetch_constant;
|
||||
shader_binding.mag_filter = translator_binding.mag_filter;
|
||||
shader_binding.min_filter = translator_binding.min_filter;
|
||||
shader_binding.mip_filter = translator_binding.mip_filter;
|
||||
shader_binding.aniso_filter = translator_binding.aniso_filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessLabel(uint32_t cf_index) {
|
||||
if (cf_index == 0) {
|
||||
// 0 already added in the beginning.
|
||||
|
|
|
@ -131,9 +131,11 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
kDescriptorSetMutableLayoutsStart,
|
||||
|
||||
// Rarely used at all, but may be changed at an unpredictable rate when
|
||||
// vertex textures are used, combined images and samplers.
|
||||
kDescriptorSetTexturesVertex = kDescriptorSetMutableLayoutsStart,
|
||||
// Per-material, combined images and samplers.
|
||||
// vertex textures are used.
|
||||
kDescriptorSetSamplersVertex = kDescriptorSetMutableLayoutsStart,
|
||||
kDescriptorSetTexturesVertex,
|
||||
// Per-material textures.
|
||||
kDescriptorSetSamplersPixel,
|
||||
kDescriptorSetTexturesPixel,
|
||||
kDescriptorSetCount,
|
||||
};
|
||||
|
@ -217,6 +219,8 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
|
||||
std::vector<uint8_t> CompleteTranslation() override;
|
||||
|
||||
void PostTranslation() override;
|
||||
|
||||
void ProcessLabel(uint32_t cf_index) override;
|
||||
|
||||
void ProcessExecInstructionBegin(const ParsedExecInstruction& instr) override;
|
||||
|
@ -229,9 +233,31 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
|
||||
void ProcessVertexFetchInstruction(
|
||||
const ParsedVertexFetchInstruction& instr) override;
|
||||
void ProcessTextureFetchInstruction(
|
||||
const ParsedTextureFetchInstruction& instr) override;
|
||||
void ProcessAluInstruction(const ParsedAluInstruction& instr) override;
|
||||
|
||||
private:
|
||||
struct TextureBinding {
|
||||
uint32_t fetch_constant;
|
||||
// Stacked and 3D are separate TextureBindings.
|
||||
xenos::FetchOpDimension dimension;
|
||||
bool is_signed;
|
||||
|
||||
spv::Id type;
|
||||
spv::Id variable;
|
||||
};
|
||||
|
||||
struct SamplerBinding {
|
||||
uint32_t fetch_constant;
|
||||
xenos::TextureFilter mag_filter;
|
||||
xenos::TextureFilter min_filter;
|
||||
xenos::TextureFilter mip_filter;
|
||||
xenos::AnisoFilter aniso_filter;
|
||||
|
||||
spv::Id variable;
|
||||
};
|
||||
|
||||
// Builder helpers.
|
||||
void SpirvCreateSelectionMerge(
|
||||
spv::Id merge_block_id, spv::SelectionControlMask selection_control_mask =
|
||||
|
@ -353,6 +379,15 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
|
||||
spv::Id LoadUint32FromSharedMemory(spv::Id address_dwords_int);
|
||||
|
||||
size_t FindOrAddTextureBinding(uint32_t fetch_constant,
|
||||
xenos::FetchOpDimension dimension,
|
||||
bool is_signed);
|
||||
size_t FindOrAddSamplerBinding(uint32_t fetch_constant,
|
||||
xenos::TextureFilter mag_filter,
|
||||
xenos::TextureFilter min_filter,
|
||||
xenos::TextureFilter mip_filter,
|
||||
xenos::AnisoFilter aniso_filter);
|
||||
|
||||
Features features_;
|
||||
|
||||
std::unique_ptr<spv::Builder> builder_;
|
||||
|
@ -446,6 +481,13 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
|
||||
spv::Id buffers_shared_memory_;
|
||||
|
||||
// Not using combined images and samplers because
|
||||
// maxPerStageDescriptorSamplers is often lower than
|
||||
// maxPerStageDescriptorSampledImages, and for every fetch constant, there
|
||||
// are, for regular fetches, two bindings (unsigned and signed).
|
||||
std::vector<TextureBinding> texture_bindings_;
|
||||
std::vector<SamplerBinding> sampler_bindings_;
|
||||
|
||||
// VS as VS only - int.
|
||||
spv::Id input_vertex_index_;
|
||||
// VS as TES only - int.
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "third_party/glslang/SPIRV/GLSL.std.450.h"
|
||||
#include "xenia/base/math.h"
|
||||
|
||||
|
@ -533,5 +535,145 @@ void SpirvShaderTranslator::ProcessVertexFetchInstruction(
|
|||
StoreResult(instr.result, result);
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessTextureFetchInstruction(
|
||||
const ParsedTextureFetchInstruction& instr) {
|
||||
UpdateInstructionPredication(instr.is_predicated, instr.predicate_condition);
|
||||
|
||||
EnsureBuildPointAvailable();
|
||||
|
||||
// TODO(Triang3l): Fetch the texture.
|
||||
if (instr.opcode == ucode::FetchOpcode::kTextureFetch) {
|
||||
uint32_t fetch_constant_index = instr.operands[1].storage_index;
|
||||
bool use_computed_lod =
|
||||
instr.attributes.use_computed_lod &&
|
||||
(is_pixel_shader() || instr.attributes.use_register_gradients);
|
||||
FindOrAddTextureBinding(fetch_constant_index, instr.dimension, false);
|
||||
FindOrAddTextureBinding(fetch_constant_index, instr.dimension, true);
|
||||
FindOrAddSamplerBinding(fetch_constant_index, instr.attributes.mag_filter,
|
||||
instr.attributes.min_filter,
|
||||
instr.attributes.mip_filter,
|
||||
use_computed_lod ? instr.attributes.aniso_filter
|
||||
: xenos::AnisoFilter::kDisabled);
|
||||
}
|
||||
}
|
||||
|
||||
size_t SpirvShaderTranslator::FindOrAddTextureBinding(
|
||||
uint32_t fetch_constant, xenos::FetchOpDimension dimension,
|
||||
bool is_signed) {
|
||||
// 1D and 2D textures (including stacked ones) are treated as 2D arrays for
|
||||
// binding and coordinate simplicity.
|
||||
if (dimension == xenos::FetchOpDimension::k1D) {
|
||||
dimension = xenos::FetchOpDimension::k2D;
|
||||
}
|
||||
for (size_t i = 0; i < texture_bindings_.size(); ++i) {
|
||||
const TextureBinding& texture_binding = texture_bindings_[i];
|
||||
if (texture_binding.fetch_constant == fetch_constant &&
|
||||
texture_binding.dimension == dimension &&
|
||||
texture_binding.is_signed == is_signed) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// TODO(Triang3l): Limit the total count to that actually supported by the
|
||||
// implementation.
|
||||
size_t new_texture_binding_index = texture_bindings_.size();
|
||||
TextureBinding& new_texture_binding = texture_bindings_.emplace_back();
|
||||
new_texture_binding.fetch_constant = fetch_constant;
|
||||
new_texture_binding.dimension = dimension;
|
||||
new_texture_binding.is_signed = is_signed;
|
||||
spv::Dim type_dimension;
|
||||
bool is_array;
|
||||
const char* dimension_name;
|
||||
switch (dimension) {
|
||||
case xenos::FetchOpDimension::k3DOrStacked:
|
||||
type_dimension = spv::Dim3D;
|
||||
is_array = false;
|
||||
dimension_name = "3d";
|
||||
break;
|
||||
case xenos::FetchOpDimension::kCube:
|
||||
type_dimension = spv::DimCube;
|
||||
is_array = false;
|
||||
dimension_name = "cube";
|
||||
break;
|
||||
default:
|
||||
type_dimension = spv::Dim2D;
|
||||
is_array = true;
|
||||
dimension_name = "2d";
|
||||
}
|
||||
new_texture_binding.type =
|
||||
builder_->makeImageType(type_float_, type_dimension, false, is_array,
|
||||
false, 1, spv::ImageFormatUnknown);
|
||||
new_texture_binding.variable = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassUniformConstant,
|
||||
new_texture_binding.type,
|
||||
fmt::format("xe_texture{}_{}_{}", fetch_constant, dimension_name,
|
||||
is_signed ? 's' : 'u')
|
||||
.c_str());
|
||||
builder_->addDecoration(
|
||||
new_texture_binding.variable, spv::DecorationDescriptorSet,
|
||||
int(is_vertex_shader() ? kDescriptorSetTexturesVertex
|
||||
: kDescriptorSetTexturesPixel));
|
||||
builder_->addDecoration(new_texture_binding.variable, spv::DecorationBinding,
|
||||
int(new_texture_binding_index));
|
||||
if (features_.spirv_version >= spv::Spv_1_4) {
|
||||
main_interface_.push_back(new_texture_binding.variable);
|
||||
}
|
||||
return new_texture_binding_index;
|
||||
}
|
||||
|
||||
size_t SpirvShaderTranslator::FindOrAddSamplerBinding(
|
||||
uint32_t fetch_constant, xenos::TextureFilter mag_filter,
|
||||
xenos::TextureFilter min_filter, xenos::TextureFilter mip_filter,
|
||||
xenos::AnisoFilter aniso_filter) {
|
||||
if (aniso_filter != xenos::AnisoFilter::kUseFetchConst) {
|
||||
// TODO(Triang3l): Limit to what's actually supported by the implementation.
|
||||
aniso_filter = std::min(aniso_filter, xenos::AnisoFilter::kMax_16_1);
|
||||
}
|
||||
for (size_t i = 0; i < sampler_bindings_.size(); ++i) {
|
||||
const SamplerBinding& sampler_binding = sampler_bindings_[i];
|
||||
if (sampler_binding.fetch_constant == fetch_constant &&
|
||||
sampler_binding.mag_filter == mag_filter &&
|
||||
sampler_binding.min_filter == min_filter &&
|
||||
sampler_binding.mip_filter == mip_filter &&
|
||||
sampler_binding.aniso_filter == aniso_filter) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// TODO(Triang3l): Limit the total count to that actually supported by the
|
||||
// implementation.
|
||||
size_t new_sampler_binding_index = sampler_bindings_.size();
|
||||
SamplerBinding& new_sampler_binding = sampler_bindings_.emplace_back();
|
||||
new_sampler_binding.fetch_constant = fetch_constant;
|
||||
new_sampler_binding.mag_filter = mag_filter;
|
||||
new_sampler_binding.min_filter = min_filter;
|
||||
new_sampler_binding.mip_filter = mip_filter;
|
||||
new_sampler_binding.aniso_filter = aniso_filter;
|
||||
std::ostringstream name;
|
||||
static const char kFilterSuffixes[] = {'p', 'l', 'b', 'f'};
|
||||
name << "xe_sampler" << fetch_constant << '_'
|
||||
<< kFilterSuffixes[uint32_t(mag_filter)]
|
||||
<< kFilterSuffixes[uint32_t(min_filter)]
|
||||
<< kFilterSuffixes[uint32_t(mip_filter)];
|
||||
if (aniso_filter != xenos::AnisoFilter::kUseFetchConst) {
|
||||
if (aniso_filter == xenos::AnisoFilter::kDisabled) {
|
||||
name << "_a0";
|
||||
} else {
|
||||
name << "_a" << (UINT32_C(1) << (uint32_t(aniso_filter) - 1));
|
||||
}
|
||||
}
|
||||
new_sampler_binding.variable = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassUniformConstant,
|
||||
builder_->makeSamplerType(), name.str().c_str());
|
||||
builder_->addDecoration(
|
||||
new_sampler_binding.variable, spv::DecorationDescriptorSet,
|
||||
int(is_vertex_shader() ? kDescriptorSetSamplersVertex
|
||||
: kDescriptorSetSamplersPixel));
|
||||
builder_->addDecoration(new_sampler_binding.variable, spv::DecorationBinding,
|
||||
int(new_sampler_binding_index));
|
||||
if (features_.spirv_version >= spv::Spv_1_4) {
|
||||
main_interface_.push_back(new_sampler_binding.variable);
|
||||
}
|
||||
return new_sampler_binding_index;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -36,7 +36,7 @@
|
|||
#include "xenia/gpu/vulkan/vulkan_texture_cache.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/ui/vulkan/transient_descriptor_pool.h"
|
||||
#include "xenia/ui/vulkan/single_type_descriptor_set_allocator.h"
|
||||
#include "xenia/ui/vulkan/vulkan_presenter.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_upload_buffer_pool.h"
|
||||
|
@ -47,6 +47,17 @@ namespace vulkan {
|
|||
|
||||
class VulkanCommandProcessor : public CommandProcessor {
|
||||
public:
|
||||
// Single-descriptor layouts for use within a single frame.
|
||||
enum class SingleTransientDescriptorLayout {
|
||||
kUniformBufferGuestVertex,
|
||||
kUniformBufferFragment,
|
||||
kUniformBufferGuestShader,
|
||||
kUniformBufferSystemConstants,
|
||||
kUniformBufferCompute,
|
||||
kStorageBufferCompute,
|
||||
kCount,
|
||||
};
|
||||
|
||||
VulkanCommandProcessor(VulkanGraphicsSystem* graphics_system,
|
||||
kernel::KernelState* kernel_state);
|
||||
~VulkanCommandProcessor();
|
||||
|
@ -119,9 +130,23 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
// scope. Submission must be open.
|
||||
void EndRenderPass();
|
||||
|
||||
VkDescriptorSetLayout GetSingleTransientDescriptorLayout(
|
||||
SingleTransientDescriptorLayout transient_descriptor_layout) const {
|
||||
return descriptor_set_layouts_single_transient_[size_t(
|
||||
transient_descriptor_layout)];
|
||||
}
|
||||
// A frame must be open.
|
||||
VkDescriptorSet AllocateSingleTransientDescriptor(
|
||||
SingleTransientDescriptorLayout transient_descriptor_layout);
|
||||
|
||||
// The returned reference is valid until a cache clear.
|
||||
VkDescriptorSetLayout GetTextureDescriptorSetLayout(bool is_samplers,
|
||||
bool is_vertex,
|
||||
size_t binding_count);
|
||||
// The returned reference is valid until a cache clear.
|
||||
const VulkanPipelineCache::PipelineLayoutProvider* GetPipelineLayout(
|
||||
uint32_t texture_count_pixel, uint32_t texture_count_vertex);
|
||||
size_t texture_count_pixel, size_t sampler_count_pixel,
|
||||
size_t texture_count_vertex, size_t sampler_count_vertex);
|
||||
|
||||
// Binds a graphics pipeline for host-specific purposes, invalidating the
|
||||
// affected state. keep_dynamic_* must be false (to invalidate the dynamic
|
||||
|
@ -172,10 +197,12 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
union TextureDescriptorSetLayoutKey {
|
||||
uint32_t key;
|
||||
struct {
|
||||
// 0 - sampled image descriptors, 1 - sampler descriptors.
|
||||
uint32_t is_samplers : 1;
|
||||
uint32_t is_vertex : 1;
|
||||
// For 0, use descriptor_set_layout_empty_ instead as these are owning
|
||||
// references.
|
||||
uint32_t texture_count : 31;
|
||||
uint32_t binding_count : 30;
|
||||
};
|
||||
|
||||
TextureDescriptorSetLayoutKey() : key(0) {
|
||||
|
@ -196,12 +223,14 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
};
|
||||
|
||||
union PipelineLayoutKey {
|
||||
uint32_t key;
|
||||
uint64_t key;
|
||||
struct {
|
||||
// Pixel textures in the low bits since those are varied much more
|
||||
// commonly.
|
||||
uint32_t texture_count_pixel : 16;
|
||||
uint32_t texture_count_vertex : 16;
|
||||
uint16_t texture_count_pixel;
|
||||
uint16_t sampler_count_pixel;
|
||||
uint16_t texture_count_vertex;
|
||||
uint16_t sampler_count_vertex;
|
||||
};
|
||||
|
||||
PipelineLayoutKey() : key(0) { static_assert_size(*this, sizeof(key)); }
|
||||
|
@ -221,29 +250,55 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
|
||||
class PipelineLayout : public VulkanPipelineCache::PipelineLayoutProvider {
|
||||
public:
|
||||
PipelineLayout(
|
||||
explicit PipelineLayout(
|
||||
VkPipelineLayout pipeline_layout,
|
||||
VkDescriptorSetLayout descriptor_set_layout_textures_vertex_ref,
|
||||
VkDescriptorSetLayout descriptor_set_layout_textures_pixel_ref)
|
||||
VkDescriptorSetLayout descriptor_set_layout_samplers_vertex_ref,
|
||||
VkDescriptorSetLayout descriptor_set_layout_textures_pixel_ref,
|
||||
VkDescriptorSetLayout descriptor_set_layout_samplers_pixel_ref)
|
||||
: pipeline_layout_(pipeline_layout),
|
||||
descriptor_set_layout_textures_vertex_ref_(
|
||||
descriptor_set_layout_textures_vertex_ref),
|
||||
descriptor_set_layout_samplers_vertex_ref_(
|
||||
descriptor_set_layout_samplers_vertex_ref),
|
||||
descriptor_set_layout_textures_pixel_ref_(
|
||||
descriptor_set_layout_textures_pixel_ref) {}
|
||||
descriptor_set_layout_textures_pixel_ref),
|
||||
descriptor_set_layout_samplers_pixel_ref_(
|
||||
descriptor_set_layout_samplers_pixel_ref) {}
|
||||
VkPipelineLayout GetPipelineLayout() const override {
|
||||
return pipeline_layout_;
|
||||
}
|
||||
VkDescriptorSetLayout descriptor_set_layout_textures_vertex_ref() const {
|
||||
return descriptor_set_layout_textures_vertex_ref_;
|
||||
}
|
||||
VkDescriptorSetLayout descriptor_set_layout_samplers_vertex_ref() const {
|
||||
return descriptor_set_layout_samplers_vertex_ref_;
|
||||
}
|
||||
VkDescriptorSetLayout descriptor_set_layout_textures_pixel_ref() const {
|
||||
return descriptor_set_layout_textures_pixel_ref_;
|
||||
}
|
||||
VkDescriptorSetLayout descriptor_set_layout_samplers_pixel_ref() const {
|
||||
return descriptor_set_layout_samplers_pixel_ref_;
|
||||
}
|
||||
|
||||
private:
|
||||
VkPipelineLayout pipeline_layout_;
|
||||
VkDescriptorSetLayout descriptor_set_layout_textures_vertex_ref_;
|
||||
VkDescriptorSetLayout descriptor_set_layout_samplers_vertex_ref_;
|
||||
VkDescriptorSetLayout descriptor_set_layout_textures_pixel_ref_;
|
||||
VkDescriptorSetLayout descriptor_set_layout_samplers_pixel_ref_;
|
||||
};
|
||||
|
||||
struct UsedSingleTransientDescriptor {
|
||||
uint64_t frame;
|
||||
SingleTransientDescriptorLayout layout;
|
||||
VkDescriptorSet set;
|
||||
};
|
||||
|
||||
struct UsedTextureTransientDescriptorSet {
|
||||
uint64_t frame;
|
||||
TextureDescriptorSetLayoutKey layout;
|
||||
VkDescriptorSet set;
|
||||
};
|
||||
|
||||
// BeginSubmission and EndSubmission may be called at any time. If there's an
|
||||
|
@ -272,6 +327,8 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
return !submission_open_ && submissions_in_flight_fences_.empty();
|
||||
}
|
||||
|
||||
void ClearTransientDescriptorPools();
|
||||
|
||||
void SplitPendingBarrier();
|
||||
|
||||
void UpdateDynamicState(const draw_util::ViewportInfo& viewport_info,
|
||||
|
@ -284,10 +341,20 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
// Allocates a descriptor, space in the uniform buffer pool, and fills the
|
||||
// VkWriteDescriptorSet structure and VkDescriptorBufferInfo referenced by it.
|
||||
// Returns null in case of failure.
|
||||
uint8_t* WriteUniformBufferBinding(
|
||||
size_t size, VkDescriptorSetLayout descriptor_set_layout,
|
||||
uint8_t* WriteTransientUniformBufferBinding(
|
||||
size_t size, SingleTransientDescriptorLayout transient_descriptor_layout,
|
||||
VkDescriptorBufferInfo& descriptor_buffer_info_out,
|
||||
VkWriteDescriptorSet& write_descriptor_set_out);
|
||||
// Allocates a descriptor set and fills the VkWriteDescriptorSet structure.
|
||||
// The descriptor set layout must be the one for the given is_samplers,
|
||||
// is_vertex, binding_count (from GetTextureDescriptorSetLayout - may be
|
||||
// already available at the moment of the call, no need to locate it again).
|
||||
// Returns whether the allocation was successful.
|
||||
bool WriteTransientTextureBindings(
|
||||
bool is_samplers, bool is_vertex, uint32_t binding_count,
|
||||
VkDescriptorSetLayout descriptor_set_layout,
|
||||
const VkDescriptorImageInfo* image_info,
|
||||
VkWriteDescriptorSet& write_descriptor_set_out);
|
||||
|
||||
bool device_lost_ = false;
|
||||
|
||||
|
@ -333,22 +400,21 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
std::vector<VkSparseBufferMemoryBindInfo> sparse_buffer_bind_infos_temp_;
|
||||
VkPipelineStageFlags sparse_bind_wait_stage_mask_ = 0;
|
||||
|
||||
std::unique_ptr<ui::vulkan::TransientDescriptorPool>
|
||||
transient_descriptor_pool_uniform_buffers_;
|
||||
// Temporary storage with reusable memory for creating descriptor set layouts.
|
||||
std::vector<VkDescriptorSetLayoutBinding> descriptor_set_layout_bindings_;
|
||||
// Temporary storage with reusable memory for writing image and sampler
|
||||
// descriptors.
|
||||
std::vector<VkDescriptorImageInfo> descriptor_write_image_info_;
|
||||
|
||||
std::unique_ptr<ui::vulkan::VulkanUploadBufferPool> uniform_buffer_pool_;
|
||||
|
||||
// Descriptor set layouts used by different shaders.
|
||||
VkDescriptorSetLayout descriptor_set_layout_empty_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout descriptor_set_layout_fetch_bool_loop_constants_ =
|
||||
VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout descriptor_set_layout_float_constants_vertex_ =
|
||||
VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout descriptor_set_layout_float_constants_pixel_ =
|
||||
VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout descriptor_set_layout_system_constants_ =
|
||||
VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout descriptor_set_layout_shared_memory_and_edram_ =
|
||||
VK_NULL_HANDLE;
|
||||
std::array<VkDescriptorSetLayout,
|
||||
size_t(SingleTransientDescriptorLayout::kCount)>
|
||||
descriptor_set_layouts_single_transient_{};
|
||||
|
||||
// Descriptor set layouts are referenced by pipeline_layouts_.
|
||||
std::unordered_map<TextureDescriptorSetLayoutKey, VkDescriptorSetLayout,
|
||||
|
@ -359,6 +425,26 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
PipelineLayoutKey::Hasher>
|
||||
pipeline_layouts_;
|
||||
|
||||
ui::vulkan::SingleTypeDescriptorSetAllocator
|
||||
transient_descriptor_allocator_uniform_buffer_;
|
||||
ui::vulkan::SingleTypeDescriptorSetAllocator
|
||||
transient_descriptor_allocator_storage_buffer_;
|
||||
std::deque<UsedSingleTransientDescriptor> single_transient_descriptors_used_;
|
||||
std::array<std::vector<VkDescriptorSet>,
|
||||
size_t(SingleTransientDescriptorLayout::kCount)>
|
||||
single_transient_descriptors_free_;
|
||||
|
||||
ui::vulkan::SingleTypeDescriptorSetAllocator
|
||||
transient_descriptor_allocator_sampled_image_;
|
||||
ui::vulkan::SingleTypeDescriptorSetAllocator
|
||||
transient_descriptor_allocator_sampler_;
|
||||
std::deque<UsedTextureTransientDescriptorSet>
|
||||
texture_transient_descriptor_sets_used_;
|
||||
std::unordered_map<TextureDescriptorSetLayoutKey,
|
||||
std::vector<VkDescriptorSet>,
|
||||
TextureDescriptorSetLayoutKey::Hasher>
|
||||
texture_transient_descriptor_sets_free_;
|
||||
|
||||
std::unique_ptr<VulkanSharedMemory> shared_memory_;
|
||||
|
||||
std::unique_ptr<VulkanPrimitiveProcessor> primitive_processor_;
|
||||
|
|
|
@ -82,6 +82,7 @@ void VulkanPipelineCache::ClearCache() {
|
|||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
|
||||
// Destroy all pipelines.
|
||||
last_pipeline_ = nullptr;
|
||||
for (const auto& pipeline_pair : pipelines_) {
|
||||
if (pipeline_pair.second.pipeline != VK_NULL_HANDLE) {
|
||||
|
@ -90,10 +91,13 @@ void VulkanPipelineCache::ClearCache() {
|
|||
}
|
||||
pipelines_.clear();
|
||||
|
||||
// Destroy all shaders.
|
||||
for (auto it : shaders_) {
|
||||
delete it.second;
|
||||
}
|
||||
shaders_.clear();
|
||||
texture_binding_layout_map_.clear();
|
||||
texture_binding_layouts_.clear();
|
||||
}
|
||||
|
||||
VulkanShader* VulkanPipelineCache::LoadShader(xenos::ShaderType shader_type,
|
||||
|
@ -241,7 +245,23 @@ bool VulkanPipelineCache::ConfigurePipeline(
|
|||
|
||||
// Create the pipeline if not the latest and not already existing.
|
||||
const PipelineLayoutProvider* pipeline_layout =
|
||||
command_processor_.GetPipelineLayout(0, 0);
|
||||
command_processor_.GetPipelineLayout(
|
||||
pixel_shader
|
||||
? static_cast<const VulkanShader&>(pixel_shader->shader())
|
||||
.GetTextureBindingsAfterTranslation()
|
||||
.size()
|
||||
: 0,
|
||||
pixel_shader
|
||||
? static_cast<const VulkanShader&>(pixel_shader->shader())
|
||||
.GetSamplerBindingsAfterTranslation()
|
||||
.size()
|
||||
: 0,
|
||||
static_cast<const VulkanShader&>(vertex_shader->shader())
|
||||
.GetTextureBindingsAfterTranslation()
|
||||
.size(),
|
||||
static_cast<const VulkanShader&>(vertex_shader->shader())
|
||||
.GetSamplerBindingsAfterTranslation()
|
||||
.size());
|
||||
if (!pipeline_layout) {
|
||||
return false;
|
||||
}
|
||||
|
@ -277,14 +297,80 @@ bool VulkanPipelineCache::ConfigurePipeline(
|
|||
bool VulkanPipelineCache::TranslateAnalyzedShader(
|
||||
SpirvShaderTranslator& translator,
|
||||
VulkanShader::VulkanTranslation& translation) {
|
||||
VulkanShader& shader = static_cast<VulkanShader&>(translation.shader());
|
||||
|
||||
// Perform translation.
|
||||
// If this fails the shader will be marked as invalid and ignored later.
|
||||
if (!translator.TranslateAnalyzedShader(translation)) {
|
||||
XELOGE("Shader {:016X} translation failed; marking as ignored",
|
||||
translation.shader().ucode_data_hash());
|
||||
shader.ucode_data_hash());
|
||||
return false;
|
||||
}
|
||||
return translation.GetOrCreateShaderModule() != VK_NULL_HANDLE;
|
||||
if (translation.GetOrCreateShaderModule() == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(Triang3l): Log that the shader has been successfully translated in
|
||||
// common code.
|
||||
|
||||
// Set up the texture binding layout.
|
||||
if (shader.EnterBindingLayoutUserUIDSetup()) {
|
||||
// Obtain the unique IDs of the binding layout if there are any texture
|
||||
// bindings, for invalidation in the command processor.
|
||||
size_t texture_binding_layout_uid = kLayoutUIDEmpty;
|
||||
const std::vector<VulkanShader::TextureBinding>& texture_bindings =
|
||||
shader.GetTextureBindingsAfterTranslation();
|
||||
size_t texture_binding_count = texture_bindings.size();
|
||||
if (texture_binding_count) {
|
||||
size_t texture_binding_layout_bytes =
|
||||
texture_binding_count * sizeof(*texture_bindings.data());
|
||||
uint64_t texture_binding_layout_hash =
|
||||
XXH3_64bits(texture_bindings.data(), texture_binding_layout_bytes);
|
||||
auto found_range =
|
||||
texture_binding_layout_map_.equal_range(texture_binding_layout_hash);
|
||||
for (auto it = found_range.first; it != found_range.second; ++it) {
|
||||
if (it->second.vector_span_length == texture_binding_count &&
|
||||
!std::memcmp(
|
||||
texture_binding_layouts_.data() + it->second.vector_span_offset,
|
||||
texture_bindings.data(), texture_binding_layout_bytes)) {
|
||||
texture_binding_layout_uid = it->second.uid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (texture_binding_layout_uid == kLayoutUIDEmpty) {
|
||||
static_assert(
|
||||
kLayoutUIDEmpty == 0,
|
||||
"Layout UID is size + 1 because it's assumed that 0 is the UID for "
|
||||
"an empty layout");
|
||||
texture_binding_layout_uid = texture_binding_layout_map_.size() + 1;
|
||||
LayoutUID new_uid;
|
||||
new_uid.uid = texture_binding_layout_uid;
|
||||
new_uid.vector_span_offset = texture_binding_layouts_.size();
|
||||
new_uid.vector_span_length = texture_binding_count;
|
||||
texture_binding_layouts_.resize(new_uid.vector_span_offset +
|
||||
texture_binding_count);
|
||||
std::memcpy(
|
||||
texture_binding_layouts_.data() + new_uid.vector_span_offset,
|
||||
texture_bindings.data(), texture_binding_layout_bytes);
|
||||
texture_binding_layout_map_.emplace(texture_binding_layout_hash,
|
||||
new_uid);
|
||||
}
|
||||
}
|
||||
shader.SetTextureBindingLayoutUserUID(texture_binding_layout_uid);
|
||||
|
||||
// Use the sampler count for samplers because it's the only thing that must
|
||||
// be the same for layouts to be compatible in this case
|
||||
// (instruction-specified parameters are used as overrides for creating
|
||||
// actual samplers).
|
||||
static_assert(
|
||||
kLayoutUIDEmpty == 0,
|
||||
"Empty layout UID is assumed to be 0 because for bindful samplers, the "
|
||||
"UID is their count");
|
||||
shader.SetSamplerBindingLayoutUserUID(
|
||||
shader.GetSamplerBindingsAfterTranslation().size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::WritePipelineRenderTargetDescription(
|
||||
|
|
|
@ -39,6 +39,8 @@ class VulkanCommandProcessor;
|
|||
// implementations.
|
||||
class VulkanPipelineCache {
|
||||
public:
|
||||
static constexpr size_t kLayoutUIDEmpty = 0;
|
||||
|
||||
class PipelineLayoutProvider {
|
||||
public:
|
||||
virtual ~PipelineLayoutProvider() {}
|
||||
|
@ -278,6 +280,21 @@ class VulkanPipelineCache {
|
|||
// Reusable shader translator on the command processor thread.
|
||||
std::unique_ptr<SpirvShaderTranslator> shader_translator_;
|
||||
|
||||
struct LayoutUID {
|
||||
size_t uid;
|
||||
size_t vector_span_offset;
|
||||
size_t vector_span_length;
|
||||
};
|
||||
std::mutex layouts_mutex_;
|
||||
// Texture binding layouts of different shaders, for obtaining layout UIDs.
|
||||
std::vector<VulkanShader::TextureBinding> texture_binding_layouts_;
|
||||
// Map of texture binding layouts used by shaders, for obtaining UIDs. Keys
|
||||
// are XXH3 hashes of layouts, values need manual collision resolution using
|
||||
// layout_vector_offset:layout_length of texture_binding_layouts_.
|
||||
std::unordered_multimap<uint64_t, LayoutUID,
|
||||
xe::hash::IdentityHasher<uint64_t>>
|
||||
texture_binding_layout_map_;
|
||||
|
||||
// Ucode hash -> shader.
|
||||
std::unordered_map<uint64_t, VulkanShader*,
|
||||
xe::hash::IdentityHasher<uint64_t>>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -45,6 +46,10 @@ VkShaderModule VulkanShader::VulkanTranslation::GetOrCreateShaderModule() {
|
|||
if (provider.dfn().vkCreateShaderModule(provider.device(),
|
||||
&shader_module_create_info, nullptr,
|
||||
&shader_module_) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanShader::VulkanTranslation: Failed to create a Vulkan shader "
|
||||
"module for shader {:016X} modification {:016X}",
|
||||
shader().ucode_data_hash(), modification());
|
||||
MakeInvalid();
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
@ -57,8 +62,8 @@ VulkanShader::VulkanShader(const ui::vulkan::VulkanProvider& provider,
|
|||
const uint32_t* ucode_dwords,
|
||||
size_t ucode_dword_count,
|
||||
std::endian ucode_source_endian)
|
||||
: Shader(shader_type, ucode_data_hash, ucode_dwords, ucode_dword_count,
|
||||
ucode_source_endian),
|
||||
: SpirvShader(shader_type, ucode_data_hash, ucode_dwords, ucode_dword_count,
|
||||
ucode_source_endian),
|
||||
provider_(provider) {}
|
||||
|
||||
Shader::Translation* VulkanShader::CreateTranslationInstance(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/spirv_shader.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
|
@ -20,12 +20,12 @@ namespace xe {
|
|||
namespace gpu {
|
||||
namespace vulkan {
|
||||
|
||||
class VulkanShader : public Shader {
|
||||
class VulkanShader : public SpirvShader {
|
||||
public:
|
||||
class VulkanTranslation : public Translation {
|
||||
class VulkanTranslation : public SpirvTranslation {
|
||||
public:
|
||||
VulkanTranslation(VulkanShader& shader, uint64_t modification)
|
||||
: Translation(shader, modification) {}
|
||||
explicit VulkanTranslation(VulkanShader& shader, uint64_t modification)
|
||||
: SpirvTranslation(shader, modification) {}
|
||||
~VulkanTranslation() override;
|
||||
|
||||
VkShaderModule GetOrCreateShaderModule();
|
||||
|
@ -35,16 +35,43 @@ class VulkanShader : public Shader {
|
|||
VkShaderModule shader_module_ = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
VulkanShader(const ui::vulkan::VulkanProvider& provider,
|
||||
xenos::ShaderType shader_type, uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords, size_t ucode_dword_count,
|
||||
std::endian ucode_source_endian = std::endian::big);
|
||||
explicit VulkanShader(const ui::vulkan::VulkanProvider& provider,
|
||||
xenos::ShaderType shader_type, uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords, size_t ucode_dword_count,
|
||||
std::endian ucode_source_endian = std::endian::big);
|
||||
|
||||
// For owning subsystem like the pipeline cache, accessors for unique
|
||||
// identifiers (used instead of hashes to make sure collisions can't happen)
|
||||
// of binding layouts used by the shader, for invalidation if a shader with an
|
||||
// incompatible layout has been bound.
|
||||
size_t GetTextureBindingLayoutUserUID() const {
|
||||
return texture_binding_layout_user_uid_;
|
||||
}
|
||||
size_t GetSamplerBindingLayoutUserUID() const {
|
||||
return sampler_binding_layout_user_uid_;
|
||||
}
|
||||
// Modifications of the same shader can be translated on different threads.
|
||||
// The "set" function must only be called if "enter" returned true - these are
|
||||
// set up only once.
|
||||
bool EnterBindingLayoutUserUIDSetup() {
|
||||
return !binding_layout_user_uids_set_up_.test_and_set();
|
||||
}
|
||||
void SetTextureBindingLayoutUserUID(size_t uid) {
|
||||
texture_binding_layout_user_uid_ = uid;
|
||||
}
|
||||
void SetSamplerBindingLayoutUserUID(size_t uid) {
|
||||
sampler_binding_layout_user_uid_ = uid;
|
||||
}
|
||||
|
||||
protected:
|
||||
Translation* CreateTranslationInstance(uint64_t modification) override;
|
||||
|
||||
private:
|
||||
const ui::vulkan::VulkanProvider& provider_;
|
||||
|
||||
std::atomic_flag binding_layout_user_uids_set_up_ = ATOMIC_FLAG_INIT;
|
||||
size_t texture_binding_layout_user_uid_ = 0;
|
||||
size_t sampler_binding_layout_user_uid_ = 0;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
|
|
|
@ -45,6 +45,17 @@ class VulkanTextureCache final : public TextureCache {
|
|||
|
||||
void BeginSubmission(uint64_t new_submission_index) override;
|
||||
|
||||
VkImageView GetNullImageView(xenos::FetchOpDimension dimension) const {
|
||||
switch (dimension) {
|
||||
case xenos::FetchOpDimension::k3DOrStacked:
|
||||
return null_image_view_3d_;
|
||||
case xenos::FetchOpDimension::kCube:
|
||||
return null_image_view_cube_;
|
||||
default:
|
||||
return null_image_view_2d_array_;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t GetHostFormatSwizzle(TextureKey key) const override;
|
||||
|
||||
|
|
|
@ -94,8 +94,7 @@ size_t SingleLayoutDescriptorSetPool::Allocate() {
|
|||
if (dfn.vkAllocateDescriptorSets(device, &descriptor_set_allocate_info,
|
||||
&descriptor_set) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"SingleLayoutDescriptorSetPool: Failed to allocate a descriptor "
|
||||
"layout");
|
||||
"SingleLayoutDescriptorSetPool: Failed to allocate a descriptor set");
|
||||
if (current_pool_sets_remaining_ >= pool_set_count_) {
|
||||
// Failed to allocate in a new pool - something completely wrong, don't
|
||||
// store empty pools as full.
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/ui/vulkan/single_type_descriptor_set_allocator.h"
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
void SingleTypeDescriptorSetAllocator::Reset() {
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorPool, device,
|
||||
page_usable_latest_.pool);
|
||||
for (const std::pair<uint32_t, Page>& page_pair : pages_usable_) {
|
||||
dfn.vkDestroyDescriptorPool(device, page_pair.second.pool, nullptr);
|
||||
}
|
||||
pages_usable_.clear();
|
||||
for (VkDescriptorPool pool : pages_full_) {
|
||||
dfn.vkDestroyDescriptorPool(device, pool, nullptr);
|
||||
}
|
||||
pages_full_.clear();
|
||||
}
|
||||
|
||||
VkDescriptorSet SingleTypeDescriptorSetAllocator::Allocate(
|
||||
VkDescriptorSetLayout descriptor_set_layout, uint32_t descriptor_count) {
|
||||
assert_not_zero(descriptor_count);
|
||||
if (descriptor_count == 0) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
|
||||
VkDescriptorSetAllocateInfo descriptor_set_allocate_info;
|
||||
descriptor_set_allocate_info.sType =
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
descriptor_set_allocate_info.pNext = nullptr;
|
||||
descriptor_set_allocate_info.descriptorSetCount = 1;
|
||||
descriptor_set_allocate_info.pSetLayouts = &descriptor_set_layout;
|
||||
VkDescriptorSet descriptor_set;
|
||||
|
||||
if (descriptor_count > descriptor_pool_size_.descriptorCount) {
|
||||
// Can't allocate in the pool, need a dedicated allocation.
|
||||
VkDescriptorPoolSize dedicated_descriptor_pool_size;
|
||||
dedicated_descriptor_pool_size.type = descriptor_pool_size_.type;
|
||||
dedicated_descriptor_pool_size.descriptorCount = descriptor_count;
|
||||
VkDescriptorPoolCreateInfo dedicated_descriptor_pool_create_info;
|
||||
dedicated_descriptor_pool_create_info.sType =
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
dedicated_descriptor_pool_create_info.pNext = nullptr;
|
||||
dedicated_descriptor_pool_create_info.flags = 0;
|
||||
dedicated_descriptor_pool_create_info.maxSets = 1;
|
||||
dedicated_descriptor_pool_create_info.poolSizeCount = 1;
|
||||
dedicated_descriptor_pool_create_info.pPoolSizes =
|
||||
&dedicated_descriptor_pool_size;
|
||||
VkDescriptorPool dedicated_descriptor_pool;
|
||||
if (dfn.vkCreateDescriptorPool(
|
||||
device, &dedicated_descriptor_pool_create_info, nullptr,
|
||||
&dedicated_descriptor_pool) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"SingleTypeDescriptorSetAllocator: Failed to create a dedicated pool "
|
||||
"for {} descriptors",
|
||||
dedicated_descriptor_pool_size.descriptorCount);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
descriptor_set_allocate_info.descriptorPool = dedicated_descriptor_pool;
|
||||
if (dfn.vkAllocateDescriptorSets(device, &descriptor_set_allocate_info,
|
||||
&descriptor_set) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"SingleTypeDescriptorSetAllocator: Failed to allocate {} descriptors "
|
||||
"in a dedicated pool",
|
||||
descriptor_count);
|
||||
dfn.vkDestroyDescriptorPool(device, dedicated_descriptor_pool, nullptr);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
pages_full_.push_back(dedicated_descriptor_pool);
|
||||
return descriptor_set;
|
||||
}
|
||||
|
||||
// Try allocating from the latest page an allocation has happened from, to
|
||||
// avoid detaching from the map and re-attaching for every allocation.
|
||||
if (page_usable_latest_.pool != VK_NULL_HANDLE) {
|
||||
assert_not_zero(page_usable_latest_.descriptors_remaining);
|
||||
assert_not_zero(page_usable_latest_.descriptor_sets_remaining);
|
||||
if (page_usable_latest_.descriptors_remaining >= descriptor_count) {
|
||||
descriptor_set_allocate_info.descriptorPool = page_usable_latest_.pool;
|
||||
if (dfn.vkAllocateDescriptorSets(device, &descriptor_set_allocate_info,
|
||||
&descriptor_set) == VK_SUCCESS) {
|
||||
page_usable_latest_.descriptors_remaining -= descriptor_count;
|
||||
--page_usable_latest_.descriptor_sets_remaining;
|
||||
if (!page_usable_latest_.descriptors_remaining ||
|
||||
!page_usable_latest_.descriptor_sets_remaining) {
|
||||
pages_full_.push_back(page_usable_latest_.pool);
|
||||
page_usable_latest_.pool = VK_NULL_HANDLE;
|
||||
}
|
||||
return descriptor_set;
|
||||
}
|
||||
// Failed to allocate internally even though there should be enough space,
|
||||
// don't try to allocate from this pool again at all.
|
||||
pages_full_.push_back(page_usable_latest_.pool);
|
||||
page_usable_latest_.pool = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
// If allocating from the latest pool wasn't possible, pick any that has free
|
||||
// space. Prefer filling pages that have the most free space as they can more
|
||||
// likely be used for more allocations later.
|
||||
while (!pages_usable_.empty()) {
|
||||
auto page_usable_last_it = std::prev(pages_usable_.cend());
|
||||
if (page_usable_last_it->second.descriptors_remaining < descriptor_count) {
|
||||
// All other pages_usable_ entries have fewer free descriptors too (the
|
||||
// remaining count is the map key).
|
||||
break;
|
||||
}
|
||||
// Remove the page from the map unconditionally - in case of a successful
|
||||
// allocation, it will have a different number of free descriptors, thus a
|
||||
// new map key (but it will also become page_usable_latest_ instead even),
|
||||
// or will become full, and in case of a failure to allocate internally even
|
||||
// though there still should be enough space, it should never be allocated
|
||||
// from again.
|
||||
Page map_page = pages_usable_.crend()->second;
|
||||
pages_usable_.erase(page_usable_last_it);
|
||||
descriptor_set_allocate_info.descriptorPool = map_page.pool;
|
||||
if (dfn.vkAllocateDescriptorSets(device, &descriptor_set_allocate_info,
|
||||
&descriptor_set) != VK_SUCCESS) {
|
||||
pages_full_.push_back(map_page.pool);
|
||||
continue;
|
||||
}
|
||||
map_page.descriptors_remaining -= descriptor_count;
|
||||
--map_page.descriptor_sets_remaining;
|
||||
if (!map_page.descriptors_remaining ||
|
||||
!map_page.descriptor_sets_remaining) {
|
||||
pages_full_.push_back(map_page.pool);
|
||||
} else {
|
||||
if (page_usable_latest_.pool != VK_NULL_HANDLE) {
|
||||
// Make the page with more free descriptors the next to allocate from.
|
||||
if (map_page.descriptors_remaining >
|
||||
page_usable_latest_.descriptors_remaining) {
|
||||
pages_usable_.emplace(page_usable_latest_.descriptors_remaining,
|
||||
page_usable_latest_);
|
||||
page_usable_latest_ = map_page;
|
||||
} else {
|
||||
pages_usable_.emplace(map_page.descriptors_remaining, map_page);
|
||||
}
|
||||
} else {
|
||||
page_usable_latest_ = map_page;
|
||||
}
|
||||
}
|
||||
return descriptor_set;
|
||||
}
|
||||
|
||||
// Try allocating from a new page.
|
||||
VkDescriptorPoolCreateInfo new_descriptor_pool_create_info;
|
||||
new_descriptor_pool_create_info.sType =
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
new_descriptor_pool_create_info.pNext = nullptr;
|
||||
new_descriptor_pool_create_info.flags = 0;
|
||||
new_descriptor_pool_create_info.maxSets = descriptor_sets_per_page_;
|
||||
new_descriptor_pool_create_info.poolSizeCount = 1;
|
||||
new_descriptor_pool_create_info.pPoolSizes = &descriptor_pool_size_;
|
||||
VkDescriptorPool new_descriptor_pool;
|
||||
if (dfn.vkCreateDescriptorPool(device, &new_descriptor_pool_create_info,
|
||||
nullptr, &new_descriptor_pool) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"SingleTypeDescriptorSetAllocator: Failed to create a pool for {} sets "
|
||||
"with {} descriptors",
|
||||
descriptor_sets_per_page_, descriptor_pool_size_.descriptorCount);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
descriptor_set_allocate_info.descriptorPool = new_descriptor_pool;
|
||||
if (dfn.vkAllocateDescriptorSets(device, &descriptor_set_allocate_info,
|
||||
&descriptor_set) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"SingleTypeDescriptorSetAllocator: Failed to allocate {} descriptors",
|
||||
descriptor_count);
|
||||
dfn.vkDestroyDescriptorPool(device, new_descriptor_pool, nullptr);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
Page new_page;
|
||||
new_page.pool = new_descriptor_pool;
|
||||
new_page.descriptors_remaining =
|
||||
descriptor_pool_size_.descriptorCount - descriptor_count;
|
||||
new_page.descriptor_sets_remaining = descriptor_sets_per_page_ - 1;
|
||||
if (!new_page.descriptors_remaining || !new_page.descriptor_sets_remaining) {
|
||||
pages_full_.push_back(new_page.pool);
|
||||
} else {
|
||||
if (page_usable_latest_.pool != VK_NULL_HANDLE) {
|
||||
// Make the page with more free descriptors the next to allocate from.
|
||||
if (new_page.descriptors_remaining >
|
||||
page_usable_latest_.descriptors_remaining) {
|
||||
pages_usable_.emplace(page_usable_latest_.descriptors_remaining,
|
||||
page_usable_latest_);
|
||||
page_usable_latest_ = new_page;
|
||||
} else {
|
||||
pages_usable_.emplace(new_page.descriptors_remaining, new_page);
|
||||
}
|
||||
} else {
|
||||
page_usable_latest_ = new_page;
|
||||
}
|
||||
}
|
||||
return descriptor_set;
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_VULKAN_SINGLE_TYPE_DESCRIPTOR_SET_ALLOCATOR_H_
|
||||
#define XENIA_UI_VULKAN_SINGLE_TYPE_DESCRIPTOR_SET_ALLOCATOR_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
// Allocates multiple descriptors of a single type in descriptor set layouts
|
||||
// consisting of descriptors of only that type. There's no way to free these
|
||||
// descriptors within the SingleTypeDescriptorSetAllocator, per-layout free
|
||||
// lists should be used externally.
|
||||
class SingleTypeDescriptorSetAllocator {
|
||||
public:
|
||||
explicit SingleTypeDescriptorSetAllocator(
|
||||
const ui::vulkan::VulkanProvider& provider,
|
||||
VkDescriptorType descriptor_type, uint32_t descriptors_per_page,
|
||||
uint32_t descriptor_sets_per_page)
|
||||
: provider_(provider),
|
||||
descriptor_sets_per_page_(descriptor_sets_per_page) {
|
||||
assert_not_zero(descriptor_sets_per_page_);
|
||||
descriptor_pool_size_.type = descriptor_type;
|
||||
// Not allocating sets with 0 descriptors using the allocator - pointless to
|
||||
// have the descriptor count below the set count.
|
||||
descriptor_pool_size_.descriptorCount =
|
||||
std::max(descriptors_per_page, descriptor_sets_per_page);
|
||||
}
|
||||
SingleTypeDescriptorSetAllocator(
|
||||
const SingleTypeDescriptorSetAllocator& allocator) = delete;
|
||||
SingleTypeDescriptorSetAllocator& operator=(
|
||||
const SingleTypeDescriptorSetAllocator& allocator) = delete;
|
||||
~SingleTypeDescriptorSetAllocator() { Reset(); }
|
||||
|
||||
void Reset();
|
||||
|
||||
VkDescriptorSet Allocate(VkDescriptorSetLayout descriptor_set_layout,
|
||||
uint32_t descriptor_count);
|
||||
|
||||
private:
|
||||
struct Page {
|
||||
VkDescriptorPool pool;
|
||||
uint32_t descriptors_remaining;
|
||||
uint32_t descriptor_sets_remaining;
|
||||
};
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider_;
|
||||
|
||||
VkDescriptorPoolSize descriptor_pool_size_;
|
||||
uint32_t descriptor_sets_per_page_;
|
||||
|
||||
std::vector<VkDescriptorPool> pages_full_;
|
||||
// Because allocations must be contiguous, overflow may happen even if a page
|
||||
// still has free descriptors, so multiple pages may have free space.
|
||||
// To avoid removing and re-adding the page to the map that keeps them sorted
|
||||
// (the key is the number of free descriptors remaining, and it changes at
|
||||
// every allocation from a page), instead of always looking for a free space
|
||||
// in the map, maintaining one page outside the map, and allocation attempts
|
||||
// will be made from that page first.
|
||||
std::multimap<uint32_t, Page> pages_usable_;
|
||||
// Doesn't exist if page_usable_latest_.pool == VK_NULL_HANDLE.
|
||||
Page page_usable_latest_ = {};
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_VULKAN_SINGLE_TYPE_DESCRIPTOR_SET_ALLOCATOR_H_
|
|
@ -1,162 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/ui/vulkan/transient_descriptor_pool.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
TransientDescriptorPool::TransientDescriptorPool(
|
||||
const VulkanProvider& provider, VkDescriptorType descriptor_type,
|
||||
uint32_t page_descriptor_set_count, uint32_t page_descriptor_count)
|
||||
: provider_(provider),
|
||||
descriptor_type_(descriptor_type),
|
||||
page_descriptor_set_count_(page_descriptor_set_count),
|
||||
page_descriptor_count_(page_descriptor_count) {
|
||||
assert_not_zero(page_descriptor_set_count);
|
||||
assert_true(page_descriptor_set_count <= page_descriptor_count);
|
||||
}
|
||||
|
||||
TransientDescriptorPool::~TransientDescriptorPool() { ClearCache(); }
|
||||
|
||||
void TransientDescriptorPool::Reclaim(uint64_t completed_submission_index) {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
while (!pages_submitted_.empty()) {
|
||||
const auto& descriptor_pool_pair = pages_submitted_.front();
|
||||
if (descriptor_pool_pair.second > completed_submission_index) {
|
||||
break;
|
||||
}
|
||||
dfn.vkResetDescriptorPool(device, descriptor_pool_pair.first, 0);
|
||||
pages_writable_.push_back(descriptor_pool_pair.first);
|
||||
pages_submitted_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void TransientDescriptorPool::ClearCache() {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
for (const auto& descriptor_pool_pair : pages_submitted_) {
|
||||
dfn.vkDestroyDescriptorPool(device, descriptor_pool_pair.first, nullptr);
|
||||
}
|
||||
pages_submitted_.clear();
|
||||
page_current_descriptors_used_ = 0;
|
||||
page_current_descriptor_sets_used_ = 0;
|
||||
page_current_last_submission_ = 0;
|
||||
for (VkDescriptorPool descriptor_pool : pages_writable_) {
|
||||
dfn.vkDestroyDescriptorPool(device, descriptor_pool, nullptr);
|
||||
}
|
||||
pages_writable_.clear();
|
||||
}
|
||||
|
||||
VkDescriptorSet TransientDescriptorPool::Request(
|
||||
uint64_t submission_index, VkDescriptorSetLayout layout,
|
||||
uint32_t layout_descriptor_count) {
|
||||
assert_true(submission_index >= page_current_last_submission_);
|
||||
assert_not_zero(layout_descriptor_count);
|
||||
assert_true(layout_descriptor_count <= page_descriptor_count_);
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
|
||||
VkDescriptorSetAllocateInfo descriptor_set_allocate_info;
|
||||
descriptor_set_allocate_info.sType =
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
descriptor_set_allocate_info.pNext = nullptr;
|
||||
descriptor_set_allocate_info.descriptorSetCount = 1;
|
||||
descriptor_set_allocate_info.pSetLayouts = &layout;
|
||||
VkDescriptorSet descriptor_set;
|
||||
|
||||
// Try to allocate as normal.
|
||||
// TODO(Triang3l): Investigate the possibility of reuse of descriptor sets, as
|
||||
// vkAllocateDescriptorSets may be implemented suboptimally.
|
||||
if (!pages_writable_.empty()) {
|
||||
if (page_current_descriptor_sets_used_ < page_descriptor_set_count_ &&
|
||||
page_current_descriptors_used_ + layout_descriptor_count <=
|
||||
page_descriptor_count_) {
|
||||
descriptor_set_allocate_info.descriptorPool = pages_writable_.front();
|
||||
switch (dfn.vkAllocateDescriptorSets(
|
||||
device, &descriptor_set_allocate_info, &descriptor_set)) {
|
||||
case VK_SUCCESS:
|
||||
page_current_last_submission_ = submission_index;
|
||||
++page_current_descriptor_sets_used_;
|
||||
page_current_descriptors_used_ += layout_descriptor_count;
|
||||
return descriptor_set;
|
||||
case VK_ERROR_FRAGMENTED_POOL:
|
||||
case VK_ERROR_OUT_OF_POOL_MEMORY:
|
||||
// Need to create a new pool.
|
||||
break;
|
||||
default:
|
||||
XELOGE(
|
||||
"Failed to allocate a transient Vulkan descriptor set with {} "
|
||||
"descriptors of type {}",
|
||||
layout_descriptor_count, uint32_t(descriptor_type_));
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
// Overflow - go to the next pool.
|
||||
pages_submitted_.emplace_back(pages_writable_.front(),
|
||||
page_current_last_submission_);
|
||||
pages_writable_.front() = pages_writable_.back();
|
||||
pages_writable_.pop_back();
|
||||
page_current_descriptor_sets_used_ = 0;
|
||||
page_current_descriptors_used_ = 0;
|
||||
}
|
||||
|
||||
if (pages_writable_.empty()) {
|
||||
VkDescriptorPoolSize descriptor_pool_size;
|
||||
descriptor_pool_size.type = descriptor_type_;
|
||||
descriptor_pool_size.descriptorCount = page_descriptor_count_;
|
||||
VkDescriptorPoolCreateInfo descriptor_pool_create_info;
|
||||
descriptor_pool_create_info.sType =
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
descriptor_pool_create_info.pNext = nullptr;
|
||||
descriptor_pool_create_info.flags = 0;
|
||||
descriptor_pool_create_info.maxSets = page_descriptor_set_count_;
|
||||
descriptor_pool_create_info.poolSizeCount = 1;
|
||||
descriptor_pool_create_info.pPoolSizes = &descriptor_pool_size;
|
||||
VkDescriptorPool descriptor_pool;
|
||||
if (dfn.vkCreateDescriptorPool(device, &descriptor_pool_create_info,
|
||||
nullptr, &descriptor_pool) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"Failed to create a transient Vulkan descriptor pool for {} sets of "
|
||||
"up to {} descriptors of type {}",
|
||||
page_descriptor_set_count_, page_descriptor_count_,
|
||||
uint32_t(descriptor_type_));
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
pages_writable_.push_back(descriptor_pool);
|
||||
}
|
||||
|
||||
// Try to allocate after handling overflow.
|
||||
descriptor_set_allocate_info.descriptorPool = pages_writable_.front();
|
||||
if (dfn.vkAllocateDescriptorSets(device, &descriptor_set_allocate_info,
|
||||
&descriptor_set) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"Failed to allocate a transient Vulkan descriptor set with {} "
|
||||
"descriptors of type {}",
|
||||
layout_descriptor_count, uint32_t(descriptor_type_));
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
page_current_last_submission_ = submission_index;
|
||||
++page_current_descriptor_sets_used_;
|
||||
page_current_descriptors_used_ += layout_descriptor_count;
|
||||
return descriptor_set;
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_VULKAN_TRANSIENT_DESCRIPTOR_POOL_H_
|
||||
#define XENIA_UI_VULKAN_TRANSIENT_DESCRIPTOR_POOL_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
// A pool of descriptor pools for single-submission use. For simplicity of
|
||||
// tracking when overflow happens, only allocating descriptors for sets
|
||||
// containing descriptors of a single type.
|
||||
class TransientDescriptorPool {
|
||||
public:
|
||||
TransientDescriptorPool(const VulkanProvider& provider,
|
||||
VkDescriptorType descriptor_type,
|
||||
uint32_t page_descriptor_set_count,
|
||||
uint32_t page_descriptor_count);
|
||||
~TransientDescriptorPool();
|
||||
|
||||
void Reclaim(uint64_t completed_submission_index);
|
||||
void ClearCache();
|
||||
|
||||
// Returns the allocated set, or VK_NULL_HANDLE if failed to allocate.
|
||||
VkDescriptorSet Request(uint64_t submission_index,
|
||||
VkDescriptorSetLayout layout,
|
||||
uint32_t layout_descriptor_count);
|
||||
|
||||
private:
|
||||
const VulkanProvider& provider_;
|
||||
|
||||
VkDescriptorType descriptor_type_;
|
||||
uint32_t page_descriptor_set_count_;
|
||||
uint32_t page_descriptor_count_;
|
||||
|
||||
std::vector<VkDescriptorPool> pages_writable_;
|
||||
uint64_t page_current_last_submission_ = 0;
|
||||
uint32_t page_current_descriptor_sets_used_ = 0;
|
||||
uint32_t page_current_descriptors_used_ = 0;
|
||||
std::deque<std::pair<VkDescriptorPool, uint64_t>> pages_submitted_;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_VULKAN_TRANSIENT_DESCRIPTOR_POOL_H_
|
Loading…
Reference in New Issue