diff --git a/data/shaders/ge_shaders/grass.vert b/data/shaders/ge_shaders/grass.vert index 722b39bae..f507fe92e 100644 --- a/data/shaders/ge_shaders/grass.vert +++ b/data/shaders/ge_shaders/grass.vert @@ -21,5 +21,9 @@ void main() f_uv = v_uv; f_uv_two = v_uv_two; f_material_id = u_object_buffer.m_objects[gl_InstanceIndex].m_material_id; +#ifdef BIND_MESH_TEXTURES_AT_ONCE + if (f_material_id < 0) + f_material_id = u_material_ids.m_material_id[gl_DrawIDARB]; +#endif f_hue_change = u_object_buffer.m_objects[gl_InstanceIndex].m_hue_change; } diff --git a/data/shaders/ge_shaders/spm.vert b/data/shaders/ge_shaders/spm.vert index fafb30d54..12e54d713 100644 --- a/data/shaders/ge_shaders/spm.vert +++ b/data/shaders/ge_shaders/spm.vert @@ -14,5 +14,9 @@ void main() f_uv = v_uv + u_object_buffer.m_objects[gl_InstanceIndex].m_texture_trans; f_uv_two = v_uv_two; f_material_id = u_object_buffer.m_objects[gl_InstanceIndex].m_material_id; +#ifdef BIND_MESH_TEXTURES_AT_ONCE + if (f_material_id < 0) + f_material_id = u_material_ids.m_material_id[gl_DrawIDARB]; +#endif f_hue_change = u_object_buffer.m_objects[gl_InstanceIndex].m_hue_change; } diff --git a/data/shaders/ge_shaders/spm_skinning.vert b/data/shaders/ge_shaders/spm_skinning.vert index 273f62de2..0763aed92 100644 --- a/data/shaders/ge_shaders/spm_skinning.vert +++ b/data/shaders/ge_shaders/spm_skinning.vert @@ -22,5 +22,9 @@ void main() f_uv = v_uv + u_object_buffer.m_objects[gl_InstanceIndex].m_texture_trans; f_uv_two = v_uv_two; f_material_id = u_object_buffer.m_objects[gl_InstanceIndex].m_material_id; +#ifdef BIND_MESH_TEXTURES_AT_ONCE + if (f_material_id < 0) + f_material_id = u_material_ids.m_material_id[gl_DrawIDARB]; +#endif f_hue_change = u_object_buffer.m_objects[gl_InstanceIndex].m_hue_change; } diff --git a/data/shaders/ge_shaders/utils/spm_layout.h b/data/shaders/ge_shaders/utils/spm_layout.h index a910b3cfd..459b20111 100644 --- a/data/shaders/ge_shaders/utils/spm_layout.h +++ b/data/shaders/ge_shaders/utils/spm_layout.h @@ -1,3 +1,7 @@ +#ifdef BIND_MESH_TEXTURES_AT_ONCE +#extension GL_ARB_shader_draw_parameters : enable +#endif + layout(std140, set = 1, binding = 0) uniform CameraBuffer { mat4 m_view_matrix; @@ -30,6 +34,13 @@ layout(std140, set = 1, binding = 2) readonly buffer SkinningMatrices mat4 m_mat[]; } u_skinning_matrices; +#ifdef BIND_MESH_TEXTURES_AT_ONCE +layout(std430, set = 1, binding = 3) readonly buffer MaterialIDs +{ + int m_material_id[]; +} u_material_ids; +#endif + layout(location = 0) in vec3 v_position; layout(location = 1) in vec4 v_normal; layout(location = 2) in vec4 v_color; diff --git a/lib/graphics_engine/src/ge_vulkan_draw_call.cpp b/lib/graphics_engine/src/ge_vulkan_draw_call.cpp index 261eaa911..81273b44a 100644 --- a/lib/graphics_engine/src/ge_vulkan_draw_call.cpp +++ b/lib/graphics_engine/src/ge_vulkan_draw_call.cpp @@ -122,6 +122,7 @@ GEVulkanDrawCall::GEVulkanDrawCall() m_sbo_data = NULL; m_object_data_padded_size = 0; m_skinning_data_padded_size = 0; + m_materials_padded_size = 0; m_data_padding = NULL; const size_t ubo_padding = m_limits.minUniformBufferOffsetAlignment; const size_t sbo_padding = m_limits.minStorageBufferOffsetAlignment; @@ -190,6 +191,7 @@ void GEVulkanDrawCall::addNode(irr::scene::ISceneNode* node) continue; const std::string& shader = getShader(node, i); m_visible_nodes[buffer][shader].emplace_back(node, i); + m_mb_map[buffer] = mesh; if (anode && !added_skinning && !anode->getSkinningMatrices().empty() && m_skinning_nodes.find(anode) == m_skinning_nodes.end()) @@ -209,7 +211,8 @@ void GEVulkanDrawCall::addBillboardNode(irr::scene::ISceneNode* node, return; irr::video::SMaterial m = node->getMaterial(0); TexturesList textures = {}; - if (!GEVulkanFeatures::supportsDifferentTexturePerDraw()) + if (!GEVulkanFeatures::supportsDifferentTexturePerDraw() || + !GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) textures = getTexturesList(m); if (m_billboard_buffers.find(textures) == m_billboard_buffers.end()) m_billboard_buffers[textures] = new GEVulkanBillboardBuffer(m); @@ -226,16 +229,17 @@ void GEVulkanDrawCall::generate() if (!m_visible_nodes.empty() && m_data_layout == VK_NULL_HANDLE) createVulkanData(); + std::vector<std::pair<void*, size_t> > data_uploading; std::unordered_map<irr::scene::ISceneNode*, int> skinning_offets; int added_joint = 1; m_skinning_data_padded_size = sizeof(irr::core::matrix4); - m_data_uploading.emplace_back((void*)m_data_padding, + data_uploading.emplace_back((void*)m_data_padding, sizeof(irr::core::matrix4)); for (GEVulkanAnimatedMeshSceneNode* node : m_skinning_nodes) { int bone_count = node->getSPM()->getJointCount(); size_t bone_size = sizeof(irr::core::matrix4) * bone_count; - m_data_uploading.emplace_back( + data_uploading.emplace_back( (void*)node->getSkinningMatrices().data(), bone_size); skinning_offets[node] = added_joint; added_joint += bone_count; @@ -246,7 +250,7 @@ void GEVulkanDrawCall::generate() if (padding > 0) { m_skinning_data_padded_size += padding; - m_data_uploading.emplace_back((void*)m_data_padding, + data_uploading.emplace_back((void*)m_data_padding, padding); } @@ -285,14 +289,25 @@ void GEVulkanDrawCall::generate() }); const bool use_base_vertex = GEVulkanFeatures::supportsBaseVertexRendering(); + const bool bind_mesh_textures = + GEVulkanFeatures::supportsBindMeshTexturesAtOnce(); unsigned accumulated_instance = 0; + + struct InstanceKey + { + std::vector<irr::scene::ISceneNode*> m_nodes; + unsigned m_instance_count; + unsigned m_first_instance; + bool m_hue_change; + }; + std::unordered_map<irr::scene::IMesh*, std::vector<InstanceKey> > + instance_keys; for (auto& p : visible_nodes) { TexturesList textures = getTexturesList(p.first->getMaterial()); const irr::video::ITexture** list = &textures[0]; int material_id = m_texture_descriptor->getTextureID(list); - if (!GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) - m_materials[p.first] = material_id; + m_materials[p.first] = material_id; const bool skinning = p.first->hasSkinning(); for (auto& q : p.second) @@ -306,6 +321,48 @@ void GEVulkanDrawCall::generate() if (m_graphics_pipelines.find(cur_shader) == m_graphics_pipelines.end()) continue; + InstanceKey key; + key.m_nodes.reserve(q.second.size()); + key.m_instance_count = visible_count; + key.m_first_instance = accumulated_instance; + key.m_hue_change = false; + bool skip_instance_key = false; + for (auto& r : q.second) + { + if (r.second == BILLBOARD_NODE || r.second == PARTICLE_NODE) + { + skip_instance_key = true; + break; + } + irr::scene::ISceneNode* node = r.first; + const irr::core::matrix4& texture_matrix = + node->getMaterial(r.second).getTextureMatrix(0); + if (texture_matrix[8] != 0.0f || texture_matrix[9] != 0.0f) + { + skip_instance_key = true; + break; + } + auto& ri = node->getMaterial(r.second).getRenderInfo(); + if (ri && ri->getHue() > 0.0f) + { + key.m_hue_change = true; + break; + } + key.m_nodes.push_back(node); + } + irr::scene::IMesh* m = m_mb_map[p.first]; + auto& cur_key = instance_keys[m]; + auto it = cur_key.end(); + if (!skip_instance_key) + { + it = std::find_if(cur_key.begin(), cur_key.end(), + [key](const InstanceKey& k) + { + return k.m_nodes == key.m_nodes && + k.m_instance_count == key.m_instance_count && + k.m_hue_change == key.m_hue_change; + }); + } for (auto& r : q.second) { irr::scene::ISceneNode* node = r.first; @@ -345,14 +402,15 @@ void GEVulkanDrawCall::generate() } } } - else + else if (skip_instance_key || it == cur_key.end()) { int skinning_offset = -1000; auto it = skinning_offets.find(node); if (it != skinning_offets.end()) skinning_offset = it->second; - m_visible_objects.emplace_back(node, material_id, - skinning_offset, r.second); + m_visible_objects.emplace_back(node, + bind_mesh_textures ? -1 : material_id, skinning_offset, + r.second); } } VkDrawIndexedIndirectCommand cmd; @@ -360,17 +418,24 @@ void GEVulkanDrawCall::generate() cmd.instanceCount = visible_count; cmd.firstIndex = use_base_vertex ? p.first->getIBOOffset() : 0; cmd.vertexOffset = use_base_vertex ? p.first->getVBOOffset() : 0; - cmd.firstInstance = accumulated_instance; - accumulated_instance += visible_count; + if (skip_instance_key || it == cur_key.end()) + { + cmd.firstInstance = accumulated_instance; + accumulated_instance += visible_count; + } + else + cmd.firstInstance = it->m_first_instance; const PipelineSettings& settings = m_graphics_pipelines[cur_shader].second; std::string sorting_key = std::string(1, settings.m_drawing_priority) + cur_shader; m_cmds.push_back({ cmd, cur_shader, sorting_key, p.first, settings.isTransparent() }); + if (!skip_instance_key && it == cur_key.end()) + cur_key.push_back(key); } } - if (!GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) + if (!bind_mesh_textures) { std::stable_sort(m_cmds.begin(), m_cmds.end(), [this](const DrawCallData& a, const DrawCallData& b) @@ -395,32 +460,91 @@ void GEVulkanDrawCall::generate() { const size_t object_data_size = sizeof(ObjectData) * m_visible_objects.size(); - m_data_uploading.emplace_back((void*)m_visible_objects.data(), + data_uploading.emplace_back((void*)m_visible_objects.data(), object_data_size); m_object_data_padded_size = object_data_size; } else { m_object_data_padded_size = 0; + std::unordered_map<uint32_t, size_t> offset_map; for (unsigned i = 0; i < m_cmds.size(); i++) { auto& cmd = m_cmds[i]; - size_t instance_size = - cmd.m_cmd.instanceCount * sizeof(ObjectData); - m_data_uploading.emplace_back( - &m_visible_objects[cmd.m_cmd.firstInstance], instance_size); - size_t cur_padding = getPadding(m_object_data_padded_size + - instance_size, sbo_alignment); - if (cur_padding > 0) + uint32_t first_instance = cmd.m_cmd.firstInstance; + if (offset_map.find(first_instance) == offset_map.end()) { - instance_size += cur_padding; - m_data_uploading.emplace_back((void*)m_data_padding, - cur_padding); + size_t instance_size = + cmd.m_cmd.instanceCount * sizeof(ObjectData); + data_uploading.emplace_back( + &m_visible_objects[first_instance], instance_size); + size_t cur_padding = getPadding(m_object_data_padded_size + + instance_size, sbo_alignment); + if (cur_padding > 0) + { + instance_size += cur_padding; + data_uploading.emplace_back((void*)m_data_padding, + cur_padding); + } + m_sbo_data_offset.push_back(m_object_data_padded_size); + offset_map[cmd.m_cmd.firstInstance] = m_object_data_padded_size; + m_object_data_padded_size += instance_size; + } + else + { + m_sbo_data_offset.push_back(offset_map.at(first_instance)); } - m_sbo_data_offset.push_back(m_object_data_padded_size); - m_object_data_padded_size += instance_size; } } + + m_materials_padded_size = 0; + if (bind_mesh_textures && !m_cmds.empty()) + { + std::string cur_shader = m_cmds[0].m_shader; + for (unsigned i = 0; i < m_cmds.size(); i++) + { + auto& cmd = m_cmds[i]; + auto& material = m_materials_data[cur_shader]; + if (cmd.m_shader != cur_shader) + { + size_t material_size = material.second.size() * sizeof(int); + m_data_uploading.emplace_back( + material.second.data(), material_size); + size_t cur_padding = getPadding(m_materials_padded_size + + material_size, sbo_alignment); + if (cur_padding > 0) + { + material_size += cur_padding; + m_data_uploading.emplace_back((void*)m_data_padding, + cur_padding); + } + material.first = m_materials_padded_size; + m_materials_padded_size += material_size; + cur_shader = cmd.m_shader; + } + m_materials_data[cmd.m_shader].second + .push_back(m_materials[cmd.m_mb]); + } + + auto& material = m_materials_data[m_cmds.back().m_shader]; + size_t material_size = material.second.size() * sizeof(int); + m_data_uploading.emplace_back( + material.second.data(), material_size); + size_t cur_padding = getPadding(m_materials_padded_size + + material_size, sbo_alignment); + if (cur_padding > 0) + { + material_size += cur_padding; + m_data_uploading.emplace_back((void*)m_data_padding, + cur_padding); + } + material.first = m_materials_padded_size; + m_materials_padded_size += material_size; + m_data_uploading.insert(m_data_uploading.end(), data_uploading.begin(), + data_uploading.end()); + } + else + std::swap(m_data_uploading, data_uploading); } // generate // ---------------------------------------------------------------------------- @@ -748,42 +872,55 @@ start: void GEVulkanDrawCall::createVulkanData() { GEVulkanDriver* vk = getVKDriver(); - const bool use_base_vertex = GEVulkanFeatures::supportsBaseVertexRendering(); + const bool use_dynamic = + !GEVulkanFeatures::supportsBaseVertexRendering() || + GEVulkanFeatures::supportsBindMeshTexturesAtOnce(); // m_data_layout VkDescriptorSetLayoutBinding camera_layout_binding = {}; camera_layout_binding.binding = 0; camera_layout_binding.descriptorCount = 1; - camera_layout_binding.descriptorType = use_base_vertex ? - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + camera_layout_binding.descriptorType = use_dynamic ? + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; camera_layout_binding.pImmutableSamplers = NULL; camera_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; VkDescriptorSetLayoutBinding object_data_layout_binding = {}; object_data_layout_binding.binding = 1; object_data_layout_binding.descriptorCount = 1; - object_data_layout_binding.descriptorType = use_base_vertex ? - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + object_data_layout_binding.descriptorType = use_dynamic ? + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC : + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; object_data_layout_binding.pImmutableSamplers = NULL; object_data_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; VkDescriptorSetLayoutBinding skinning_layout_binding = {}; skinning_layout_binding.binding = 2; skinning_layout_binding.descriptorCount = 1; - skinning_layout_binding.descriptorType = use_base_vertex ? - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + skinning_layout_binding.descriptorType = use_dynamic ? + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC : + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; skinning_layout_binding.pImmutableSamplers = NULL; skinning_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - std::array<VkDescriptorSetLayoutBinding, 3> bindings = - {{ + std::vector<VkDescriptorSetLayoutBinding> bindings = + { camera_layout_binding, object_data_layout_binding, skinning_layout_binding - }}; + }; + if (GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) + { + VkDescriptorSetLayoutBinding material_binding = {}; + material_binding.binding = 3; + material_binding.descriptorCount = 1; + material_binding.descriptorType = + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + material_binding.pImmutableSamplers = NULL; + material_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + bindings.push_back(material_binding); + } VkDescriptorSetLayoutCreateInfo setinfo = {}; setinfo.flags = 0; @@ -801,21 +938,23 @@ void GEVulkanDrawCall::createVulkanData() } // m_descriptor_pool - std::array<VkDescriptorPoolSize, 2> sizes = - {{ + std::vector<VkDescriptorPoolSize> sizes = + { { - use_base_vertex ? - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + use_dynamic ? + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk->getMaxFrameInFlight() }, { - use_base_vertex ? - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, + use_dynamic ? + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC : + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk->getMaxFrameInFlight() * 2 } - }}; + }; + if (GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) + sizes.push_back(sizes.back()); VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; @@ -877,7 +1016,7 @@ void GEVulkanDrawCall::createVulkanData() createAllPipelines(vk); size_t extra_size = 0; - const bool use_multidraw = GEVulkanFeatures::supportsMultiDrawIndirect() && + const bool use_multidraw = GEVulkanFeatures::supportsBindMeshTexturesAtOnce(); VkBufferUsageFlags flags = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; if (use_multidraw) @@ -913,7 +1052,7 @@ void GEVulkanDrawCall::uploadDynamicData(GEVulkanDriver* vk, data_uploading.emplace_back((void*)cam->getUBOData(), sizeof(GEVulkanCameraUBO)); - const bool use_multidraw = GEVulkanFeatures::supportsMultiDrawIndirect() && + const bool use_multidraw = GEVulkanFeatures::supportsBindMeshTexturesAtOnce(); if (use_multidraw) { @@ -927,34 +1066,30 @@ void GEVulkanDrawCall::uploadDynamicData(GEVulkanDriver* vk, m_dynamic_data->setCurrentData(data_uploading, cmd); const bool use_base_vertex = GEVulkanFeatures::supportsBaseVertexRendering(); + size_t min_size = 0; if (!use_base_vertex) { // Make sure dynamic offset won't become invaild - size_t min_size = m_skinning_data_padded_size + + min_size = m_skinning_data_padded_size + (m_object_data_padded_size * 2); - m_sbo_data->resizeIfNeeded(min_size); } + else if (use_multidraw) + { + min_size = (m_materials_padded_size * 2) + + m_skinning_data_padded_size + m_object_data_padded_size; + } + m_sbo_data->resizeIfNeeded(min_size); m_sbo_data->setCurrentData(m_data_uploading, cmd); - VkBufferMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + VkMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; if (use_multidraw) barrier.dstAccessMask |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.buffer = m_dynamic_data->getCurrentBuffer(); - barrier.size = m_dynamic_data->getRealSize(); - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage, 0, 0, - NULL, 1, &barrier, 0, NULL); - - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier.buffer = m_sbo_data->getCurrentBuffer(); - barrier.size = m_skinning_data_padded_size + m_object_data_padded_size; - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage, 0, 0, - NULL, 1, &barrier, 0, NULL); + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage, 0, 1, + &barrier, 0, NULL, 0, NULL); } // uploadDynamicData // ---------------------------------------------------------------------------- @@ -969,70 +1104,78 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, const unsigned cur_frame = vk->getCurrentFrame(); const bool use_base_vertex = GEVulkanFeatures::supportsBaseVertexRendering(); + const bool bind_mesh_textures = GEVulkanFeatures::supportsBindMeshTexturesAtOnce(); + const bool use_dynamic = !use_base_vertex || bind_mesh_textures; VkDescriptorBufferInfo ubo_info; ubo_info.buffer = m_dynamic_data->getCurrentBuffer(); ubo_info.offset = 0; ubo_info.range = sizeof(GEVulkanCameraUBO); - std::array<VkWriteDescriptorSet, 3> data_set = {}; + std::vector<VkWriteDescriptorSet> data_set; + data_set.resize(3, {}); data_set[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; data_set[0].dstSet = m_data_descriptor_sets[cur_frame]; data_set[0].dstBinding = 0; data_set[0].dstArrayElement = 0; - data_set[0].descriptorType = use_base_vertex ? - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + data_set[0].descriptorType = use_dynamic ? + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; data_set[0].descriptorCount = 1; data_set[0].pBufferInfo = &ubo_info; VkDescriptorBufferInfo sbo_info_objects; sbo_info_objects.buffer = m_sbo_data->getCurrentBuffer(); - sbo_info_objects.offset = m_skinning_data_padded_size; + sbo_info_objects.offset = + m_materials_padded_size + m_skinning_data_padded_size; sbo_info_objects.range = m_object_data_padded_size; data_set[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; data_set[1].dstSet = m_data_descriptor_sets[cur_frame]; data_set[1].dstBinding = 1; data_set[1].dstArrayElement = 0; - data_set[1].descriptorType = use_base_vertex ? - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + data_set[1].descriptorType = use_dynamic ? + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC : + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; data_set[1].descriptorCount = 1; data_set[1].pBufferInfo = &sbo_info_objects; VkDescriptorBufferInfo sbo_info_skinning; sbo_info_skinning.buffer = m_sbo_data->getCurrentBuffer(); - sbo_info_skinning.offset = 0; + sbo_info_skinning.offset = m_materials_padded_size; sbo_info_skinning.range = m_skinning_data_padded_size; data_set[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; data_set[2].dstSet = m_data_descriptor_sets[cur_frame]; data_set[2].dstBinding = 2; data_set[2].dstArrayElement = 0; - data_set[2].descriptorType = use_base_vertex ? - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER : - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; ; + data_set[2].descriptorType = use_dynamic ? + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC : + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; ; data_set[2].descriptorCount = 1; data_set[2].pBufferInfo = &sbo_info_skinning; + VkDescriptorBufferInfo sbo_info_material; + sbo_info_material.buffer = m_sbo_data->getCurrentBuffer(); + sbo_info_material.offset = 0; + sbo_info_material.range = m_materials_padded_size; + if (bind_mesh_textures) + { + data_set.resize(4, {}); + data_set[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + data_set[3].dstSet = m_data_descriptor_sets[cur_frame]; + data_set[3].dstBinding = 3; + data_set[3].dstArrayElement = 0; + data_set[3].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + data_set[3].descriptorCount = 1; + data_set[3].pBufferInfo = &sbo_info_material; + } + vkUpdateDescriptorSets(vk->getDevice(), data_set.size(), data_set.data(), 0, NULL); m_texture_descriptor->updateDescriptor(); - - if (GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) - { - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_pipeline_layout, 0, 1, m_texture_descriptor->getDescriptorSet(), - 0, NULL); - } - if (use_base_vertex) { - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_pipeline_layout, 1, 1, &m_data_descriptor_sets[cur_frame], 0, - NULL); - GEVulkanMeshCache* mc = vk->getVulkanMeshCache(); std::array<VkBuffer, 2> vertex_buffer = {{ @@ -1071,13 +1214,21 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, scissor.extent.height = vp.height; vkCmdSetScissor(cmd, 0, 1, &scissor); - const bool use_multidraw = GEVulkanFeatures::supportsMultiDrawIndirect() && - GEVulkanFeatures::supportsBindMeshTexturesAtOnce(); - std::string cur_pipeline = m_cmds[0].m_shader; bool drawn_skybox = false; - if (use_multidraw) + if (bind_mesh_textures) { + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_pipeline_layout, 0, 1, m_texture_descriptor->getDescriptorSet(), + 0, NULL); + + std::array<uint32_t, 4> material_offsets = + {{ + 0u, + 0u, + 0u, + 0u, + }}; size_t indirect_offset = sizeof(GEVulkanCameraUBO); const size_t indirect_size = sizeof(VkDrawIndexedIndirectCommand); unsigned draw_count = 0; @@ -1086,6 +1237,11 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, if (m_cmds[i].m_shader != cur_pipeline) { bindPipeline(cmd, cur_pipeline); + material_offsets[3] = m_materials_data[cur_pipeline].first; + vkCmdBindDescriptorSets(cmd, + VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 1, 1, + &m_data_descriptor_sets[cur_frame], material_offsets.size(), + material_offsets.data()); vkCmdDrawIndexedIndirect(cmd, m_dynamic_data->getCurrentBuffer(), indirect_offset, draw_count, indirect_size); @@ -1101,16 +1257,17 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 0, 1, m_texture_descriptor->getDescriptorSet(), 0, NULL); - - vkCmdBindDescriptorSets(cmd, - VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 1, 1, - &m_data_descriptor_sets[cur_frame], 0, NULL); } continue; } draw_count++; } bindPipeline(cmd, m_cmds.back().m_shader); + material_offsets[3] = m_materials_data[m_cmds.back().m_shader].first; + vkCmdBindDescriptorSets(cmd, + VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 1, 1, + &m_data_descriptor_sets[cur_frame], material_offsets.size(), + material_offsets.data()); vkCmdDrawIndexedIndirect(cmd, m_dynamic_data->getCurrentBuffer(), indirect_offset, draw_count, indirect_size); @@ -1119,11 +1276,14 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, { int cur_mid = m_materials[m_cmds[0].m_mb]; bindPipeline(cmd, cur_pipeline); - if (!GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_pipeline_layout, 0, 1, + &m_texture_descriptor->getDescriptorSet()[cur_mid], 0, NULL); + if (use_base_vertex) { - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_pipeline_layout, 0, 1, - &m_texture_descriptor->getDescriptorSet()[cur_mid], 0, NULL); + vkCmdBindDescriptorSets(cmd, + VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 1, 1, + &m_data_descriptor_sets[cur_frame], 0, NULL); } for (unsigned i = 0; i < m_cmds.size(); i++) { @@ -1133,19 +1293,10 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, drawn_skybox = true; GEVulkanSkyBoxRenderer::render(cmd, cam); - if (!GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) - { - vkCmdBindDescriptorSets(cmd, - VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 0, - 1, &m_texture_descriptor->getDescriptorSet()[cur_mid], - 0, NULL); - } - else - { - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_pipeline_layout, 0, 1, - m_texture_descriptor->getDescriptorSet(), 0, NULL); - } + vkCmdBindDescriptorSets(cmd, + VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 0, 1, + &m_texture_descriptor->getDescriptorSet()[cur_mid], 0, + NULL); if (use_base_vertex) { vkCmdBindDescriptorSets(cmd, @@ -1155,8 +1306,7 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, } int mid = m_materials[m_cmds[i].m_mb]; - if (!GEVulkanFeatures::supportsBindMeshTexturesAtOnce() && - cur_mid != mid) + if (cur_mid != mid) { cur_mid = mid; vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, diff --git a/lib/graphics_engine/src/ge_vulkan_draw_call.hpp b/lib/graphics_engine/src/ge_vulkan_draw_call.hpp index 780dd744a..ce49f5d35 100644 --- a/lib/graphics_engine/src/ge_vulkan_draw_call.hpp +++ b/lib/graphics_engine/src/ge_vulkan_draw_call.hpp @@ -22,6 +22,7 @@ namespace irr namespace scene { class ISceneNode; class IBillboardSceneNode; struct SParticle; + class IMesh; } } @@ -104,6 +105,8 @@ private: std::vector<std::pair<irr::scene::ISceneNode*, int> > > > m_visible_nodes; + std::unordered_map<GESPMBuffer*, irr::scene::IMesh*> m_mb_map; + GECullingTool* m_culling_tool; std::vector<DrawCallData> m_cmds; @@ -120,6 +123,8 @@ private: size_t m_skinning_data_padded_size; + size_t m_materials_padded_size; + char* m_data_padding; VkDescriptorSetLayout m_data_layout; @@ -143,6 +148,9 @@ private: std::vector<size_t> m_sbo_data_offset; + std::unordered_map<std::string, std::pair<uint32_t, std::vector<int> > > + m_materials_data; + // ------------------------------------------------------------------------ void createAllPipelines(GEVulkanDriver* vk); // ------------------------------------------------------------------------ @@ -207,12 +215,14 @@ public: void reset() { m_visible_nodes.clear(); + m_mb_map.clear(); m_cmds.clear(); m_visible_objects.clear(); m_materials.clear(); m_skinning_nodes.clear(); m_data_uploading.clear(); m_sbo_data_offset.clear(); + m_materials_data.clear(); } }; // GEVulkanDrawCall diff --git a/lib/graphics_engine/src/ge_vulkan_driver.cpp b/lib/graphics_engine/src/ge_vulkan_driver.cpp index db08bb648..bec282164 100644 --- a/lib/graphics_engine/src/ge_vulkan_driver.cpp +++ b/lib/graphics_engine/src/ge_vulkan_driver.cpp @@ -890,17 +890,21 @@ bool GEVulkanDriver::checkDeviceExtensions(VkPhysicalDevice device) VkPhysicalDeviceProperties properties = {}; vkGetPhysicalDeviceProperties(device, &properties); - if (properties.apiVersion < VK_API_VERSION_1_2) + for (auto& ext : extensions) { - for (auto& ext : extensions) + if (properties.apiVersion < VK_API_VERSION_1_2 && + strcmp(ext.extensionName, + VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME) == 0) { - if (strcmp(ext.extensionName, - VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME) == 0) - { - m_device_extensions.push_back( - VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); - break; - } + m_device_extensions.push_back( + VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); + } + else if (properties.apiVersion < VK_API_VERSION_1_1 && + strcmp(ext.extensionName, + VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME) == 0) + { + m_device_extensions.push_back( + VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME); } } @@ -1027,6 +1031,13 @@ void GEVulkanDriver::createDevice() descriptor_indexing_features.descriptorBindingPartiallyBound = GEVulkanFeatures::supportsPartiallyBound(); + VkPhysicalDeviceShaderDrawParametersFeatures shader_draw = {}; + shader_draw.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES; + shader_draw.shaderDrawParameters = + GEVulkanFeatures::supportsShaderDrawParameters(); + descriptor_indexing_features.pNext = &shader_draw; + if (m_features.samplerAnisotropy == VK_TRUE) device_features.samplerAnisotropy = VK_TRUE; diff --git a/lib/graphics_engine/src/ge_vulkan_features.cpp b/lib/graphics_engine/src/ge_vulkan_features.cpp index 67e7a4b0c..ec3dabd75 100644 --- a/lib/graphics_engine/src/ge_vulkan_features.cpp +++ b/lib/graphics_engine/src/ge_vulkan_features.cpp @@ -190,6 +190,9 @@ void GEVulkanFeatures::init(GEVulkanDriver* vk) g_supports_base_vertex_rendering = mvk_features.baseVertexInstanceDrawing; if (!g_supports_base_vertex_rendering) g_supports_multi_draw_indirect = false; + + // https://github.com/KhronosGroup/MoltenVK/issues/1743 + g_supports_shader_draw_parameters = false; #endif } // init @@ -286,7 +289,8 @@ bool GEVulkanFeatures::supportsPartiallyBound() // ---------------------------------------------------------------------------- bool GEVulkanFeatures::supportsBindMeshTexturesAtOnce() { - if (!g_supports_bind_textures_at_once) + if (!g_supports_bind_textures_at_once || !g_supports_multi_draw_indirect || + !g_supports_shader_draw_parameters) return false; const unsigned sampler_count = GEVulkanShaderManager::getSamplerSize() * GEVulkanShaderManager::getMeshTextureLayer();