Vulkan: Fill unused descriptor array elements with a dummy image (required by API)

This commit is contained in:
DrChat 2017-02-13 14:43:06 -06:00
parent 75b34b83a6
commit 4a99b88650
3 changed files with 155 additions and 4 deletions

View File

@ -127,15 +127,15 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file,
// On low sampler counts: Rarely would we experience over 16 unique samplers. // On low sampler counts: Rarely would we experience over 16 unique samplers.
// This code could be refactored to scale up/down to the # of samplers. // This code could be refactored to scale up/down to the # of samplers.
auto& limits = device_->device_info().properties.limits; auto& limits = device_->device_info().properties.limits;
if (limits.maxPerStageDescriptorSamplers < kMaxTextureSamplers * 4 || if (limits.maxPerStageDescriptorSamplers < kMaxTextureSamplers ||
limits.maxPerStageDescriptorSampledImages < kMaxTextureSamplers * 4) { limits.maxPerStageDescriptorSampledImages < kMaxTextureSamplers) {
XELOGE( XELOGE(
"Physical device is unable to support required number of sampled " "Physical device is unable to support required number of sampled "
"images! Expect instability! (maxPerStageDescriptorSamplers=%d, " "images! Expect instability! (maxPerStageDescriptorSamplers=%d, "
"maxPerStageDescriptorSampledImages=%d)", "maxPerStageDescriptorSampledImages=%d)",
limits.maxPerStageDescriptorSamplers, limits.maxPerStageDescriptorSamplers,
limits.maxPerStageDescriptorSampledImages); limits.maxPerStageDescriptorSampledImages);
assert_always(); // assert_always();
} }
// Create the descriptor set layout used for rendering. // Create the descriptor set layout used for rendering.
@ -170,6 +170,7 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file,
invalidated_textures_ = &invalidated_textures_sets_[0]; invalidated_textures_ = &invalidated_textures_sets_[0];
device_queue_ = device_->AcquireQueue(); device_queue_ = device_->AcquireQueue();
SetupEmptySet();
} }
TextureCache::~TextureCache() { TextureCache::~TextureCache() {
@ -177,6 +178,7 @@ TextureCache::~TextureCache() {
device_->ReleaseQueue(device_queue_); device_->ReleaseQueue(device_queue_);
} }
DestroyEmptySet();
for (auto it = samplers_.begin(); it != samplers_.end(); ++it) { for (auto it = samplers_.begin(); it != samplers_.end(); ++it) {
vkDestroySampler(*device_, it->second->sampler, nullptr); vkDestroySampler(*device_, it->second->sampler, nullptr);
delete it->second; delete it->second;
@ -187,6 +189,127 @@ TextureCache::~TextureCache() {
nullptr); nullptr);
} }
void TextureCache::SetupEmptySet() {
// Create an image first.
VkImageCreateInfo image_info = {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.imageType = VK_IMAGE_TYPE_2D;
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
image_info.format = format;
image_info.extent = {1, 1, 1};
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.queueFamilyIndexCount = 0;
image_info.pQueueFamilyIndices = nullptr;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImage image;
auto err = vkCreateImage(*device_, &image_info, nullptr, &image);
CheckResult(err, "vkCreateImage");
VkMemoryRequirements mem_requirements;
vkGetImageMemoryRequirements(*device_, image, &mem_requirements);
// TODO: Use a circular buffer or something else to allocate this memory.
// The device has a limited amount (around 64) of memory allocations that we
// can make.
// Now that we have the size, back the image with GPU memory.
auto memory = device_->AllocateMemory(mem_requirements, 0);
if (!memory) {
// Crap.
assert_always();
vkDestroyImage(*device_, image, nullptr);
return;
}
err = vkBindImageMemory(*device_, image, memory, 0);
CheckResult(err, "vkBindImageMemory");
VkImageViewCreateInfo view_info;
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.pNext = nullptr;
view_info.flags = 0;
view_info.image = image;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = format;
view_info.components = {VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE,
VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE};
view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
err = vkCreateImageView(*device_, &view_info, nullptr, &empty_image_view_);
CheckResult(err, "vkCreateImageView");
// Empty image is setup!
empty_image_ = image;
empty_image_memory_ = memory;
// Setup an empty sampler
VkSamplerCreateInfo sampler_info;
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_info.pNext = nullptr;
sampler_info.flags = 0;
sampler_info.magFilter = VK_FILTER_NEAREST;
sampler_info.minFilter = VK_FILTER_NEAREST;
sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler_info.mipLodBias = 0.f;
sampler_info.anisotropyEnable = VK_FALSE;
sampler_info.maxAnisotropy = 0.f;
sampler_info.compareEnable = VK_FALSE;
sampler_info.compareOp = VK_COMPARE_OP_NEVER;
sampler_info.minLod = 0.f;
sampler_info.maxLod = 0.f;
sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
sampler_info.unnormalizedCoordinates = VK_FALSE;
err = vkCreateSampler(*device_, &sampler_info, nullptr, &empty_sampler_);
CheckResult(err, "vkCreateSampler");
// Okay, allocate and setup an empty descriptor set.
VkDescriptorPool pool = descriptor_pool_->descriptor_pool();
VkDescriptorSetAllocateInfo alloc_info;
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.pNext = nullptr;
alloc_info.descriptorPool = pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &texture_descriptor_set_layout_;
vkAllocateDescriptorSets(*device_, &alloc_info, &empty_set_);
VkWriteDescriptorSet empty_write;
empty_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
empty_write.pNext = nullptr;
empty_write.dstSet = empty_set_;
empty_write.dstBinding = 0;
empty_write.dstArrayElement = 0;
empty_write.descriptorCount = 32;
empty_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
VkDescriptorImageInfo info[32];
std::memset(info, 0, sizeof(info));
for (int i = 0; i < 32; i++) {
info[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
info[i].imageView = empty_image_view_;
info[i].sampler = empty_sampler_;
}
empty_write.pImageInfo = info;
vkUpdateDescriptorSets(*device_, 1, &empty_write, 0, nullptr);
}
void TextureCache::DestroyEmptySet() {
vkFreeDescriptorSets(*device_, descriptor_pool_->descriptor_pool(), 1,
&empty_set_);
vkDestroySampler(*device_, empty_sampler_, nullptr);
vkDestroyImageView(*device_, empty_image_view_, nullptr);
vkDestroyImage(*device_, empty_image_, nullptr);
vkFreeMemory(*device_, empty_image_memory_, nullptr);
}
TextureCache::Texture* TextureCache::AllocateTexture( TextureCache::Texture* TextureCache::AllocateTexture(
const TextureInfo& texture_info) { const TextureInfo& texture_info) {
// Create an image first. // Create an image first.
@ -595,7 +718,6 @@ TextureCache::Sampler* TextureCache::Demand(const SamplerInfo& sampler_info) {
VkResult status = VK_SUCCESS; VkResult status = VK_SUCCESS;
// Create a new sampler and cache it. // Create a new sampler and cache it.
// TODO: Actually set the properties
VkSamplerCreateInfo sampler_create_info; VkSamplerCreateInfo sampler_create_info;
sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_create_info.pNext = nullptr; sampler_create_info.pNext = nullptr;
@ -1270,6 +1392,19 @@ VkDescriptorSet TextureCache::PrepareTextureSet(
return nullptr; return nullptr;
} }
// Copy in empty descriptors first
VkCopyDescriptorSet empty_copy;
empty_copy.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET;
empty_copy.pNext = nullptr;
empty_copy.srcSet = empty_set_;
empty_copy.srcBinding = 0;
empty_copy.srcArrayElement = 0;
empty_copy.dstSet = descriptor_set;
empty_copy.dstBinding = 0;
empty_copy.dstArrayElement = 0;
empty_copy.descriptorCount = 32;
vkUpdateDescriptorSets(*device_, 0, nullptr, 1, &empty_copy);
for (uint32_t i = 0; i < update_set_info->image_write_count; i++) { for (uint32_t i = 0; i < update_set_info->image_write_count; i++) {
update_set_info->image_writes[i].dstSet = descriptor_set; update_set_info->image_writes[i].dstSet = descriptor_set;
} }
@ -1354,11 +1489,15 @@ bool TextureCache::SetupTextureBinding(VkCommandBuffer command_buffer,
update_set_info->image_write_count++; update_set_info->image_write_count++;
image_write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; image_write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
image_write->pNext = nullptr;
// image_write->dstSet is set later...
image_write->dstBinding = 0; image_write->dstBinding = 0;
image_write->dstArrayElement = binding.fetch_constant; image_write->dstArrayElement = binding.fetch_constant;
image_write->descriptorCount = 1; image_write->descriptorCount = 1;
image_write->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; image_write->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
image_write->pImageInfo = image_info; image_write->pImageInfo = image_info;
image_write->pBufferInfo = nullptr;
image_write->pTexelBufferView = nullptr;
image_info->imageView = view->view; image_info->imageView = view->view;
image_info->imageLayout = texture->image_layout; image_info->imageLayout = texture->image_layout;

View File

@ -134,6 +134,9 @@ class TextureCache {
VkSampler sampler; VkSampler sampler;
}; };
void SetupEmptySet();
void DestroyEmptySet();
// Allocates a new texture and memory to back it on the GPU. // Allocates a new texture and memory to back it on the GPU.
Texture* AllocateTexture(const TextureInfo& texture_info); Texture* AllocateTexture(const TextureInfo& texture_info);
bool FreeTexture(Texture* texture); bool FreeTexture(Texture* texture);
@ -188,6 +191,12 @@ class TextureCache {
std::unordered_map<uint64_t, VkDescriptorSet> texture_bindings_; std::unordered_map<uint64_t, VkDescriptorSet> texture_bindings_;
VkDescriptorSetLayout texture_descriptor_set_layout_ = nullptr; VkDescriptorSetLayout texture_descriptor_set_layout_ = nullptr;
VkImage empty_image_ = nullptr;
VkImageView empty_image_view_ = nullptr;
VkDeviceMemory empty_image_memory_ = nullptr;
VkSampler empty_sampler_ = nullptr;
VkDescriptorSet empty_set_ = nullptr;
ui::vulkan::CircularBuffer staging_buffer_; ui::vulkan::CircularBuffer staging_buffer_;
std::unordered_map<uint64_t, Texture*> textures_; std::unordered_map<uint64_t, Texture*> textures_;
std::unordered_map<uint64_t, Sampler*> samplers_; std::unordered_map<uint64_t, Sampler*> samplers_;

View File

@ -308,6 +308,9 @@ class DescriptorPool : public BaseFencedPool<DescriptorPool, VkDescriptorSet> {
return Base::AcquireEntry(layout); return Base::AcquireEntry(layout);
} }
// WARNING: Allocating sets from the vulkan pool will not be tracked!
VkDescriptorPool descriptor_pool() { return descriptor_pool_; }
protected: protected:
friend class BaseFencedPool<DescriptorPool, VkDescriptorSet>; friend class BaseFencedPool<DescriptorPool, VkDescriptorSet>;
VkDescriptorSet AllocateEntry(void* data); VkDescriptorSet AllocateEntry(void* data);