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 = 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;
}

View File

@ -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();

View File

@ -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<VkDescriptorPoolSize, 1> 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<VkDescriptorSetLayout> 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();

View File

@ -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;

View File

@ -1,6 +1,7 @@
#include "ge_vulkan_features.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_shader_manager.hpp"
#include <set>
#include <string>
@ -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
// ----------------------------------------------------------------------------

View File

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

View File

@ -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";