Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mesh optimization #3459

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,6 @@
[submodule "data/terrain_overrides/high"]
path = data/terrain_overrides/high
url = https://github.com/Warzone2100/data-terrain-high.git
[submodule "3rdparty/meshoptimizer"]
path = 3rdparty/meshoptimizer
url = https://github.com/zeux/meshoptimizer.git
7 changes: 7 additions & 0 deletions 3rdparty/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,10 @@ if (WZ_PROFILING_NVTX)
find_package(CUDAToolkit REQUIRED VERSION 5.0)
set(PROFILING_NVTX_INCLUDE ${CUDAToolkit_INCLUDE_DIRS} PARENT_SCOPE)
endif ()


# meshoptimizer
add_subdirectory(meshoptimizer EXCLUDE_FROM_ALL)
set_property(TARGET meshoptimizer PROPERTY FOLDER "3rdparty")
set_property(TARGET meshoptimizer PROPERTY XCODE_ATTRIBUTE_CLANG_WARN_COMMA NO) # -Wcomma
set_property(TARGET meshoptimizer PROPERTY XCODE_ATTRIBUTE_WARNING_CFLAGS "-Wno-cast-align")
1 change: 1 addition & 0 deletions 3rdparty/meshoptimizer
Submodule meshoptimizer added at c21d3b
2 changes: 2 additions & 0 deletions COPYING.NONGPL
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ data/base/texpages/page-25-sky-urban.png
- MIT, Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>
3rdparty/LRUCache11/*
- ISC License, Copyright (c) 2012-22 SAURAV MOHAPATRA (https://github.com/mohaps/lrucache11)
3rdparty/meshoptimizer/*
- MIT, Copyright (c) 2016-2023 Arseny Kapoulkine (https://github.com/zeux/meshoptimizer/)
3rdparty/micro-ecc/*
- BSD, Copyright (c) 2014, Kenneth MacKay (https://github.com/kmackay/micro-ecc)
3rdparty/sha/*
Expand Down
2 changes: 1 addition & 1 deletion lib/ivis_opengl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ include(WZTargetConfiguration)
WZ_TARGET_CONFIGURATION(ivis-opengl)

target_include_directories(ivis-opengl PRIVATE ${HARFBUZZ_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIR_ft2build})
target_link_libraries(ivis-opengl PRIVATE framework launchinfo PNG::PNG ${HARFBUZZ_LIBRARIES} ${FREETYPE_LIBRARIES})
target_link_libraries(ivis-opengl PRIVATE framework launchinfo PNG::PNG ${HARFBUZZ_LIBRARIES} ${FREETYPE_LIBRARIES} meshoptimizer)
target_link_libraries(ivis-opengl PUBLIC glad)
target_link_libraries(ivis-opengl PUBLIC optional-lite)
if (WZ_DEBUG_GFX_API_LEAKS)
Expand Down
121 changes: 114 additions & 7 deletions lib/ivis_opengl/imdload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <string>
#include <unordered_map>
#include <array>
#include <algorithm>

#include "lib/framework/frame.h"
#include "lib/framework/string_ext.h"
Expand All @@ -45,6 +46,8 @@
#include <glm/vec4.hpp>
using Vector4f = glm::vec4;

#include <meshoptimizer/src/meshoptimizer.h>

// Scale animation numbers from int to float
#define INT_SCALE 1000

Expand Down Expand Up @@ -1254,6 +1257,99 @@ void finishTangentsGeneration()
}
}

// NOTE: Only intended to be called from _imd_load_level after all buffers are ready
static inline void _imd_load_level_optimize(iIMDShape &s)
{
std::vector<meshopt_Stream> streams = {
{&vertices[0], sizeof(gfx_api::gfxFloat) * 3, sizeof(gfx_api::gfxFloat) * 3},
{&normals[0], sizeof(gfx_api::gfxFloat) * 3, sizeof(gfx_api::gfxFloat) * 3},
{&texcoords[0], sizeof(gfx_api::gfxFloat) * 4, sizeof(gfx_api::gfxFloat) * 4}
};
if (!tangents.empty())
{
streams.push_back({&tangents[0], sizeof(gfx_api::gfxFloat) * 4, sizeof(gfx_api::gfxFloat) * 4});
}

const size_t index_count = indices.size();

// convert to std::vector<unsigned int>, as expected by meshopt
static std::vector<unsigned int> indices_uint; // Static, to save allocations.
indices_uint.resize(0);
std::transform(indices.begin(), indices.end(), std::back_inserter(indices_uint),
[](uint16_t c) -> unsigned int { return static_cast<unsigned int>(c); });

// Indexing
size_t initial_vertex_count = vertexCount;
std::vector<unsigned int> remap(index_count);
size_t vertex_count = meshopt_generateVertexRemapMulti(&remap[0], &indices_uint[0], index_count, initial_vertex_count, &streams[0], streams.size());

if (vertex_count != initial_vertex_count)
{
debug(LOG_3D, "imd[_load_level_optimize] = Reduced vertices: %zu -> %zu", initial_vertex_count, vertex_count);
}

{
std::vector<unsigned int> indexArray;
indexArray.resize(index_count);
meshopt_remapIndexBuffer(&indexArray[0], &indices_uint[0], index_count, &remap[0]);
indices_uint.swap(indexArray);
}

{
std::vector<gfx_api::gfxFloat> vertexArray;
vertexArray.resize(vertex_count * 3);
meshopt_remapVertexBuffer(&vertexArray[0], &vertices[0], initial_vertex_count, sizeof(gfx_api::gfxFloat) * 3, &remap[0]);
vertices.swap(vertexArray);
}

{
std::vector<gfx_api::gfxFloat> normalArray;
normalArray.resize(vertex_count * 3);
meshopt_remapVertexBuffer(&normalArray[0], &normals[0], initial_vertex_count, sizeof(gfx_api::gfxFloat) * 3, &remap[0]);
normals.swap(normalArray);
}

{
std::vector<gfx_api::gfxFloat> textureArray;
textureArray.resize(vertex_count * 4);
meshopt_remapVertexBuffer(&textureArray[0], &texcoords[0], initial_vertex_count, sizeof(gfx_api::gfxFloat) * 4, &remap[0]);
texcoords.swap(textureArray);
}

if (!tangents.empty())
{
std::vector<gfx_api::gfxFloat> tangentArray;
tangentArray.resize(vertex_count * 4);
meshopt_remapVertexBuffer(&tangentArray[0], &tangents[0], initial_vertex_count, sizeof(gfx_api::gfxFloat) * 4, &remap[0]);
tangents.swap(tangentArray);
}

// Vertex cache optimization
meshopt_optimizeVertexCache(&indices_uint[0], &indices_uint[0], index_count, vertex_count);

// Overdraw optimization
meshopt_optimizeOverdraw(&indices_uint[0], &indices_uint[0], index_count, &vertices[0], vertex_count, sizeof(gfx_api::gfxFloat) * 3, 1.05f);

// Vertex fetch optimization
meshopt_optimizeVertexFetchRemap(&remap[0], &indices_uint[0], index_count, vertex_count);
meshopt_remapIndexBuffer(&indices_uint[0], &indices_uint[0], index_count, &remap[0]);
meshopt_remapVertexBuffer(&vertices[0], &vertices[0], vertex_count, sizeof(gfx_api::gfxFloat) * 3, &remap[0]);
meshopt_remapVertexBuffer(&normals[0], &normals[0], vertex_count, sizeof(gfx_api::gfxFloat) * 3, &remap[0]);
meshopt_remapVertexBuffer(&texcoords[0], &texcoords[0], vertex_count, sizeof(gfx_api::gfxFloat) * 4, &remap[0]);
if (!tangents.empty())
{
meshopt_remapVertexBuffer(&tangents[0], &tangents[0], vertex_count, sizeof(gfx_api::gfxFloat) * 4, &remap[0]);
}

// update s.vertexCount
s.vertexCount = vertex_count;

// transform indices back
indices.clear();
std::transform(indices_uint.begin(), indices_uint.end(), std::back_inserter(indices),
[](unsigned int c) -> uint16_t { return static_cast<uint16_t>(c); });
}

/*!
* Load shape levels recursively
* \param ppFileData Pointer to the data (usually read from a file)
Expand Down Expand Up @@ -1543,6 +1639,8 @@ static std::unique_ptr<iIMDShape> _imd_load_level(const WzString &filename, cons
indices.emplace_back(addVertex(s, 2, &p, npol, pie_level_normals));
}

ASSERT(indices.size() == s.polys.size() * 3, "???");
s.indicesCount = indices.size();
s.vertexCount = vertexCount;

// Tangents are optional, only if normals were loaded and passed sanity check above
Expand All @@ -1554,15 +1652,17 @@ static std::unique_ptr<iIMDShape> _imd_load_level(const WzString &filename, cons
for (size_t i = 0; i < indices.size(); i += 3)
calculateTangentsForTriangle(indices[i], indices[i+1], indices[i+2]);
finishTangentsGeneration();

if (!tangents.empty())
{
if (!s.buffers[VBO_TANGENT])
s.buffers[VBO_TANGENT] = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer, gfx_api::context::buffer_storage_hint::static_draw, "tangent buffer");
s.buffers[VBO_TANGENT]->upload(tangents.size() * sizeof(gfx_api::gfxFloat), tangents.data());
}
}
else
{
tangents.resize(0);
bitangents.resize(0);
}

_imd_load_level_optimize(s);
s.vertexCount = vertexCount;
s.indicesCount = indices.size();

if (!s.buffers[VBO_VERTEX])
s.buffers[VBO_VERTEX] = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer, gfx_api::context::buffer_storage_hint::static_draw, "vertex buffer");
if (vertices.empty())
Expand Down Expand Up @@ -1594,6 +1694,13 @@ static std::unique_ptr<iIMDShape> _imd_load_level(const WzString &filename, cons
debug(LOG_ERROR, "_imd_load_level: file corrupt? - no texcoords?: %s (key: %s)", filename.toUtf8().c_str(), key.c_str());
}
s.buffers[VBO_TEXCOORD]->upload(texcoords.size() * sizeof(gfx_api::gfxFloat), texcoords.data());

if (!tangents.empty())
{
if (!s.buffers[VBO_TANGENT])
s.buffers[VBO_TANGENT] = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer, gfx_api::context::buffer_storage_hint::static_draw, "tangent buffer");
s.buffers[VBO_TANGENT]->upload(tangents.size() * sizeof(gfx_api::gfxFloat), tangents.data());
}
}

indices.resize(0);
Expand Down
3 changes: 2 additions & 1 deletion lib/ivis_opengl/ivisdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ struct iIMDShape

// The new rendering data
gfx_api::buffer* buffers[VBO_COUNT] = { nullptr };
uint16_t vertexCount = 0;
size_t vertexCount = 0;
size_t indicesCount = 0; // the number of polys * 3

// object animation (animating a level, rather than its texture)
std::vector<ANIMFRAME> objanimdata;
Expand Down
22 changes: 11 additions & 11 deletions lib/ivis_opengl/piedraw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,8 @@ void pie_Draw3DButton(const iIMDShape *shape, PIELIGHT teamcolour, const glm::ma
gfx_api::Draw3DShapeOpaque::get().bind_textures(&pie_Texture(textures.texpage), tcmask, normalmap, specularmap);
gfx_api::Draw3DShapeOpaque::get().bind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD], pTangentBuffer);
gfx_api::context::get().bind_index_buffer(*shape->buffers[VBO_INDEX], gfx_api::index_type::u16);
gfx_api::Draw3DShapeOpaque::get().draw_elements(shape->polys.size() * 3, 0);
polyCount += shape->polys.size();
gfx_api::Draw3DShapeOpaque::get().draw_elements(shape->indicesCount, 0);
polyCount += shape->indicesCount / 3;
gfx_api::Draw3DShapeOpaque::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD], pTangentBuffer);
gfx_api::context::get().unbind_index_buffer(*shape->buffers[VBO_INDEX]);
}
Expand Down Expand Up @@ -430,7 +430,7 @@ static void draw3dShapeTemplated(const templatedState &lastState, ShaderOnce& gl
AdditivePSO::get().bind_textures(&pie_Texture(textures.texpage), tcmask, normalmap, specularmap);
}
AdditivePSO::get().set_uniforms_at(2, instanceUniforms);
AdditivePSO::get().draw_elements(shape->polys.size() * 3, 0);
AdditivePSO::get().draw_elements(shape->indicesCount, 0);
// AdditivePSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
}
else if (pieFlag & pie_TRANSLUCENT)
Expand All @@ -448,7 +448,7 @@ static void draw3dShapeTemplated(const templatedState &lastState, ShaderOnce& gl
AlphaPSO::get().bind_textures(&pie_Texture(textures.texpage), tcmask, normalmap, specularmap);
}
AlphaPSO::get().set_uniforms_at(2, instanceUniforms);
AlphaPSO::get().draw_elements(shape->polys.size() * 3, 0);
AlphaPSO::get().draw_elements(shape->indicesCount, 0);
// AlphaPSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
}
else
Expand All @@ -464,7 +464,7 @@ static void draw3dShapeTemplated(const templatedState &lastState, ShaderOnce& gl
AlphaNoDepthWRTPSO::get().bind_textures(&pie_Texture(textures.texpage), tcmask, normalmap, specularmap);
}
AlphaNoDepthWRTPSO::get().set_uniforms_at(2, instanceUniforms);
AlphaNoDepthWRTPSO::get().draw_elements(shape->polys.size() * 3, 0);
AlphaNoDepthWRTPSO::get().draw_elements(shape->indicesCount, 0);
// AlphaPSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
}
}
Expand All @@ -481,7 +481,7 @@ static void draw3dShapeTemplated(const templatedState &lastState, ShaderOnce& gl
PremultipliedPSO::get().bind_textures(&pie_Texture(textures.texpage), tcmask, normalmap, specularmap);
}
PremultipliedPSO::get().set_uniforms_at(2, instanceUniforms);
PremultipliedPSO::get().draw_elements(shape->polys.size() * 3, 0);
PremultipliedPSO::get().draw_elements(shape->indicesCount, 0);
// PremultipliedPSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
}
else
Expand All @@ -497,7 +497,7 @@ static void draw3dShapeTemplated(const templatedState &lastState, ShaderOnce& gl
OpaquePSO::get().bind_textures(&pie_Texture(textures.texpage), tcmask, normalmap, specularmap);
}
OpaquePSO::get().set_uniforms_at(2, instanceUniforms);
OpaquePSO::get().draw_elements(shape->polys.size() * 3, 0);
OpaquePSO::get().draw_elements(shape->indicesCount, 0);
// OpaquePSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
}
}
Expand Down Expand Up @@ -579,7 +579,7 @@ static templatedState pie_Draw3DShape2(const templatedState &lastState, ShaderOn
draw3dShapeTemplated<SHADER_NOLIGHT, gfx_api::Draw3DShapeNoLightAdditive, gfx_api::Draw3DShapeNoLightAlpha, gfx_api::Draw3DShapeNoLightAlphaNoDepthWRT, gfx_api::Draw3DShapeNoLightPremul, gfx_api::Draw3DShapeNoLightOpaque>(lastState, globalsOnce, globalUniforms, colour, teamcolour, stretchDepth, ecmState, globalUniforms.ViewMatrix * modelMatrix, shape, pieFlag, frame);
}

polyCount += shape->polys.size();
polyCount += (shape->indicesCount / 3);

return currentState;
}
Expand Down Expand Up @@ -1412,7 +1412,7 @@ static void drawInstanced3dShapeTemplated_Inner(ShaderOnce& globalsOnce, const g
std::make_tuple(instanceDataBuffer, instanceBufferOffset) });
Draw3DInstancedPSO::get().bind_textures(&pie_Texture(textures.texpage), tcmask, normalmap, specularmap, gfx_api::context::get().getDepthTexture(), lightmapTexture);

Draw3DInstancedPSO::get().draw_elements_instanced(shape->polys.size() * 3, 0, instance_count);
Draw3DInstancedPSO::get().draw_elements_instanced(shape->indicesCount, 0, instance_count);
// Draw3DInstancedPSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
}

Expand All @@ -1439,7 +1439,7 @@ static void drawInstanced3dShapeDepthOnly(ShaderOnce& globalsOnce, const gfx_api
std::make_tuple(instanceDataBuffer, instanceBufferOffset) });
// gfx_api::Draw3DShapeDepthOnly_Instanced::get().bind_textures(&pie_Texture(shape->texpage), tcmask, normalmap, specularmap);

gfx_api::Draw3DShapeDepthOnly_Instanced::get().draw_elements_instanced(shape->polys.size() * 3, 0, instance_count);
gfx_api::Draw3DShapeDepthOnly_Instanced::get().draw_elements_instanced(shape->indicesCount, 0, instance_count);
// Draw3DInstancedPSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
}

Expand Down Expand Up @@ -1534,7 +1534,7 @@ static void pie_Draw3DShape2_Instanced(ShaderOnce& globalsOnce, const gfx_api::D
drawInstanced3dShapeTemplated<SHADER_NOLIGHT_INSTANCED, gfx_api::Draw3DShapeNoLightAdditive_Instanced, gfx_api::Draw3DShapeNoLightAdditiveNoDepthWRT_Instanced, gfx_api::Draw3DShapeNoLightAlpha_Instanced, gfx_api::Draw3DShapeNoLightAlphaNoDepthWRT_Instanced, gfx_api::Draw3DShapeNoLightPremul_Instanced, gfx_api::Draw3DShapeNoLightOpaque_Instanced>(globalsOnce, globalUniforms, shape, pieFlag, instanceDataBuffer, instanceBufferOffset, instance_count, lightmapTexture);
}

polyCount += shape->polys.size();
polyCount += shape->indicesCount / 3;
}

void InstancedMeshRenderer::Draw3DShapes_Instanced(uint64_t currentGameFrame, ShaderOnce& globalsOnce, const gfx_api::Draw3DShapeInstancedGlobalUniforms& globalUniforms, int drawParts, bool depthPass)
Expand Down
4 changes: 4 additions & 0 deletions pkg/copyright
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ Files: 3rdparty/LRUCache11/*
Copyright: 2012-2022 SAURAV MOHAPATRA
License: ISC

Files: 3rdparty/meshoptimizer/*
Copyright: 2016-2023 Arseny Kapoulkine
License: Expat

Files: 3rdparty/mINI/*
Copyright: 2018 Danijel Durakovic
License: Expat
Expand Down
Loading