Reduce vram usage by removing bones data in static spm

This commit is contained in:
Benau 2022-07-27 11:45:09 +08:00
parent d37a3b8c7e
commit 6d74e84609
7 changed files with 116 additions and 38 deletions

View File

@ -29,12 +29,15 @@ private:
size_t m_vbo_offset;
size_t m_ibo_offset;
bool m_has_skinning;
public:
// ------------------------------------------------------------------------
GESPMBuffer()
{
m_vbo_offset = 0;
m_ibo_offset = 0;
m_has_skinning = false;
}
// ------------------------------------------------------------------------
virtual const irr::video::SMaterial& getMaterial() const
@ -160,6 +163,8 @@ public:
void setIBOOffset(size_t offset) { m_ibo_offset = offset; }
// ------------------------------------------------------------------------
size_t getIBOOffset() const { return m_ibo_offset; }
// ------------------------------------------------------------------------
bool hasSkinning() const { return m_has_skinning; }
};
} // end namespace irr

View File

@ -155,16 +155,13 @@ void GEVulkanDrawCall::generate()
unsigned visible_count = p.second.size();
if (visible_count != 0)
{
bool skinning = false;
bool skinning = p.first->hasSkinning();
for (auto* node : p.second)
{
int skinning_offset = -1000;
auto it = skinning_offets.find(node);
if (it != skinning_offets.end())
{
skinning = true;
skinning_offset = it->second;
}
m_visible_objects.emplace_back(node, material_id,
skinning_offset);
}
@ -213,23 +210,27 @@ void GEVulkanDrawCall::createAllPipelines(GEVulkanDriver* vk)
{
PipelineSettings settings;
settings.m_vertex_shader = "spm.vert";
settings.m_skinning_vertex_shader = "spm_skinning.vert";
settings.m_fragment_shader = "solid.frag";
settings.m_shader_name = "solid";
createPipeline(vk, settings);
settings.m_vertex_shader = "spm_skinning.vert";
settings.m_shader_name = "solid_skinning";
createPipeline(vk, settings);
} // createAllPipelines
// ----------------------------------------------------------------------------
void GEVulkanDrawCall::createPipeline(GEVulkanDriver* vk,
const PipelineSettings& settings)
{
bool creating_animated_pipeline_for_skinning = false;
std::string shader_name = settings.m_shader_name;
start:
VkPipelineShaderStageCreateInfo vert_shader_stage_info = {};
vert_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vert_shader_stage_info.module = GEVulkanShaderManager::getShader(settings.m_vertex_shader);
vert_shader_stage_info.module = GEVulkanShaderManager::getShader(
creating_animated_pipeline_for_skinning ?
settings.m_skinning_vertex_shader : settings.m_vertex_shader);
vert_shader_stage_info.pName = "main";
VkPipelineShaderStageCreateInfo frag_shader_stage_info = {};
@ -244,10 +245,15 @@ void GEVulkanDrawCall::createPipeline(GEVulkanDriver* vk,
frag_shader_stage_info
}};
VkVertexInputBindingDescription binding_description = {};
binding_description.binding = 0;
binding_description.stride = sizeof(irr::video::S3DVertexSkinnedMesh);
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
size_t bone_pitch = sizeof(int16_t) * 8;
size_t static_pitch = sizeof(irr::video::S3DVertexSkinnedMesh) - bone_pitch;
std::array<VkVertexInputBindingDescription, 2> binding_descriptions;
binding_descriptions[0].binding = 0;
binding_descriptions[0].stride = static_pitch;
binding_descriptions[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
binding_descriptions[1].binding = 1;
binding_descriptions[1].stride = bone_pitch;
binding_descriptions[1].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
std::array<VkVertexInputAttributeDescription, 8> attribute_descriptions = {};
attribute_descriptions[0].binding = 0;
@ -274,21 +280,21 @@ void GEVulkanDrawCall::createPipeline(GEVulkanDriver* vk,
attribute_descriptions[5].location = 5;
attribute_descriptions[5].format = VK_FORMAT_A2B10G10R10_SNORM_PACK32;
attribute_descriptions[5].offset = offsetof(irr::video::S3DVertexSkinnedMesh, m_tangent);
attribute_descriptions[6].binding = 0;
attribute_descriptions[6].binding = 1;
attribute_descriptions[6].location = 6;
attribute_descriptions[6].format = VK_FORMAT_R16G16B16A16_SINT;
attribute_descriptions[6].offset = offsetof(irr::video::S3DVertexSkinnedMesh, m_joint_idx);
attribute_descriptions[7].binding = 0;
attribute_descriptions[6].offset = 0;
attribute_descriptions[7].binding = 1;
attribute_descriptions[7].location = 7;
attribute_descriptions[7].format = VK_FORMAT_R16G16B16A16_SFLOAT;
attribute_descriptions[7].offset = offsetof(irr::video::S3DVertexSkinnedMesh, m_weight);
attribute_descriptions[7].offset = sizeof(int16_t) * 4;
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_info.vertexBindingDescriptionCount = 1;
vertex_input_info.vertexAttributeDescriptionCount = attribute_descriptions.size();
vertex_input_info.pVertexBindingDescriptions = &binding_description;
vertex_input_info.pVertexAttributeDescriptions = &attribute_descriptions[0];
vertex_input_info.vertexBindingDescriptionCount = 2;
vertex_input_info.vertexAttributeDescriptionCount = 8;
vertex_input_info.pVertexBindingDescriptions = binding_descriptions.data();
vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions.data();
VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
@ -390,10 +396,19 @@ void GEVulkanDrawCall::createPipeline(GEVulkanDriver* vk,
if (result != VK_SUCCESS)
{
throw std::runtime_error("vkCreateGraphicsPipelines failed for " +
settings.m_shader_name);
shader_name);
}
m_graphics_pipelines[settings.m_shader_name] = std::make_pair(
m_graphics_pipelines[shader_name] = std::make_pair(
graphics_pipeline, settings);
if (settings.m_skinning_vertex_shader.empty())
return;
else if (creating_animated_pipeline_for_skinning)
return;
creating_animated_pipeline_for_skinning = true;
shader_name = settings.m_shader_name + "_skinning";
goto start;
} // createPipeline
// ----------------------------------------------------------------------------
@ -648,10 +663,19 @@ 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);
VkDeviceSize offsets[] = {0};
GEVulkanMeshCache* mc = vk->getVulkanMeshCache();
VkBuffer vertex_buffer = mc->getBuffer();
vkCmdBindVertexBuffers(cmd, 0, 1, &vertex_buffer, offsets);
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);

View File

@ -42,6 +42,7 @@ struct ObjectData
struct PipelineSettings
{
std::string m_vertex_shader;
std::string m_skinning_vertex_shader;
std::string m_fragment_shader;
std::string m_shader_name;
};

View File

@ -6,6 +6,8 @@
#include "IAnimatedMesh.h"
#include <algorithm>
#include <cassert>
#include <vector>
namespace GE
@ -19,7 +21,7 @@ GEVulkanMeshCache::GEVulkanMeshCache()
m_ge_cache_time = 0;
m_buffer = VK_NULL_HANDLE;
m_memory = VK_NULL_HANDLE;
m_ibo_offset = 0;
m_ibo_offset = m_skinning_vbo_offset = 0;
} // init
// ----------------------------------------------------------------------------
@ -36,6 +38,10 @@ void GEVulkanMeshCache::updateCache()
m_ge_cache_time = m_irrlicht_cache_time;
destroy();
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, ibo_size;
vbo_size = 0;
ibo_size = 0;
@ -47,14 +53,21 @@ void GEVulkanMeshCache::updateCache()
continue;
for (unsigned j = 0; j < mesh->getMeshBufferCount(); j++)
{
scene::IMeshBuffer* mb = mesh->getMeshBuffer(j);
vbo_size += mesh->getMeshBuffer(j)->getVertexCount();
ibo_size += mesh->getMeshBuffer(j)->getIndexCount();
buffers.push_back(static_cast<GESPMBuffer*>(mb));
GESPMBuffer* mb = static_cast<GESPMBuffer*>(mesh->getMeshBuffer(j));
size_t pitch = mb->hasSkinning() ? total_pitch : static_pitch;
vbo_size += mb->getVertexCount() * pitch;
ibo_size += mb->getIndexCount();
buffers.push_back(mb);
}
}
vbo_size *= getVertexPitchFromType(video::EVT_SKINNED_MESH);
ibo_size *= sizeof(uint16_t);
// Some devices (Apple for now) require vertex offset of alignment 4 bytes
ibo_size += getPadding(ibo_size, 4);
std::stable_partition(buffers.begin(), buffers.end(),
[](const GESPMBuffer* mb)
{
return !mb->hasSkinning();
});
VkBuffer staging_buffer = VK_NULL_HANDLE;
VmaAllocation staging_memory = VK_NULL_HANDLE;
@ -78,12 +91,16 @@ void GEVulkanMeshCache::updateCache()
size_t offset = 0;
for (GESPMBuffer* spm_buffer : buffers)
{
size_t copy_size = spm_buffer->getVertexCount() *
getVertexPitchFromType(video::EVT_SKINNED_MESH);
size_t real_size = spm_buffer->getVertexCount() * total_pitch;
size_t copy_size = spm_buffer->getVertexCount() * static_pitch;
uint8_t* loc = mapped + offset;
memcpy(loc, spm_buffer->getVertices(), copy_size);
spm_buffer->setVBOOffset(
offset / getVertexPitchFromType(video::EVT_SKINNED_MESH));
for (unsigned i = 0; i < real_size; i += total_pitch)
{
uint8_t* vertices = ((uint8_t*)spm_buffer->getVertices()) + i;
memcpy(loc, vertices, static_pitch);
loc += static_pitch;
}
spm_buffer->setVBOOffset(offset / static_pitch);
offset += copy_size;
}
m_ibo_offset = offset;
@ -97,6 +114,33 @@ void GEVulkanMeshCache::updateCache()
spm_buffer->setIBOOffset(offset / sizeof(uint16_t));
offset += copy_size;
}
m_skinning_vbo_offset = m_ibo_offset + offset;
m_skinning_vbo_offset += getPadding(m_skinning_vbo_offset, 4);
offset = 0;
size_t static_vertex_offset = 0;
for (GESPMBuffer* spm_buffer : buffers)
{
if (!spm_buffer->hasSkinning())
{
static_vertex_offset += spm_buffer->getVertexCount() * bone_pitch;
continue;
}
uint8_t* loc = mapped + offset + m_skinning_vbo_offset;
size_t real_size = spm_buffer->getVertexCount() * total_pitch;
for (unsigned i = 0; i < real_size; i += total_pitch)
{
uint8_t* vertices = ((uint8_t*)spm_buffer->getVertices()) + i +
static_pitch;
memcpy(loc, vertices, bone_pitch);
loc += bone_pitch;
}
offset += spm_buffer->getVertexCount() * bone_pitch;
}
assert(m_skinning_vbo_offset + offset == vbo_size + ibo_size);
assert(static_vertex_offset < m_skinning_vbo_offset);
m_skinning_vbo_offset -= static_vertex_offset;
vmaUnmapMemory(m_vk->getVmaAllocator(), staging_memory);
vmaFlushAllocation(m_vk->getVmaAllocator(), staging_memory, 0, offset);
@ -121,7 +165,7 @@ void GEVulkanMeshCache::destroy()
vmaDestroyBuffer(m_vk->getVmaAllocator(), m_buffer, m_memory);
m_buffer = VK_NULL_HANDLE;
m_memory = VK_NULL_HANDLE;
m_ibo_offset = 0;
m_ibo_offset = m_skinning_vbo_offset = 0;
} // destroy
}

View File

@ -21,7 +21,7 @@ private:
VmaAllocation m_memory;
size_t m_ibo_offset;
size_t m_ibo_offset, m_skinning_vbo_offset;
public:
// ------------------------------------------------------------------------
GEVulkanMeshCache();
@ -35,6 +35,8 @@ public:
VkBuffer getBuffer() const { return m_buffer; }
// ------------------------------------------------------------------------
size_t getIBOOffset() const { return m_ibo_offset; }
// ------------------------------------------------------------------------
size_t getSkinningVBOOffset() const { return m_skinning_vbo_offset; }
}; // GEVulkanMeshCache
}

View File

@ -105,6 +105,7 @@ scene::IAnimatedMesh* B3DMeshLoader::createMesh(io::IReadFile* f)
{
SP::SPMeshBuffer* spbuf = spm->getSPMeshBuffer(i);
GE::GESPMBuffer* gebuf = new GE::GESPMBuffer();
gebuf->m_has_skinning = !spm->isStatic();
ge_spm->m_buffer.push_back(gebuf);
std::swap(gebuf->m_vertices, spbuf->getVerticesRef());
std::swap(gebuf->m_indices, spbuf->getIndicesRef());

View File

@ -556,6 +556,7 @@ void SPMeshLoader::decompressGESPM(irr::io::IReadFile* spm,
// 1.0 in half float (16bit)
vertex.m_weight[0] = 15360;
}
mb->m_has_skinning = true;
}
vertices.push_back(vertex);
}