diff --git a/data/shaders/ge_shaders/2d_render.frag b/data/shaders/ge_shaders/2d_render.frag index b07ad391d..871e88256 100644 --- a/data/shaders/ge_shaders/2d_render.frag +++ b/data/shaders/ge_shaders/2d_render.frag @@ -2,11 +2,20 @@ layout(location = 0) in vec4 f_color; layout(location = 1) in vec2 f_uv; layout(location = 2) flat in int f_sampler_index; +#ifdef BIND_TEXTURES_AT_ONCE layout(binding = 0) uniform sampler2D f_tex[SAMPLER_SIZE]; +#else +layout(binding = 0) uniform sampler2D f_tex; +#endif layout(location = 0) out vec4 o_color; void main() { - o_color = texture(f_tex[GE_SAMPLE_TEX_INDEX(f_sampler_index)], f_uv) * f_color; +#ifdef BIND_TEXTURES_AT_ONCE + vec4 tex_color = texture(f_tex[GE_SAMPLE_TEX_INDEX(f_sampler_index)], f_uv); +#else + vec4 tex_color = texture(f_tex, f_uv); +#endif + o_color = tex_color * f_color; } diff --git a/lib/graphics_engine/include/ge_vulkan_driver.hpp b/lib/graphics_engine/include/ge_vulkan_driver.hpp index b08c4557b..b8d3665dc 100644 --- a/lib/graphics_engine/include/ge_vulkan_driver.hpp +++ b/lib/graphics_engine/include/ge_vulkan_driver.hpp @@ -283,6 +283,8 @@ namespace GE VkDeviceMemory& buffer_memory); VkPhysicalDevice getPhysicalDevice() const { return m_physical_device; } void waitIdle() { vkQueueWaitIdle(m_graphics_queue); } + const VkPhysicalDeviceFeatures& getPhysicalDeviceFeatures() const + { return m_features; } const VkPhysicalDeviceProperties& getPhysicalDeviceProperties() const { return m_properties; } VkCommandBuffer beginSingleTimeCommands(); diff --git a/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp b/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp index d9cd838b9..213d54b0c 100644 --- a/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp +++ b/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp @@ -83,7 +83,9 @@ void GEVulkan2dRenderer::createDescriptorSetLayout() { VkDescriptorSetLayoutBinding sampler_layout_binding = {}; sampler_layout_binding.binding = 0; - sampler_layout_binding.descriptorCount = GEVulkanShaderManager::getSamplerSize(); + sampler_layout_binding.descriptorCount = + GEVulkanFeatures::supportsBindTexturesAtOnce() ? + GEVulkanShaderManager::getSamplerSize() : 1; sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; sampler_layout_binding.pImmutableSamplers = NULL; sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; @@ -276,6 +278,9 @@ void GEVulkan2dRenderer::createTrisBuffers() void GEVulkan2dRenderer::createDescriptorPool() { uint32_t descriptor_count = g_vk->getMaxFrameInFlight(); + if (!GEVulkanFeatures::supportsBindTexturesAtOnce()) + descriptor_count *= GEVulkanShaderManager::getSamplerSize(); + std::array pool_sizes = {}; pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; pool_sizes[0].descriptorCount = GEVulkanShaderManager::getSamplerSize() * @@ -295,7 +300,10 @@ void GEVulkan2dRenderer::createDescriptorPool() // ---------------------------------------------------------------------------- void GEVulkan2dRenderer::createDescriptorSets() { - g_descriptor_sets.resize(g_vk->getMaxFrameInFlight()); + unsigned set_size = g_vk->getMaxFrameInFlight(); + if (!GEVulkanFeatures::supportsBindTexturesAtOnce()) + set_size *= GEVulkanShaderManager::getSamplerSize(); + g_descriptor_sets.resize(set_size); std::vector layouts(g_descriptor_sets.size(), g_descriptor_set_layout); @@ -371,20 +379,43 @@ void GEVulkan2dRenderer::render() if (image_infos.size() >= GEVulkanShaderManager::getSamplerSize()) break; } - image_infos.resize(GEVulkanShaderManager::getSamplerSize(), image_infos[0]); - VkWriteDescriptorSet write_descriptor_set = {}; - write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_descriptor_set.dstBinding = 0; - write_descriptor_set.dstArrayElement = 0; - write_descriptor_set.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - write_descriptor_set.descriptorCount = GEVulkanShaderManager::getSamplerSize(); - write_descriptor_set.pBufferInfo = 0; - write_descriptor_set.dstSet = g_descriptor_sets[g_vk->getCurrentFrame()]; - write_descriptor_set.pImageInfo = image_infos.data(); + if (GEVulkanFeatures::supportsBindTexturesAtOnce()) + { + image_infos.resize(GEVulkanShaderManager::getSamplerSize(), image_infos[0]); - vkUpdateDescriptorSets(g_vk->getDevice(), 1, &write_descriptor_set, 0, - NULL); + VkWriteDescriptorSet write_descriptor_set = {}; + write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptor_set.dstBinding = 0; + write_descriptor_set.dstArrayElement = 0; + write_descriptor_set.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_descriptor_set.descriptorCount = GEVulkanShaderManager::getSamplerSize(); + write_descriptor_set.pBufferInfo = 0; + write_descriptor_set.dstSet = g_descriptor_sets[g_vk->getCurrentFrame()]; + write_descriptor_set.pImageInfo = image_infos.data(); + + vkUpdateDescriptorSets(g_vk->getDevice(), 1, &write_descriptor_set, 0, + NULL); + } + else + { + for (unsigned i = 0; i < image_infos.size(); i++) + { + VkWriteDescriptorSet write_descriptor_set = {}; + write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptor_set.dstBinding = 0; + write_descriptor_set.dstArrayElement = 0; + write_descriptor_set.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_descriptor_set.descriptorCount = 1; + write_descriptor_set.pBufferInfo = 0; + const unsigned set_idx = g_vk->getCurrentFrame() * GEVulkanShaderManager::getSamplerSize() + i; + write_descriptor_set.dstSet = g_descriptor_sets[set_idx]; + write_descriptor_set.pImageInfo = &image_infos[i]; + + vkUpdateDescriptorSets(g_vk->getDevice(), 1, &write_descriptor_set, + 0, NULL); + } + } VkDeviceSize offsets[] = {0}; VkBuffer vertex_buffer = VK_NULL_HANDLE; @@ -418,9 +449,12 @@ void GEVulkan2dRenderer::render() vkCmdBindIndexBuffer(g_vk->getCurrentCommandBuffer(), indices_buffer, 0, VK_INDEX_TYPE_UINT16); - vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(), - VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1, - &g_descriptor_sets[g_vk->getCurrentFrame()], 0, NULL); + if (GEVulkanFeatures::supportsBindTexturesAtOnce()) + { + vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(), + VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1, + &g_descriptor_sets[g_vk->getCurrentFrame()], 0, NULL); + } if (GEVulkanFeatures::supportsDifferentTexturePerDraw()) { @@ -437,6 +471,13 @@ void GEVulkan2dRenderer::render() Tri& cur_tri = g_tris_queue[g_tris_index_queue[idx]]; if (cur_tri.sampler_idx != sampler_idx) { + if (!GEVulkanFeatures::supportsBindTexturesAtOnce()) + { + const unsigned set_idx = g_vk->getCurrentFrame() * GEVulkanShaderManager::getSamplerSize() + sampler_idx; + vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(), + VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1, + &g_descriptor_sets[set_idx], 0, NULL); + } vkCmdDrawIndexed(g_vk->getCurrentCommandBuffer(), idx_count, 1, idx - idx_count, 0, 0); sampler_idx = cur_tri.sampler_idx; @@ -447,6 +488,13 @@ void GEVulkan2dRenderer::render() idx_count += 3; } } + if (!GEVulkanFeatures::supportsBindTexturesAtOnce()) + { + const unsigned set_idx = g_vk->getCurrentFrame() * GEVulkanShaderManager::getSamplerSize() + sampler_idx; + vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(), + VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1, + &g_descriptor_sets[set_idx], 0, NULL); + } vkCmdDrawIndexed(g_vk->getCurrentCommandBuffer(), idx_count, 1, idx - idx_count, 0, 0); } @@ -474,7 +522,7 @@ void GEVulkan2dRenderer::addVerticesIndices(irr::video::S3DVertex* vertices, g_tex_map[t] = pre_size; } int sampler_idx = g_tex_map.at(t); - // In STK we rarely use more than 96 textures per (2d) draw call + // In STK we rarely use more than 256 textures per (2d) draw call if (sampler_idx >= (int)GEVulkanShaderManager::getSamplerSize()) sampler_idx = GEVulkanShaderManager::getSamplerSize() - 1; uint16_t last_index = (uint16_t)g_tris_queue.size(); diff --git a/lib/graphics_engine/src/ge_vulkan_driver.cpp b/lib/graphics_engine/src/ge_vulkan_driver.cpp index dcd788d5b..b29155af9 100644 --- a/lib/graphics_engine/src/ge_vulkan_driver.cpp +++ b/lib/graphics_engine/src/ge_vulkan_driver.cpp @@ -482,6 +482,7 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params, m_device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); findPhysicalDevice(); + vkGetPhysicalDeviceProperties(m_physical_device, &m_properties); GEVulkanFeatures::init(this); createDevice(); @@ -495,7 +496,6 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params, } #endif - vkGetPhysicalDeviceProperties(m_physical_device, &m_properties); createSwapChain(); createSyncObjects(); createCommandPool(); @@ -756,9 +756,8 @@ void GEVulkanDriver::createDevice() } VkPhysicalDeviceFeatures device_features = {}; - if (m_features.shaderSampledImageArrayDynamicIndexing == VK_FALSE) - throw std::runtime_error("doesn't support shaderSampledImageArrayDynamicIndexing"); - device_features.shaderSampledImageArrayDynamicIndexing = VK_TRUE; + device_features.shaderSampledImageArrayDynamicIndexing = + GEVulkanFeatures::supportsBindTexturesAtOnce(); VkPhysicalDeviceVulkan12Features vulkan12_features = {}; vulkan12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; diff --git a/lib/graphics_engine/src/ge_vulkan_features.cpp b/lib/graphics_engine/src/ge_vulkan_features.cpp index a6e7977fa..311b1037e 100644 --- a/lib/graphics_engine/src/ge_vulkan_features.cpp +++ b/lib/graphics_engine/src/ge_vulkan_features.cpp @@ -1,6 +1,7 @@ #include "ge_vulkan_features.hpp" #include "ge_vulkan_driver.hpp" +#include "ge_vulkan_shader_manager.hpp" #include #include @@ -13,6 +14,7 @@ namespace GE namespace GEVulkanFeatures { // ============================================================================ +bool g_supports_bind_textures_at_once = false; // https://chunkstories.xyz/blog/a-note-on-descriptor-indexing bool g_supports_descriptor_indexing = false; bool g_supports_non_uniform_indexing = false; @@ -22,6 +24,24 @@ bool g_supports_partially_bound = false; // ============================================================================ void GEVulkanFeatures::init(GEVulkanDriver* vk) { + g_supports_bind_textures_at_once = true; + VkPhysicalDeviceLimits limit = vk->getPhysicalDeviceProperties().limits; + // https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxDescriptorSetSamplers&platform=all + // https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxDescriptorSetSampledImages&platform=all + // https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPerStageDescriptorSamplers&platform=all + // https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPerStageDescriptorSampledImages&platform=all + // We decide 256 (GEVulkanShaderManager::getSamplerSize()) based on those infos + if (limit.maxDescriptorSetSamplers < GEVulkanShaderManager::getSamplerSize()) + g_supports_bind_textures_at_once = false; + if (limit.maxDescriptorSetSampledImages < GEVulkanShaderManager::getSamplerSize()) + g_supports_bind_textures_at_once = false; + if (limit.maxPerStageDescriptorSamplers < GEVulkanShaderManager::getSamplerSize()) + g_supports_bind_textures_at_once = false; + if (limit.maxPerStageDescriptorSampledImages < GEVulkanShaderManager::getSamplerSize()) + g_supports_bind_textures_at_once = false; + if (vk->getPhysicalDeviceFeatures().shaderSampledImageArrayDynamicIndexing == VK_FALSE) + g_supports_bind_textures_at_once = false; + uint32_t extension_count; vkEnumerateDeviceExtensionProperties(vk->getPhysicalDevice(), NULL, &extension_count, NULL); @@ -58,6 +78,9 @@ void GEVulkanFeatures::init(GEVulkanDriver* vk) // ---------------------------------------------------------------------------- void GEVulkanFeatures::printStats() { + os::Printer::log( + "Vulkan can bind textures at once in shader", + g_supports_bind_textures_at_once ? "true" : "false"); os::Printer::log( "Vulkan supports VK_EXT_descriptor_indexing", g_supports_descriptor_indexing ? "true" : "false"); @@ -69,6 +92,12 @@ void GEVulkanFeatures::printStats() g_supports_partially_bound ? "true" : "false"); } // printStats +// ---------------------------------------------------------------------------- +bool GEVulkanFeatures::supportsBindTexturesAtOnce() +{ + return g_supports_bind_textures_at_once; +} // supportsBindTexturesAtOnce + // ---------------------------------------------------------------------------- bool GEVulkanFeatures::supportsDescriptorIndexing() { @@ -84,7 +113,8 @@ bool GEVulkanFeatures::supportsNonUniformIndexing() // ---------------------------------------------------------------------------- bool GEVulkanFeatures::supportsDifferentTexturePerDraw() { - return g_supports_descriptor_indexing && g_supports_non_uniform_indexing; + return g_supports_bind_textures_at_once && + g_supports_descriptor_indexing && g_supports_non_uniform_indexing; } // supportsDifferentTexturePerDraw // ---------------------------------------------------------------------------- diff --git a/lib/graphics_engine/src/ge_vulkan_features.hpp b/lib/graphics_engine/src/ge_vulkan_features.hpp index dde734a8d..05df74ee0 100644 --- a/lib/graphics_engine/src/ge_vulkan_features.hpp +++ b/lib/graphics_engine/src/ge_vulkan_features.hpp @@ -13,6 +13,8 @@ void init(GEVulkanDriver*); // ---------------------------------------------------------------------------- void printStats(); // ---------------------------------------------------------------------------- +bool supportsBindTexturesAtOnce(); +// ---------------------------------------------------------------------------- bool supportsDescriptorIndexing(); // ---------------------------------------------------------------------------- bool supportsNonUniformIndexing(); diff --git a/lib/graphics_engine/src/ge_vulkan_shader_manager.cpp b/lib/graphics_engine/src/ge_vulkan_shader_manager.cpp index 10dff5b6a..9ce6f63a6 100644 --- a/lib/graphics_engine/src/ge_vulkan_shader_manager.cpp +++ b/lib/graphics_engine/src/ge_vulkan_shader_manager.cpp @@ -20,7 +20,7 @@ GEVulkanDriver* g_vk = NULL; irr::io::IFileSystem* g_file_system = NULL; std::string g_predefines = ""; -uint32_t g_sampler_size = 0; +uint32_t g_sampler_size = 256; VkShaderModule g_2d_render_vert = VK_NULL_HANDLE; VkShaderModule g_2d_render_frag = VK_NULL_HANDLE; @@ -32,16 +32,12 @@ void GEVulkanShaderManager::init(GEVulkanDriver* vk) g_vk = vk; g_file_system = vk->getFileSystem(); - VkPhysicalDeviceLimits limit = g_vk->getPhysicalDeviceProperties().limits; - // According to https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxDescriptorSetSampledImages&platform=all - // almost all users have at least 96 - if (limit.maxDescriptorSetSampledImages < 96) - throw std::runtime_error("maxDescriptorSetSampledImages is too low"); - g_sampler_size = 96; - std::ostringstream oss; oss << "#version 450\n"; oss << "#define SAMPLER_SIZE " << g_sampler_size << "\n"; + if (GEVulkanFeatures::supportsBindTexturesAtOnce()) + oss << "#define BIND_TEXTURES_AT_ONCE\n"; + if (GEVulkanFeatures::supportsDifferentTexturePerDraw()) { oss << "#extension GL_EXT_nonuniform_qualifier : enable\n";