diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index 2c85f5bfe..2ba8fa44f 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -127,15 +127,15 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file, // 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. auto& limits = device_->device_info().properties.limits; - if (limits.maxPerStageDescriptorSamplers < kMaxTextureSamplers * 4 || - limits.maxPerStageDescriptorSampledImages < kMaxTextureSamplers * 4) { + if (limits.maxPerStageDescriptorSamplers < kMaxTextureSamplers || + limits.maxPerStageDescriptorSampledImages < kMaxTextureSamplers) { XELOGE( "Physical device is unable to support required number of sampled " "images! Expect instability! (maxPerStageDescriptorSamplers=%d, " "maxPerStageDescriptorSampledImages=%d)", limits.maxPerStageDescriptorSamplers, limits.maxPerStageDescriptorSampledImages); - assert_always(); + // assert_always(); } // 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]; device_queue_ = device_->AcquireQueue(); + SetupEmptySet(); } TextureCache::~TextureCache() { @@ -177,6 +178,7 @@ TextureCache::~TextureCache() { device_->ReleaseQueue(device_queue_); } + DestroyEmptySet(); for (auto it = samplers_.begin(); it != samplers_.end(); ++it) { vkDestroySampler(*device_, it->second->sampler, nullptr); delete it->second; @@ -187,6 +189,127 @@ TextureCache::~TextureCache() { 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( const TextureInfo& texture_info) { // Create an image first. @@ -595,7 +718,6 @@ TextureCache::Sampler* TextureCache::Demand(const SamplerInfo& sampler_info) { VkResult status = VK_SUCCESS; // Create a new sampler and cache it. - // TODO: Actually set the properties VkSamplerCreateInfo sampler_create_info; sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler_create_info.pNext = nullptr; @@ -1270,6 +1392,19 @@ VkDescriptorSet TextureCache::PrepareTextureSet( 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++) { 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++; 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->dstArrayElement = binding.fetch_constant; image_write->descriptorCount = 1; image_write->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; image_write->pImageInfo = image_info; + image_write->pBufferInfo = nullptr; + image_write->pTexelBufferView = nullptr; image_info->imageView = view->view; image_info->imageLayout = texture->image_layout; diff --git a/src/xenia/gpu/vulkan/texture_cache.h b/src/xenia/gpu/vulkan/texture_cache.h index 35646656b..3f8653136 100644 --- a/src/xenia/gpu/vulkan/texture_cache.h +++ b/src/xenia/gpu/vulkan/texture_cache.h @@ -134,6 +134,9 @@ class TextureCache { VkSampler sampler; }; + void SetupEmptySet(); + void DestroyEmptySet(); + // Allocates a new texture and memory to back it on the GPU. Texture* AllocateTexture(const TextureInfo& texture_info); bool FreeTexture(Texture* texture); @@ -188,6 +191,12 @@ class TextureCache { std::unordered_map texture_bindings_; 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_; std::unordered_map textures_; std::unordered_map samplers_; diff --git a/src/xenia/ui/vulkan/fenced_pools.h b/src/xenia/ui/vulkan/fenced_pools.h index 02e63d6bf..7413086a2 100644 --- a/src/xenia/ui/vulkan/fenced_pools.h +++ b/src/xenia/ui/vulkan/fenced_pools.h @@ -308,6 +308,9 @@ class DescriptorPool : public BaseFencedPool { return Base::AcquireEntry(layout); } + // WARNING: Allocating sets from the vulkan pool will not be tracked! + VkDescriptorPool descriptor_pool() { return descriptor_pool_; } + protected: friend class BaseFencedPool; VkDescriptorSet AllocateEntry(void* data);