Skip to content

Commit

Permalink
Use meshoptimizer to optimize vertex cache and strips.
Browse files Browse the repository at this point in the history
  • Loading branch information
Hans-Kristian Arntzen committed May 12, 2018
1 parent b4eaebe commit 9580cb3
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@
[submodule "third_party/volk"]
path = third_party/volk
url = git://github.com/zeux/volk
[submodule "third_party/meshoptimizer"]
path = third_party/meshoptimizer
url = git://github.com/zeux/meshoptimizer
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ add_subdirectory(third_party/shaderc EXCLUDE_FROM_ALL)
add_subdirectory(third_party/stb EXCLUDE_FROM_ALL)
add_subdirectory(third_party/spirv-cross EXCLUDE_FROM_ALL)
add_subdirectory(third_party/astc-encoder/Source EXCLUDE_FROM_ALL)
add_subdirectory(third_party/meshoptimizer EXCLUDE_FROM_ALL)

set(FOSSILIZE_CLI OFF CACHE BOOL "Fossilize CLI." FORCE)
set(FOSSILIZE_TESTS OFF CACHE BOOL "Fossilize tests." FORCE)
Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,18 +161,15 @@ The code is licensed under MIT. Feel free to use it for whatever purpose.

These are pulled in as submodules.

- [FastNoise](https://github.com/Auburns/FastNoise)
- [GLFW](https://github.com/glfw/glfw)
- [GLI](https://github.com/g-truc/gli)
- [GLM](https://github.com/g-truc/glm)
- [glslang](https://github.com/google/glslang.git)
- [muFFT](https://github.com/Themaister/muFFT)
- [rapidjson](https://github.com/miloyip/rapidjson)
- [shaderc](https://github.com/google/shaderc.git)
- [SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross)
- [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers)
- [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools)
- [stb](https://github.com/nothings/stb)
- [volk](https://github.com/zeux/volk)
- [meshoptimizer](https://github.com/zeux/meshoptimizer)
- [Fossilize](https://github.com/Themaister/Fossilize)

2 changes: 1 addition & 1 deletion scene_formats/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ add_granite_library(scene-formats
add_granite_library(scene-formats-export
gltf_export.cpp gltf_export.hpp)

target_link_libraries(scene-formats math util filesystem renderer vulkan-backend rapidjson)
target_link_libraries(scene-formats math util filesystem renderer vulkan-backend rapidjson meshoptimizer)
target_link_libraries(scene-formats-export scene-formats math util filesystem renderer vulkan-backend rapidjson texture-compression threading)
target_include_directories(scene-formats PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(scene-formats-export PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Expand Down
55 changes: 52 additions & 3 deletions scene_formats/gltf_export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct EmittedMesh
int material = -1;
uint32_t attribute_mask = 0;
int attribute_accessor[ecast(MeshAttribute::Count)] = {};
VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_END_RANGE;
};

struct EmittedEnvironment
Expand Down Expand Up @@ -784,8 +785,7 @@ static void quantize_attribute_fp32_fp16(uint8_t *output,
}
}

static void quantize_attribute_rg32f_rg16unorm(uint8_t *output,
const uint8_t *buffer, uint32_t count)
static void quantize_attribute_rg32f_rg16unorm(uint8_t *output, const uint8_t *buffer, uint32_t count)
{
for (uint32_t i = 0; i < count; i++)
{
Expand All @@ -799,6 +799,17 @@ static void quantize_attribute_rg32f_rg16unorm(uint8_t *output,
}
}

static void quantize_attribute_rg32f_rg16f(uint8_t *output, const uint8_t *buffer, uint32_t count)
{
for (uint32_t i = 0; i < count; i++)
{
vec2 input;
memcpy(input.data, buffer + sizeof(vec2) * i, sizeof(vec2));
u16vec2 result = floatToHalf(input);
memcpy(output + i * sizeof(u16vec2), result.data, sizeof(u16vec2));
}
}

static void quantize_attribute_fp32_a2bgr10snorm(uint8_t *output, const uint8_t *buffer, uint32_t stride, uint32_t count)
{
for (uint32_t i = 0; i < count; i++)
Expand Down Expand Up @@ -834,11 +845,11 @@ static void extract_attribute(uint8_t *output,
void RemapState::emit_mesh(unsigned remapped_index)
{
auto mesh = mesh_optimize_index_buffer(*this->mesh.info[remapped_index]);
//auto &mesh = *this->mesh.info[remapped_index];
mesh_cache.resize(std::max<size_t>(mesh_cache.size(), remapped_index + 1));

auto &emit = mesh_cache[remapped_index];
emit.material = mesh.has_material ? int(mesh.material_index) : -1;
emit.topology = mesh.topology;

if (!mesh.indices.empty())
{
Expand Down Expand Up @@ -988,6 +999,14 @@ void RemapState::emit_mesh(unsigned remapped_index)
remapped_format = VK_FORMAT_R16G16_UNORM;
format_size = sizeof(u16vec2);
}
else if (all(lessThanEqual(max_uv, vec2(0x8000))) && all(greaterThanEqual(min_uv, vec2(-0x8000))))
{
vector<uint8_t> quantized(attr_count * sizeof(u16vec2));
quantize_attribute_rg32f_rg16f(quantized.data(), unpacked_buffer.data(), attr_count);
unpacked_buffer = move(quantized);
remapped_format = VK_FORMAT_R16G16_SFLOAT;
format_size = sizeof(u16vec2);
}
}

auto buffer_index = emit_buffer(unpacked_buffer, format_size);
Expand Down Expand Up @@ -1924,6 +1943,36 @@ bool export_scene_to_glb(const SceneInformation &scene, const string &path, cons
if (m.material >= 0)
prim.AddMember("material", state.material.to_index[m.material], allocator);

switch (m.topology)
{
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
prim.AddMember("mode", 0, allocator);
break;

case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
prim.AddMember("mode", 1, allocator);
break;

case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
prim.AddMember("mode", 3, allocator);
break;

case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
prim.AddMember("mode", 4, allocator);
break;

case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
prim.AddMember("mode", 5, allocator);
break;

case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
prim.AddMember("mode", 6, allocator);
break;

default:
break;
}

prim.AddMember("attributes", attribs, allocator);
primitives.PushBack(prim, allocator);
}
Expand Down
99 changes: 87 additions & 12 deletions scene_formats/scene_formats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "meshoptimizer.h"

using namespace Util;
using namespace std;
Expand Down Expand Up @@ -105,31 +106,76 @@ static vector<uint32_t> build_canonical_index_buffer(const Mesh &mesh, const vec
return index_buffer;
}

static void rebuild_new_attributes(vector<uint8_t> &positions, unsigned position_stride,
vector<uint8_t> &attributes, unsigned attribute_stride,
const vector<uint8_t> &source_positions, const vector<uint8_t> &source_attributes,
const vector<uint32_t> &unique_attrib_to_source_index)
static void rebuild_new_attributes_remap_src(vector<uint8_t> &positions, unsigned position_stride,
vector<uint8_t> &attributes, unsigned attribute_stride,
const vector<uint8_t> &source_positions, const vector<uint8_t> &source_attributes,
const vector<uint32_t> &unique_attrib_to_source_index)
{
positions.resize(position_stride * unique_attrib_to_source_index.size());
vector<uint8_t> new_positions;
vector<uint8_t> new_attributes;

new_positions.resize(position_stride * unique_attrib_to_source_index.size());
if (attribute_stride)
attributes.resize(attribute_stride * unique_attrib_to_source_index.size());
new_attributes.resize(attribute_stride * unique_attrib_to_source_index.size());

size_t count = unique_attrib_to_source_index.size();
for (size_t i = 0; i < count; i++)
{
memcpy(positions.data() + i * position_stride,
memcpy(new_positions.data() + i * position_stride,
source_positions.data() + unique_attrib_to_source_index[i] * position_stride,
position_stride);

if (attribute_stride)
{
memcpy(attributes.data() + i * attribute_stride,
memcpy(new_attributes.data() + i * attribute_stride,
source_attributes.data() + unique_attrib_to_source_index[i] * attribute_stride,
attribute_stride);
}
}

positions = move(new_positions);
attributes = move(new_attributes);
}

static void rebuild_new_attributes_remap_dst(vector<uint8_t> &positions, unsigned position_stride,
vector<uint8_t> &attributes, unsigned attribute_stride,
const vector<uint8_t> &source_positions, const vector<uint8_t> &source_attributes,
const vector<uint32_t> &unique_attrib_to_dest_index)
{
vector<uint8_t> new_positions;
vector<uint8_t> new_attributes;

new_positions.resize(position_stride * unique_attrib_to_dest_index.size());
if (attribute_stride)
new_attributes.resize(attribute_stride * unique_attrib_to_dest_index.size());

size_t count = unique_attrib_to_dest_index.size();
for (size_t i = 0; i < count; i++)
{
memcpy(new_positions.data() + unique_attrib_to_dest_index[i] * position_stride,
source_positions.data() + i * position_stride,
position_stride);

if (attribute_stride)
{
memcpy(new_attributes.data() + unique_attrib_to_dest_index[i] * attribute_stride,
source_attributes.data() + i * attribute_stride,
attribute_stride);
}
}

positions = move(new_positions);
attributes = move(new_attributes);
}

static vector<uint32_t> remap_indices(const vector<uint32_t> &indices, const vector<uint32_t> &remap_table)
{
vector<uint32_t> remapped;
remapped.reserve(indices.size());
for (auto &i : indices)
remapped.push_back(remap_table[i]);
return remapped;
}

Mesh mesh_optimize_index_buffer(const Mesh &mesh)
{
Expand All @@ -140,11 +186,41 @@ Mesh mesh_optimize_index_buffer(const Mesh &mesh)
optimized.position_stride = mesh.position_stride;
optimized.attribute_stride = mesh.attribute_stride;

// Remove redundant indices and rewrite index and attribute buffers.
auto index_remap = build_index_remap_list(mesh);
auto index_buffer = build_canonical_index_buffer(mesh, index_remap.index_remap);
rebuild_new_attributes(optimized.positions, optimized.position_stride,
optimized.attributes, optimized.attribute_stride,
mesh.positions, mesh.attributes, index_remap.unique_attrib_to_source_index);
rebuild_new_attributes_remap_src(optimized.positions, optimized.position_stride,
optimized.attributes, optimized.attribute_stride,
mesh.positions, mesh.attributes, index_remap.unique_attrib_to_source_index);

size_t vertex_count = optimized.positions.size() / optimized.position_stride;

// Optimize for vertex cache.
meshopt_optimizeVertexCache(index_buffer.data(), index_buffer.data(), index_buffer.size(),
vertex_count);

// Remap vertex fetch to get contiguous indices as much as possible.
vector<uint32_t> remap_table(optimized.positions.size() / optimized.position_stride);
meshopt_optimizeVertexFetchRemap(remap_table.data(), index_buffer.data(), index_buffer.size(), vertex_count);
index_buffer = remap_indices(index_buffer, remap_table);
rebuild_new_attributes_remap_dst(optimized.positions, optimized.position_stride,
optimized.attributes, optimized.attribute_stride,
optimized.positions, optimized.attributes, remap_table);

// Try to stripify the mesh. If we end up with fewer indices, use that.
vector<uint32_t> stripped_index_buffer((index_buffer.size() / 3) * 4);
size_t stripped_index_count = meshopt_stripify(stripped_index_buffer.data(),
index_buffer.data(), index_buffer.size(),
vertex_count);

stripped_index_buffer.resize(stripped_index_count);
if (stripped_index_count < index_buffer.size())
{
optimized.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
index_buffer = move(stripped_index_buffer);
}
else
optimized.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;

uint32_t max_index = 0;
for (auto &i : index_buffer)
Expand Down Expand Up @@ -172,7 +248,6 @@ Mesh mesh_optimize_index_buffer(const Mesh &mesh)
}

optimized.count = unsigned(index_buffer.size());
optimized.topology = mesh.topology;

memcpy(optimized.attribute_layout, mesh.attribute_layout, sizeof(mesh.attribute_layout));
optimized.material_index = mesh.material_index;
Expand Down
1 change: 1 addition & 0 deletions third_party/meshoptimizer
Submodule meshoptimizer added at 435168

0 comments on commit 9580cb3

Please sign in to comment.