Add code to render vbo and ibo individually for some devices

This commit is contained in:
Benau 2022-08-02 10:21:07 +08:00
parent 0ad22efcf7
commit 00e7b04f13
7 changed files with 230 additions and 33 deletions

View File

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

View File

@ -1,10 +1,14 @@
#ifndef HEADER_GE_SPM_BUFFER_HPP
#define HEADER_GE_SPM_BUFFER_HPP
#include <array>
#include <cstddef>
#include <vector>
#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<VkBuffer, 2> vertex_buffer =
{{
m_buffer,
m_buffer
}};
std::array<VkDeviceSize, 2> 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

View File

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

View File

@ -0,0 +1,103 @@
#include "ge_spm_buffer.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_features.hpp"
#include <algorithm>
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
}

View File

@ -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<VkBuffer, 2> vertex_buffer =
{{
mc->getBuffer(),
mc->getBuffer()
}};
std::array<VkDeviceSize, 2> 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<VkBuffer, 2> vertex_buffer =
{{
mc->getBuffer(),
mc->getBuffer()
}};
std::array<VkDeviceSize, 2> 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,

View File

@ -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<std::string, std::pair<VkPipeline, PipelineSettings> >
m_graphics_pipelines;
std::unordered_map<int32_t, int> m_materials;
std::unordered_map<size_t, int> m_materials;
GEVulkanTextureDescriptor* m_texture_descriptor;

View File

@ -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<GESPMBuffer*>(
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