Vulkan: Fill unused descriptor array elements with a dummy image (required by API)
This commit is contained in:
parent
75b34b83a6
commit
4a99b88650
|
@ -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;
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue