Add drawing code for devices with low sampler images

This commit is contained in:
Benau 2022-03-20 13:35:22 +08:00
parent bb6551fdf6
commit ea1733ed08
7 changed files with 118 additions and 32 deletions

View File

@ -2,11 +2,20 @@ layout(location = 0) in vec4 f_color;
layout(location = 1) in vec2 f_uv; layout(location = 1) in vec2 f_uv;
layout(location = 2) flat in int f_sampler_index; layout(location = 2) flat in int f_sampler_index;
#ifdef BIND_TEXTURES_AT_ONCE
layout(binding = 0) uniform sampler2D f_tex[SAMPLER_SIZE]; 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; layout(location = 0) out vec4 o_color;
void main() 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;
} }

View File

@ -283,6 +283,8 @@ namespace GE
VkDeviceMemory& buffer_memory); VkDeviceMemory& buffer_memory);
VkPhysicalDevice getPhysicalDevice() const { return m_physical_device; } VkPhysicalDevice getPhysicalDevice() const { return m_physical_device; }
void waitIdle() { vkQueueWaitIdle(m_graphics_queue); } void waitIdle() { vkQueueWaitIdle(m_graphics_queue); }
const VkPhysicalDeviceFeatures& getPhysicalDeviceFeatures() const
{ return m_features; }
const VkPhysicalDeviceProperties& getPhysicalDeviceProperties() const const VkPhysicalDeviceProperties& getPhysicalDeviceProperties() const
{ return m_properties; } { return m_properties; }
VkCommandBuffer beginSingleTimeCommands(); VkCommandBuffer beginSingleTimeCommands();

View File

@ -83,7 +83,9 @@ void GEVulkan2dRenderer::createDescriptorSetLayout()
{ {
VkDescriptorSetLayoutBinding sampler_layout_binding = {}; VkDescriptorSetLayoutBinding sampler_layout_binding = {};
sampler_layout_binding.binding = 0; 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.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
sampler_layout_binding.pImmutableSamplers = NULL; sampler_layout_binding.pImmutableSamplers = NULL;
sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
@ -276,6 +278,9 @@ void GEVulkan2dRenderer::createTrisBuffers()
void GEVulkan2dRenderer::createDescriptorPool() void GEVulkan2dRenderer::createDescriptorPool()
{ {
uint32_t descriptor_count = g_vk->getMaxFrameInFlight(); uint32_t descriptor_count = g_vk->getMaxFrameInFlight();
if (!GEVulkanFeatures::supportsBindTexturesAtOnce())
descriptor_count *= GEVulkanShaderManager::getSamplerSize();
std::array<VkDescriptorPoolSize, 1> pool_sizes = {}; std::array<VkDescriptorPoolSize, 1> pool_sizes = {};
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_sizes[0].descriptorCount = GEVulkanShaderManager::getSamplerSize() * pool_sizes[0].descriptorCount = GEVulkanShaderManager::getSamplerSize() *
@ -295,7 +300,10 @@ void GEVulkan2dRenderer::createDescriptorPool()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void GEVulkan2dRenderer::createDescriptorSets() 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<VkDescriptorSetLayout> layouts(g_descriptor_sets.size(), std::vector<VkDescriptorSetLayout> layouts(g_descriptor_sets.size(),
g_descriptor_set_layout); g_descriptor_set_layout);
@ -371,6 +379,9 @@ void GEVulkan2dRenderer::render()
if (image_infos.size() >= GEVulkanShaderManager::getSamplerSize()) if (image_infos.size() >= GEVulkanShaderManager::getSamplerSize())
break; break;
} }
if (GEVulkanFeatures::supportsBindTexturesAtOnce())
{
image_infos.resize(GEVulkanShaderManager::getSamplerSize(), image_infos[0]); image_infos.resize(GEVulkanShaderManager::getSamplerSize(), image_infos[0]);
VkWriteDescriptorSet write_descriptor_set = {}; VkWriteDescriptorSet write_descriptor_set = {};
@ -385,6 +396,26 @@ void GEVulkan2dRenderer::render()
vkUpdateDescriptorSets(g_vk->getDevice(), 1, &write_descriptor_set, 0, vkUpdateDescriptorSets(g_vk->getDevice(), 1, &write_descriptor_set, 0,
NULL); 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}; VkDeviceSize offsets[] = {0};
VkBuffer vertex_buffer = VK_NULL_HANDLE; VkBuffer vertex_buffer = VK_NULL_HANDLE;
@ -418,9 +449,12 @@ void GEVulkan2dRenderer::render()
vkCmdBindIndexBuffer(g_vk->getCurrentCommandBuffer(), indices_buffer, 0, vkCmdBindIndexBuffer(g_vk->getCurrentCommandBuffer(), indices_buffer, 0,
VK_INDEX_TYPE_UINT16); VK_INDEX_TYPE_UINT16);
if (GEVulkanFeatures::supportsBindTexturesAtOnce())
{
vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(), vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1, VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1,
&g_descriptor_sets[g_vk->getCurrentFrame()], 0, NULL); &g_descriptor_sets[g_vk->getCurrentFrame()], 0, NULL);
}
if (GEVulkanFeatures::supportsDifferentTexturePerDraw()) if (GEVulkanFeatures::supportsDifferentTexturePerDraw())
{ {
@ -437,6 +471,13 @@ void GEVulkan2dRenderer::render()
Tri& cur_tri = g_tris_queue[g_tris_index_queue[idx]]; Tri& cur_tri = g_tris_queue[g_tris_index_queue[idx]];
if (cur_tri.sampler_idx != sampler_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, vkCmdDrawIndexed(g_vk->getCurrentCommandBuffer(), idx_count, 1,
idx - idx_count, 0, 0); idx - idx_count, 0, 0);
sampler_idx = cur_tri.sampler_idx; sampler_idx = cur_tri.sampler_idx;
@ -447,6 +488,13 @@ void GEVulkan2dRenderer::render()
idx_count += 3; 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, vkCmdDrawIndexed(g_vk->getCurrentCommandBuffer(), idx_count, 1,
idx - idx_count, 0, 0); idx - idx_count, 0, 0);
} }
@ -474,7 +522,7 @@ void GEVulkan2dRenderer::addVerticesIndices(irr::video::S3DVertex* vertices,
g_tex_map[t] = pre_size; g_tex_map[t] = pre_size;
} }
int sampler_idx = g_tex_map.at(t); 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()) if (sampler_idx >= (int)GEVulkanShaderManager::getSamplerSize())
sampler_idx = GEVulkanShaderManager::getSamplerSize() - 1; sampler_idx = GEVulkanShaderManager::getSamplerSize() - 1;
uint16_t last_index = (uint16_t)g_tris_queue.size(); uint16_t last_index = (uint16_t)g_tris_queue.size();

View File

@ -482,6 +482,7 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params,
m_device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); m_device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
findPhysicalDevice(); findPhysicalDevice();
vkGetPhysicalDeviceProperties(m_physical_device, &m_properties);
GEVulkanFeatures::init(this); GEVulkanFeatures::init(this);
createDevice(); createDevice();
@ -495,7 +496,6 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params,
} }
#endif #endif
vkGetPhysicalDeviceProperties(m_physical_device, &m_properties);
createSwapChain(); createSwapChain();
createSyncObjects(); createSyncObjects();
createCommandPool(); createCommandPool();
@ -756,9 +756,8 @@ void GEVulkanDriver::createDevice()
} }
VkPhysicalDeviceFeatures device_features = {}; VkPhysicalDeviceFeatures device_features = {};
if (m_features.shaderSampledImageArrayDynamicIndexing == VK_FALSE) device_features.shaderSampledImageArrayDynamicIndexing =
throw std::runtime_error("doesn't support shaderSampledImageArrayDynamicIndexing"); GEVulkanFeatures::supportsBindTexturesAtOnce();
device_features.shaderSampledImageArrayDynamicIndexing = VK_TRUE;
VkPhysicalDeviceVulkan12Features vulkan12_features = {}; VkPhysicalDeviceVulkan12Features vulkan12_features = {};
vulkan12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; vulkan12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;

View File

@ -1,6 +1,7 @@
#include "ge_vulkan_features.hpp" #include "ge_vulkan_features.hpp"
#include "ge_vulkan_driver.hpp" #include "ge_vulkan_driver.hpp"
#include "ge_vulkan_shader_manager.hpp"
#include <set> #include <set>
#include <string> #include <string>
@ -13,6 +14,7 @@ namespace GE
namespace GEVulkanFeatures namespace GEVulkanFeatures
{ {
// ============================================================================ // ============================================================================
bool g_supports_bind_textures_at_once = false;
// https://chunkstories.xyz/blog/a-note-on-descriptor-indexing // https://chunkstories.xyz/blog/a-note-on-descriptor-indexing
bool g_supports_descriptor_indexing = false; bool g_supports_descriptor_indexing = false;
bool g_supports_non_uniform_indexing = false; bool g_supports_non_uniform_indexing = false;
@ -22,6 +24,24 @@ bool g_supports_partially_bound = false;
// ============================================================================ // ============================================================================
void GEVulkanFeatures::init(GEVulkanDriver* vk) 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; uint32_t extension_count;
vkEnumerateDeviceExtensionProperties(vk->getPhysicalDevice(), NULL, vkEnumerateDeviceExtensionProperties(vk->getPhysicalDevice(), NULL,
&extension_count, NULL); &extension_count, NULL);
@ -58,6 +78,9 @@ void GEVulkanFeatures::init(GEVulkanDriver* vk)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void GEVulkanFeatures::printStats() 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( os::Printer::log(
"Vulkan supports VK_EXT_descriptor_indexing", "Vulkan supports VK_EXT_descriptor_indexing",
g_supports_descriptor_indexing ? "true" : "false"); g_supports_descriptor_indexing ? "true" : "false");
@ -69,6 +92,12 @@ void GEVulkanFeatures::printStats()
g_supports_partially_bound ? "true" : "false"); g_supports_partially_bound ? "true" : "false");
} // printStats } // printStats
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsBindTexturesAtOnce()
{
return g_supports_bind_textures_at_once;
} // supportsBindTexturesAtOnce
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsDescriptorIndexing() bool GEVulkanFeatures::supportsDescriptorIndexing()
{ {
@ -84,7 +113,8 @@ bool GEVulkanFeatures::supportsNonUniformIndexing()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsDifferentTexturePerDraw() 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 } // supportsDifferentTexturePerDraw
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -13,6 +13,8 @@ void init(GEVulkanDriver*);
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void printStats(); void printStats();
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool supportsBindTexturesAtOnce();
// ----------------------------------------------------------------------------
bool supportsDescriptorIndexing(); bool supportsDescriptorIndexing();
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool supportsNonUniformIndexing(); bool supportsNonUniformIndexing();

View File

@ -20,7 +20,7 @@ GEVulkanDriver* g_vk = NULL;
irr::io::IFileSystem* g_file_system = NULL; irr::io::IFileSystem* g_file_system = NULL;
std::string g_predefines = ""; 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_vert = VK_NULL_HANDLE;
VkShaderModule g_2d_render_frag = VK_NULL_HANDLE; VkShaderModule g_2d_render_frag = VK_NULL_HANDLE;
@ -32,16 +32,12 @@ void GEVulkanShaderManager::init(GEVulkanDriver* vk)
g_vk = vk; g_vk = vk;
g_file_system = vk->getFileSystem(); 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; std::ostringstream oss;
oss << "#version 450\n"; oss << "#version 450\n";
oss << "#define SAMPLER_SIZE " << g_sampler_size << "\n"; oss << "#define SAMPLER_SIZE " << g_sampler_size << "\n";
if (GEVulkanFeatures::supportsBindTexturesAtOnce())
oss << "#define BIND_TEXTURES_AT_ONCE\n";
if (GEVulkanFeatures::supportsDifferentTexturePerDraw()) if (GEVulkanFeatures::supportsDifferentTexturePerDraw())
{ {
oss << "#extension GL_EXT_nonuniform_qualifier : enable\n"; oss << "#extension GL_EXT_nonuniform_qualifier : enable\n";