Vulkan TextureCache: Use a hashed and fenced pool for descriptors
This commit is contained in:
parent
a16dc261da
commit
366eeeaa60
|
@ -113,21 +113,15 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file,
|
|||
device_(device),
|
||||
staging_buffer_(device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
kStagingBufferSize) {
|
||||
VkResult err = VK_SUCCESS;
|
||||
|
||||
// Descriptor pool used for all of our cached descriptors.
|
||||
VkDescriptorPoolCreateInfo descriptor_pool_info;
|
||||
descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
descriptor_pool_info.pNext = nullptr;
|
||||
descriptor_pool_info.flags =
|
||||
VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||
descriptor_pool_info.maxSets = 32768;
|
||||
VkDescriptorPoolSize pool_sizes[1];
|
||||
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
pool_sizes[0].descriptorCount = 32768;
|
||||
descriptor_pool_info.poolSizeCount = 1;
|
||||
descriptor_pool_info.pPoolSizes = pool_sizes;
|
||||
auto err = vkCreateDescriptorPool(*device_, &descriptor_pool_info, nullptr,
|
||||
&descriptor_pool_);
|
||||
CheckResult(err, "vkCreateDescriptorPool");
|
||||
descriptor_pool_ = std::make_unique<ui::vulkan::DescriptorPool>(
|
||||
*device_, 32768,
|
||||
std::vector<VkDescriptorPoolSize>(pool_sizes, std::end(pool_sizes)));
|
||||
|
||||
// Create the descriptor set layout used for rendering.
|
||||
// We always have the same number of samplers but only some are used.
|
||||
|
@ -177,7 +171,6 @@ TextureCache::~TextureCache() {
|
|||
|
||||
vkDestroyDescriptorSetLayout(*device_, texture_descriptor_set_layout_,
|
||||
nullptr);
|
||||
vkDestroyDescriptorPool(*device_, descriptor_pool_, nullptr);
|
||||
}
|
||||
|
||||
TextureCache::Texture* TextureCache::AllocateTexture(
|
||||
|
@ -1191,10 +1184,44 @@ bool TextureCache::UploadTextureCube(VkCommandBuffer command_buffer,
|
|||
return true;
|
||||
}
|
||||
|
||||
void TextureCache::HashTextureBindings(
|
||||
XXH64_state_t* hash_state, uint32_t& fetch_mask,
|
||||
const std::vector<Shader::TextureBinding>& bindings) {
|
||||
for (auto& binding : bindings) {
|
||||
uint32_t fetch_bit = 1 << binding.fetch_constant;
|
||||
if (fetch_mask & fetch_bit) {
|
||||
// We've covered this binding.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& regs = *register_file_;
|
||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + binding.fetch_constant * 6;
|
||||
auto group =
|
||||
reinterpret_cast<const xenos::xe_gpu_fetch_group_t*>(®s.values[r]);
|
||||
auto& fetch = group->texture_fetch;
|
||||
|
||||
XXH64_update(hash_state, &fetch, sizeof(fetch));
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorSet TextureCache::PrepareTextureSet(
|
||||
VkCommandBuffer command_buffer, VkFence completion_fence,
|
||||
const std::vector<Shader::TextureBinding>& vertex_bindings,
|
||||
const std::vector<Shader::TextureBinding>& pixel_bindings) {
|
||||
XXH64_state_t hash_state;
|
||||
XXH64_reset(&hash_state, 0);
|
||||
|
||||
// (quickly) Generate a hash.
|
||||
uint32_t fetch_mask = 0;
|
||||
HashTextureBindings(&hash_state, fetch_mask, vertex_bindings);
|
||||
HashTextureBindings(&hash_state, fetch_mask, pixel_bindings);
|
||||
uint64_t hash = XXH64_digest(&hash_state);
|
||||
for (auto it = texture_bindings_.find(hash); it != texture_bindings_.end();
|
||||
++it) {
|
||||
// TODO(DrChat): We need to compare the bindings and ensure they're equal.
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Clear state.
|
||||
auto update_set_info = &update_set_info_;
|
||||
update_set_info->has_setup_fetch_mask = 0;
|
||||
|
@ -1217,19 +1244,15 @@ VkDescriptorSet TextureCache::PrepareTextureSet(
|
|||
// TODO(benvanik): actually bail out here?
|
||||
}
|
||||
|
||||
// TODO(benvanik): reuse.
|
||||
VkDescriptorSet descriptor_set = nullptr;
|
||||
VkDescriptorSetAllocateInfo set_alloc_info;
|
||||
set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
set_alloc_info.pNext = nullptr;
|
||||
set_alloc_info.descriptorPool = descriptor_pool_;
|
||||
set_alloc_info.descriptorSetCount = 1;
|
||||
set_alloc_info.pSetLayouts = &texture_descriptor_set_layout_;
|
||||
auto err =
|
||||
vkAllocateDescriptorSets(*device_, &set_alloc_info, &descriptor_set);
|
||||
CheckResult(err, "vkAllocateDescriptorSets");
|
||||
// Open a new batch of descriptor sets (for this frame)
|
||||
if (!descriptor_pool_->has_open_batch()) {
|
||||
descriptor_pool_->BeginBatch(completion_fence);
|
||||
}
|
||||
|
||||
if (err != VK_SUCCESS) {
|
||||
// TODO(benvanik): reuse.
|
||||
auto descriptor_set =
|
||||
descriptor_pool_->AcquireEntry(texture_descriptor_set_layout_);
|
||||
if (!descriptor_set) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1243,7 +1266,7 @@ VkDescriptorSet TextureCache::PrepareTextureSet(
|
|||
update_set_info->image_writes, 0, nullptr);
|
||||
}
|
||||
|
||||
in_flight_sets_.push_back({descriptor_set, completion_fence});
|
||||
texture_bindings_[hash] = descriptor_set;
|
||||
return descriptor_set;
|
||||
}
|
||||
|
||||
|
@ -1351,20 +1374,16 @@ void TextureCache::ClearCache() {
|
|||
}
|
||||
|
||||
void TextureCache::Scavenge() {
|
||||
// Free unused descriptor sets
|
||||
for (auto it = in_flight_sets_.begin(); it != in_flight_sets_.end();) {
|
||||
if (vkGetFenceStatus(*device_, it->second) == VK_SUCCESS) {
|
||||
// We can free this one.
|
||||
vkFreeDescriptorSets(*device_, descriptor_pool_, 1, &it->first);
|
||||
it = in_flight_sets_.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We've encountered an item that hasn't been used yet, so any items
|
||||
// afterwards are guaranteed to be unused.
|
||||
break;
|
||||
// Close any open descriptor pool batches
|
||||
if (descriptor_pool_->has_open_batch()) {
|
||||
descriptor_pool_->EndBatch();
|
||||
}
|
||||
|
||||
// Free unused descriptor sets
|
||||
// TODO(DrChat): These sets could persist across frames, we just need a smart
|
||||
// way to detect if they're unused and free them.
|
||||
texture_bindings_.clear();
|
||||
descriptor_pool_->Scavenge();
|
||||
staging_buffer_.Scavenge();
|
||||
|
||||
// Kill all pending delete textures.
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/circular_buffer.h"
|
||||
#include "xenia/ui/vulkan/fenced_pools.h"
|
||||
#include "xenia/ui/vulkan/vulkan.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
|
@ -165,6 +166,8 @@ class TextureCache {
|
|||
VkFence completion_fence, Texture* dest,
|
||||
const TextureInfo& src);
|
||||
|
||||
void HashTextureBindings(XXH64_state_t* hash_state, uint32_t& fetch_mask,
|
||||
const std::vector<Shader::TextureBinding>& bindings);
|
||||
bool SetupTextureBindings(
|
||||
VkCommandBuffer command_buffer, VkFence completion_fence,
|
||||
UpdateSetInfo* update_set_info,
|
||||
|
@ -181,9 +184,9 @@ class TextureCache {
|
|||
ui::vulkan::VulkanDevice* device_ = nullptr;
|
||||
VkQueue device_queue_ = nullptr;
|
||||
|
||||
VkDescriptorPool descriptor_pool_ = nullptr;
|
||||
std::unique_ptr<xe::ui::vulkan::DescriptorPool> descriptor_pool_ = nullptr;
|
||||
std::unordered_map<uint64_t, VkDescriptorSet> texture_bindings_;
|
||||
VkDescriptorSetLayout texture_descriptor_set_layout_ = nullptr;
|
||||
std::list<std::pair<VkDescriptorSet, VkFence>> in_flight_sets_;
|
||||
|
||||
ui::vulkan::CircularBuffer staging_buffer_;
|
||||
std::unordered_map<uint64_t, Texture*> textures_;
|
||||
|
|
Loading…
Reference in New Issue