diff --git a/lib/graphics_engine/CMakeLists.txt b/lib/graphics_engine/CMakeLists.txt index d66399e84..4bcfb47ad 100644 --- a/lib/graphics_engine/CMakeLists.txt +++ b/lib/graphics_engine/CMakeLists.txt @@ -47,6 +47,7 @@ set(GE_SOURCES src/ge_vulkan_texture_descriptor.cpp src/ge_gl_texture.cpp src/ge_spm.cpp + src/ge_spm_buffer.cpp ) if(NOT APPLE OR DLOPEN_MOLTENVK) diff --git a/lib/graphics_engine/include/ge_spm_buffer.hpp b/lib/graphics_engine/include/ge_spm_buffer.hpp index 381dcdca2..62443acae 100644 --- a/lib/graphics_engine/include/ge_spm_buffer.hpp +++ b/lib/graphics_engine/include/ge_spm_buffer.hpp @@ -1,10 +1,14 @@ #ifndef HEADER_GE_SPM_BUFFER_HPP #define HEADER_GE_SPM_BUFFER_HPP +#include #include #include #include "IMeshBuffer.h" +#include "ge_vma.hpp" +#include "vulkan_wrapper.h" + class B3DMeshLoader; class SPMeshLoader; @@ -30,6 +34,12 @@ private: size_t m_ibo_offset; + size_t m_skinning_vbo_offset; + + VkBuffer m_buffer; + + VmaAllocation m_memory; + bool m_has_skinning; public: // ------------------------------------------------------------------------ @@ -37,9 +47,14 @@ public: { m_vbo_offset = 0; m_ibo_offset = 0; + m_skinning_vbo_offset = 0; + m_buffer = VK_NULL_HANDLE; + m_memory = VK_NULL_HANDLE; m_has_skinning = false; } // ------------------------------------------------------------------------ + ~GESPMBuffer() { destroyVertexIndexBuffer(); } + // ------------------------------------------------------------------------ virtual const irr::video::SMaterial& getMaterial() const { return m_material; } // ------------------------------------------------------------------------ @@ -165,6 +180,28 @@ public: size_t getIBOOffset() const { return m_ibo_offset; } // ------------------------------------------------------------------------ bool hasSkinning() const { return m_has_skinning; } + // ------------------------------------------------------------------------ + void bindVertexIndexBuffer(VkCommandBuffer cmd) + { + std::array vertex_buffer = + {{ + m_buffer, + m_buffer + }}; + std::array offsets = + {{ + 0, + m_skinning_vbo_offset + }}; + vkCmdBindVertexBuffers(cmd, 0, vertex_buffer.size(), + vertex_buffer.data(), offsets.data()); + vkCmdBindIndexBuffer(cmd, m_buffer, m_ibo_offset, + VK_INDEX_TYPE_UINT16); + } + // ------------------------------------------------------------------------ + void createVertexIndexBuffer(); + // ------------------------------------------------------------------------ + void destroyVertexIndexBuffer(); }; } // end namespace irr diff --git a/lib/graphics_engine/src/ge_spm.cpp b/lib/graphics_engine/src/ge_spm.cpp index eb78b4d03..f88715618 100644 --- a/lib/graphics_engine/src/ge_spm.cpp +++ b/lib/graphics_engine/src/ge_spm.cpp @@ -45,7 +45,10 @@ void GESPM::finalize() { m_bounding_box.reset(0.0f, 0.0f, 0.0f); for (unsigned i = 0; i < m_buffer.size(); i++) + { m_bounding_box.addInternalBox(m_buffer[i]->getBoundingBox()); + m_buffer[i]->createVertexIndexBuffer(); + } for (Armature& arm : getArmatures()) { diff --git a/lib/graphics_engine/src/ge_spm_buffer.cpp b/lib/graphics_engine/src/ge_spm_buffer.cpp new file mode 100644 index 000000000..ca80a7424 --- /dev/null +++ b/lib/graphics_engine/src/ge_spm_buffer.cpp @@ -0,0 +1,103 @@ +#include "ge_spm_buffer.hpp" + +#include "ge_main.hpp" +#include "ge_vulkan_driver.hpp" +#include "ge_vulkan_features.hpp" + +#include + +namespace GE +{ +// ---------------------------------------------------------------------------- +void GESPMBuffer::createVertexIndexBuffer() +{ + if (GEVulkanFeatures::supportsBaseVertexRendering()) + return; + + GEVulkanDriver* vk = getVKDriver(); + size_t total_pitch = getVertexPitchFromType(video::EVT_SKINNED_MESH); + size_t bone_pitch = sizeof(int16_t) * 8; + size_t static_pitch = total_pitch - bone_pitch; + size_t vbo_size = getVertexCount() * static_pitch; + m_ibo_offset = vbo_size; + size_t ibo_size = getIndexCount() * sizeof(uint16_t); + size_t total_size = vbo_size + ibo_size; + if (m_has_skinning) + { + total_size += getPadding(total_size, 4); + m_skinning_vbo_offset = total_size; + total_size += getVertexCount() * bone_pitch; + } + + VkBuffer staging_buffer = VK_NULL_HANDLE; + VmaAllocation staging_memory = VK_NULL_HANDLE; + VmaAllocationCreateInfo staging_buffer_create_info = {}; + staging_buffer_create_info.usage = VMA_MEMORY_USAGE_AUTO; + staging_buffer_create_info.flags = + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + staging_buffer_create_info.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + + if (!vk->createBuffer(total_size, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, staging_buffer_create_info, + staging_buffer, staging_memory)) + { + throw std::runtime_error("createVertexIndexBuffer create staging " + "buffer failed"); + } + + uint8_t* mapped; + if (vmaMapMemory(vk->getVmaAllocator(), staging_memory, + (void**)&mapped) != VK_SUCCESS) + throw std::runtime_error("createVertexIndexBuffer vmaMapMemory failed"); + + size_t real_size = getVertexCount() * total_pitch; + uint8_t* loc = mapped; + for (unsigned i = 0; i < real_size; i += total_pitch) + { + uint8_t* vertices = ((uint8_t*)getVertices()) + i; + memcpy(loc, vertices, static_pitch); + loc += static_pitch; + } + memcpy(loc, m_indices.data(), m_indices.size() * sizeof(uint16_t)); + + if (m_has_skinning) + { + loc = mapped + m_skinning_vbo_offset; + for (unsigned i = 0; i < real_size; i += total_pitch) + { + uint8_t* vertices = ((uint8_t*)getVertices()) + i + + static_pitch; + memcpy(loc, vertices, bone_pitch); + loc += bone_pitch; + } + } + + vmaUnmapMemory(vk->getVmaAllocator(), staging_memory); + vmaFlushAllocation(vk->getVmaAllocator(), staging_memory, 0, total_size); + + VmaAllocationCreateInfo local_create_info = {}; + local_create_info.usage = VMA_MEMORY_USAGE_AUTO; + if (!vk->createBuffer(total_size, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, local_create_info, m_buffer, + m_memory)) + throw std::runtime_error("updateCache create buffer failed"); + + vk->copyBuffer(staging_buffer, m_buffer, total_size); + vmaDestroyBuffer(vk->getVmaAllocator(), staging_buffer, staging_memory); +} // createVertexIndexBuffer + +// ---------------------------------------------------------------------------- +void GESPMBuffer::destroyVertexIndexBuffer() +{ + if (m_buffer == VK_NULL_HANDLE || m_memory == VK_NULL_HANDLE) + return; + + getVKDriver()->waitIdle(); + vmaDestroyBuffer(getVKDriver()->getVmaAllocator(), m_buffer, m_memory); + m_buffer = VK_NULL_HANDLE; + m_memory = VK_NULL_HANDLE; +} // destroyVertexIndexBuffer + +} diff --git a/lib/graphics_engine/src/ge_vulkan_draw_call.cpp b/lib/graphics_engine/src/ge_vulkan_draw_call.cpp index f50ba9dee..0bdd07930 100644 --- a/lib/graphics_engine/src/ge_vulkan_draw_call.cpp +++ b/lib/graphics_engine/src/ge_vulkan_draw_call.cpp @@ -172,6 +172,7 @@ void GEVulkanDrawCall::generate() return nodes_area.at(a.first) < nodes_area.at(b.first) ; }); + const bool use_base_vertex = GEVulkanFeatures::supportsBaseVertexRendering(); unsigned accumulated_instance = 0; for (auto& p : visible_nodes) { @@ -190,7 +191,12 @@ void GEVulkanDrawCall::generate() const irr::video::ITexture** list = &textures[0]; const int material_id = m_texture_descriptor->getTextureID(list); if (!GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) - m_materials[p.first->getVBOOffset()] = material_id; + { + if (use_base_vertex) + m_materials[p.first->getVBOOffset()] = material_id; + else + m_materials[(size_t)p.first] = material_id; + } const bool skinning = p.first->hasSkinning(); for (auto& q : p.second) @@ -217,25 +223,37 @@ void GEVulkanDrawCall::generate() VkDrawIndexedIndirectCommand cmd; cmd.indexCount = p.first->getIndexCount(); cmd.instanceCount = visible_count; - cmd.firstIndex = p.first->getIBOOffset(); - cmd.vertexOffset = p.first->getVBOOffset(); + 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; 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 }); + m_cmds.push_back({ cmd, cur_shader, sorting_key, p.first }); } } if (!GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) { - std::stable_sort(m_cmds.begin(), m_cmds.end(), - [this](const DrawCallData& a, const DrawCallData& b) - { - return m_materials[a.m_cmd.vertexOffset] < - m_materials[b.m_cmd.vertexOffset]; - }); + if (use_base_vertex) + { + std::stable_sort(m_cmds.begin(), m_cmds.end(), + [this](const DrawCallData& a, const DrawCallData& b) + { + return m_materials[a.m_cmd.vertexOffset] < + m_materials[b.m_cmd.vertexOffset]; + }); + } + else + { + std::stable_sort(m_cmds.begin(), m_cmds.end(), + [this](const DrawCallData& a, const DrawCallData& b) + { + return m_materials[(size_t)a.m_mb] < + m_materials[(size_t)b.m_mb]; + }); + } } std::stable_sort(m_cmds.begin(), m_cmds.end(), @@ -888,25 +906,25 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, 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 vertex_buffer = + {{ + mc->getBuffer(), + mc->getBuffer() + }}; + std::array offsets = + {{ + 0, + mc->getSkinningVBOOffset() + }}; + vkCmdBindVertexBuffers(cmd, 0, vertex_buffer.size(), + vertex_buffer.data(), offsets.data()); + + vkCmdBindIndexBuffer(cmd, mc->getBuffer(), mc->getIBOOffset(), + VK_INDEX_TYPE_UINT16); } - GEVulkanMeshCache* mc = vk->getVulkanMeshCache(); - std::array vertex_buffer = - {{ - mc->getBuffer(), - mc->getBuffer() - }}; - std::array offsets = - {{ - 0, - mc->getSkinningVBOOffset() - }}; - vkCmdBindVertexBuffers(cmd, 0, vertex_buffer.size(), vertex_buffer.data(), - offsets.data()); - - vkCmdBindIndexBuffer(cmd, mc->getBuffer(), mc->getIBOOffset(), - VK_INDEX_TYPE_UINT16); - VkViewport vp; vp.x = cam->getViewPort().UpperLeftCorner.X; vp.y = cam->getViewPort().UpperLeftCorner.Y; @@ -956,7 +974,11 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, } else { - int cur_mid = m_materials[m_cmds[0].m_cmd.vertexOffset]; + int cur_mid = 0; + if (use_base_vertex) + cur_mid = m_materials[m_cmds[0].m_cmd.vertexOffset]; + else + cur_mid = m_materials[(size_t)m_cmds[0].m_mb]; bindPipeline(cmd, cur_pipeline); if (!GEVulkanFeatures::supportsBindMeshTexturesAtOnce()) { @@ -967,7 +989,11 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, for (unsigned i = 0; i < m_cmds.size(); i++) { const VkDrawIndexedIndirectCommand& cur_cmd = m_cmds[i].m_cmd; - int mid = m_materials[cur_cmd.vertexOffset]; + int mid = 0; + if (use_base_vertex) + mid = m_materials[cur_cmd.vertexOffset]; + else + mid = m_materials[(size_t)m_cmds[i].m_mb]; if (!GEVulkanFeatures::supportsBindMeshTexturesAtOnce() && cur_mid != mid) { @@ -993,6 +1019,7 @@ void GEVulkanDrawCall::render(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam, vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 1, 1, &m_data_descriptor_sets[cur_frame], dynamic_offsets.size(), dynamic_offsets.data()); + m_cmds[i].m_mb->bindVertexIndexBuffer(cmd); } vkCmdDrawIndexed(cmd, cur_cmd.indexCount, cur_cmd.instanceCount, cur_cmd.firstIndex, cur_cmd.vertexOffset, diff --git a/lib/graphics_engine/src/ge_vulkan_draw_call.hpp b/lib/graphics_engine/src/ge_vulkan_draw_call.hpp index 73c51dfe0..46e5210f9 100644 --- a/lib/graphics_engine/src/ge_vulkan_draw_call.hpp +++ b/lib/graphics_engine/src/ge_vulkan_draw_call.hpp @@ -62,6 +62,7 @@ struct DrawCallData VkDrawIndexedIndirectCommand m_cmd; std::string m_shader; std::string m_sorting_key; + GESPMBuffer* m_mb; }; class GEVulkanDrawCall @@ -96,7 +97,7 @@ private: std::unordered_map > m_graphics_pipelines; - std::unordered_map m_materials; + std::unordered_map m_materials; GEVulkanTextureDescriptor* m_texture_descriptor; diff --git a/lib/graphics_engine/src/ge_vulkan_mesh_cache.cpp b/lib/graphics_engine/src/ge_vulkan_mesh_cache.cpp index 169f2f6c2..4e3175bb7 100644 --- a/lib/graphics_engine/src/ge_vulkan_mesh_cache.cpp +++ b/lib/graphics_engine/src/ge_vulkan_mesh_cache.cpp @@ -1,8 +1,10 @@ #include "ge_vulkan_mesh_cache.hpp" #include "ge_main.hpp" + #include "ge_spm_buffer.hpp" #include "ge_vulkan_driver.hpp" +#include "ge_vulkan_features.hpp" #include "IAnimatedMesh.h" @@ -33,6 +35,9 @@ void GEVulkanMeshCache::meshCacheChanged() // ---------------------------------------------------------------------------- void GEVulkanMeshCache::updateCache() { + if (!GEVulkanFeatures::supportsBaseVertexRendering()) + return; + if (m_irrlicht_cache_time <= m_ge_cache_time) return; m_ge_cache_time = m_irrlicht_cache_time; @@ -161,10 +166,30 @@ void GEVulkanMeshCache::updateCache() void GEVulkanMeshCache::destroy() { m_vk->waitIdle(); + m_vk->setDisableWaitIdle(true); + if (!GEVulkanFeatures::supportsBaseVertexRendering()) + { + for (unsigned i = 0; i < Meshes.size(); i++) + { + scene::IAnimatedMesh* mesh = Meshes[i].Mesh; + if (mesh->getMeshType() != scene::EAMT_SPM) + continue; + for (unsigned j = 0; j < mesh->getMeshBufferCount(); j++) + { + GESPMBuffer* mb = static_cast( + mesh->getMeshBuffer(j)); + mb->destroyVertexIndexBuffer(); + } + } + } + else + { + vmaDestroyBuffer(m_vk->getVmaAllocator(), m_buffer, m_memory); + m_buffer = VK_NULL_HANDLE; + m_memory = VK_NULL_HANDLE; + } + m_vk->setDisableWaitIdle(false); - vmaDestroyBuffer(m_vk->getVmaAllocator(), m_buffer, m_memory); - m_buffer = VK_NULL_HANDLE; - m_memory = VK_NULL_HANDLE; m_ibo_offset = m_skinning_vbo_offset = 0; } // destroy