From 65cf3887b734f0eb6e76d930dd99e63a5a6f3ead Mon Sep 17 00:00:00 2001 From: ecker Date: Sun, 24 Aug 2025 20:47:32 -0500 Subject: [PATCH] finally nailed a memory leak because past me made assumptions RP3D cleared the triangle parts on body destruction per the documentation (although I still crash after a while on the DC build from fragmentation at 9MiB consumed......), crammed filetype preference to resolveURI since everything should be using that anyways, some other cruft I forget trying to nail down this memory leak --- bin/data/config.json | 6 +-- bin/dreamcast/data/config.json | 2 +- bin/exe/default/cc | 2 +- engine/inc/uf/ext/opengl/commands.h | 5 ++ engine/src/engine/asset/asset.cpp | 1 + engine/src/engine/graph/decode.cpp | 30 +++++------ engine/src/engine/graph/graph.cpp | 16 +++--- engine/src/ext/opengl/commands.cpp | 57 +++++++++++++++++--- engine/src/ext/reactphysics/reactphysics.cpp | 42 +++++++++++---- makefiles/default/arch | 2 +- 10 files changed, 119 insertions(+), 44 deletions(-) diff --git a/bin/data/config.json b/bin/data/config.json index efab3978..1e000147 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -341,7 +341,7 @@ } }, "memory pool": { - "enabled": true, // needs to be kept on + "enabled": true, // needs to be kept on for GC "subPools": true, "alignment": 64, "override": false, @@ -355,11 +355,11 @@ "render modes": { "gui": true, "deferred": true }, "limiters": { "deltaTime": 5, - "framerate": 0 // "auto" + "framerate": 0 // "auto" // for some reason drops to 60 }, "threads": { "workers" : "auto", - "frame limiter": 0 // "auto" + "frame limiter": "auto" }, "debug": { "framerate": { diff --git a/bin/dreamcast/data/config.json b/bin/dreamcast/data/config.json index ef9d69c2..26c16fd3 100644 --- a/bin/dreamcast/data/config.json +++ b/bin/dreamcast/data/config.json @@ -83,7 +83,7 @@ } }, "audio": { - "mute": false, + "mute": true, "async update": false, "streams by default": true, "buffers": { diff --git a/bin/exe/default/cc b/bin/exe/default/cc index b08d5af5..060d289b 100644 --- a/bin/exe/default/cc +++ b/bin/exe/default/cc @@ -1 +1 @@ -gcc \ No newline at end of file +clang \ No newline at end of file diff --git a/engine/inc/uf/ext/opengl/commands.h b/engine/inc/uf/ext/opengl/commands.h index 97bee2ff..25400038 100644 --- a/engine/inc/uf/ext/opengl/commands.h +++ b/engine/inc/uf/ext/opengl/commands.h @@ -13,6 +13,7 @@ #include #include +#define UF_COMMAND_BUFFER_USERDATA 1 #define UF_COMMAND_BUFFER_POINTERED_USERDATA 1 namespace ext { @@ -79,11 +80,15 @@ namespace ext { } color; }; + #if UF_COMMAND_BUFFER_USERDATA #if UF_COMMAND_BUFFER_POINTERED_USERDATA typedef uf::PointeredUserdata userdata_t; #else typedef uf::Userdata userdata_t; #endif + #else + typedef Info* userdata_t; + #endif typedef std::function function_t; static size_t preallocate; diff --git a/engine/src/engine/asset/asset.cpp b/engine/src/engine/asset/asset.cpp index d119c214..fd737a3f 100644 --- a/engine/src/engine/asset/asset.cpp +++ b/engine/src/engine/asset/asset.cpp @@ -100,6 +100,7 @@ uf::asset::Payload uf::asset::resolveToPayload( const uf::stl::string& uri, cons { "jpg", uf::asset::Type::IMAGE }, { "jpeg", uf::asset::Type::IMAGE }, { "png", uf::asset::Type::IMAGE }, + { "dtex", uf::asset::Type::IMAGE }, { "ogg", uf::asset::Type::AUDIO }, { "wav", uf::asset::Type::AUDIO }, diff --git a/engine/src/engine/graph/decode.cpp b/engine/src/engine/graph/decode.cpp index 6cf422de..064afd40 100644 --- a/engine/src/engine/graph/decode.cpp +++ b/engine/src/engine/graph/decode.cpp @@ -18,7 +18,7 @@ #define UF_GRAPH_EXTENDED 1 -#if 0 && UF_ENV_DREAMCAST +#if UF_ENV_DREAMCAST #define UF_DEBUG_TIMER_MULTITRACE_START(...) UF_TIMER_MULTITRACE_START(__VA_ARGS__) #define UF_DEBUG_TIMER_MULTITRACE(...) UF_TIMER_MULTITRACE(__VA_ARGS__) #define UF_DEBUG_TIMER_MULTITRACE_END(...) UF_TIMER_MULTITRACE_END(__VA_ARGS__) @@ -248,7 +248,7 @@ namespace { #if !UF_GRAPH_EXTENDED mesh.buffers.emplace_back(uf::io::readAsBuffer( directory + "/" + filename )); #else - if ( graph.metadata["stream"]["enabled"].as() ) { + if ( graph.settings.stream.enabled ) { mesh.buffers.emplace_back(); mesh.buffer_paths.emplace_back(directory + "/" + filename); } else { @@ -427,7 +427,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const ext::json::forEach( serializer["primitives"], [&]( ext::json::Value& value ){ auto name = key + value["name"].as(); // UF_MSG_DEBUG("{}", name); - /*graph.storage*/storage.primitives[name] = decodePrimitives( value, graph ); + storage.primitives[name] = decodePrimitives( value, graph ); graph.primitives.emplace_back(name); }); UF_DEBUG_TIMER_MULTITRACE("Read primitives."); @@ -442,7 +442,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const ext::json::forEach( serializer["meshes"], [&]( ext::json::Value& value ){ auto name = key + value["name"].as(); // UF_MSG_DEBUG("{}", name); - /*graph.storage*/storage.meshes[name] = decodeMesh( value, graph ); + storage.meshes[name] = decodeMesh( value, graph ); graph.meshes.emplace_back(name); }); UF_DEBUG_TIMER_MULTITRACE("Read meshes"); @@ -457,7 +457,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const ext::json::forEach( serializer["images"], [&]( ext::json::Value& value ){ auto name = key + value["name"].as(); // UF_MSG_DEBUG("{}", name); - /*graph.storage*/storage.images[name] = decodeImage( value, graph ); + storage.images[name] = decodeImage( value, graph ); graph.images.emplace_back(name); }); UF_DEBUG_TIMER_MULTITRACE("Read images"); @@ -486,7 +486,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const ext::json::forEach( serializer["textures"], [&]( ext::json::Value& value ){ auto name = key + value["name"].as(); // UF_MSG_DEBUG("{}", name); - /*graph.storage*/storage.textures[name] = decodeTexture( value, graph ); + storage.textures[name] = decodeTexture( value, graph ); graph.textures.emplace_back(name); }); UF_DEBUG_TIMER_MULTITRACE("Read texture information"); @@ -501,7 +501,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const ext::json::forEach( serializer["samplers"], [&]( ext::json::Value& value ){ auto name = key + value["name"].as(); // UF_MSG_DEBUG("{}", name); - /*graph.storage*/storage.samplers[name] = decodeSampler( value, graph ); + storage.samplers[name] = decodeSampler( value, graph ); graph.samplers.emplace_back(name); }); UF_DEBUG_TIMER_MULTITRACE("Read sampler information"); @@ -516,7 +516,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const ext::json::forEach( serializer["materials"], [&]( ext::json::Value& value ){ auto name = key + value["name"].as(); // UF_MSG_DEBUG("{}", name); - /*graph.storage*/storage.materials[name] = decodeMaterial( value, graph ); + storage.materials[name] = decodeMaterial( value, graph ); graph.materials.emplace_back(name); }); UF_DEBUG_TIMER_MULTITRACE("Read material information"); @@ -542,7 +542,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const tasks.queue([&]{ // load animation information UF_DEBUG_TIMER_MULTITRACE("Reading animation information..."); - /*graph.storage*/storage.animations.map.reserve( serializer["animations"].size() ); + storage.animations.map.reserve( serializer["animations"].size() ); ext::json::forEach( serializer["animations"], [&]( ext::json::Value& value ){ if ( value.is() ) { auto path = directory + "/" + value.as(); @@ -550,9 +550,9 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const json.readFromFile( path ); auto name = key + json["name"].as(); if ( graph.settings.stream.animations ) { - /*graph.storage*/storage.animations[name].path = path; + storage.animations[name].path = path; } else { - /*graph.storage*/storage.animations[name] = decodeAnimation( json, graph ); + storage.animations[name] = decodeAnimation( json, graph ); } graph.animations.emplace_back(name); } else { @@ -564,14 +564,14 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const auto name = key + json["name"].as(); if ( graph.settings.stream.animations ) { - /*graph.storage*/storage.animations[name].path = path; + storage.animations[name].path = path; } else { - /*graph.storage*/storage.animations[name] = decodeAnimation( json, graph ); + storage.animations[name] = decodeAnimation( json, graph ); } graph.animations.emplace_back(name); } else { auto name = key + value["name"].as(); - /*graph.storage*/storage.animations[name] = decodeAnimation( value, graph ); + storage.animations[name] = decodeAnimation( value, graph ); graph.animations.emplace_back(name); } } @@ -587,7 +587,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const ext::json::forEach( serializer["skins"], [&]( ext::json::Value& value ){ auto name = key + value["name"].as(); // UF_MSG_DEBUG("{}", name); - /*graph.storage*/storage.skins[name] = decodeSkin( value, graph ); + storage.skins[name] = decodeSkin( value, graph ); graph.skins.emplace_back(name); }); #if UF_ENV_DREAMCAST diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index 38596fd6..d56ad14f 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -26,7 +26,7 @@ #if UF_USE_OPENGL #define UF_GRAPH_SPARSE_READ_MESH 1 #else - #define UF_GRAPH_SPARSE_READ_MESH 0 + #define UF_GRAPH_SPARSE_READ_MESH 1 #endif #define UF_GRAPH_EXTENDED 1 @@ -1663,12 +1663,15 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { // bail if no update is detected auto drawCommandHash = ::fnv1aHash(queuedDrawIDs); - if ( drawCommandHash == graph.settings.stream.hash ) { - return; - } - graph.settings.stream.hash = drawCommandHash; graph.settings.stream.lastUpdate = uf::physics::time::current; + if ( drawCommandHash == graph.settings.stream.hash ) { + // return; + } + graph.settings.stream.hash = drawCommandHash; + + // read from disk + #if UF_GRAPH_SPARSE_READ_MESH // needs to be dequantized first, naively copying the descriptor settings just doesn't work { #if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT @@ -1690,8 +1693,6 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { #endif } - // read from disk - #if UF_GRAPH_SPARSE_READ_MESH // reset counts mesh.vertex.count = 0; mesh.index.count = 0; @@ -1867,6 +1868,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { LOAD_MESH_DATA( vertex ); } + // in the event streamed in mesh data from any pathway isn't already converted { #if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT mesh.convert(); diff --git a/engine/src/ext/opengl/commands.cpp b/engine/src/ext/opengl/commands.cpp index 2176782a..66b5b432 100644 --- a/engine/src/ext/opengl/commands.cpp +++ b/engine/src/ext/opengl/commands.cpp @@ -10,9 +10,6 @@ #include #include -#define VERBOSE false -#define VERBOSE_SUBMIT false - namespace { size_t culled = 0; } @@ -41,13 +38,17 @@ void ext::opengl::CommandBuffer::end() { } void ext::opengl::CommandBuffer::record( const CommandBuffer::Info& header ) { if ( state != 1 ) return; - switch ( header.type ) { case ext::opengl::enums::Command::CLEAR: { InfoClear* info = (InfoClear*) &header; + #if UF_COMMAND_BUFFER_USERDATA auto& userdata = infos.emplace_back(); userdata.create( *info ); info = &userdata.get(); + #else + info = new InfoClear(*info); + infos.emplace_back(info); + #endif info->type = enums::Command::CLEAR; info->next = NULL; } break; @@ -55,25 +56,40 @@ void ext::opengl::CommandBuffer::record( const CommandBuffer::Info& header ) { InfoViewport* info = (InfoViewport*) &header; if ( info->size.x == 0 ) info->size.x = ext::opengl::settings::width; if ( info->size.y == 0 ) info->size.y = ext::opengl::settings::height; + #if UF_COMMAND_BUFFER_USERDATA auto& userdata = infos.emplace_back(); userdata.create( *info ); - info->next = NULL; info = &userdata.get(); + #else + info = new InfoViewport(*info); + infos.emplace_back(info); + #endif info->type = enums::Command::VIEWPORT; + info->next = NULL; } break; case ext::opengl::enums::Command::VARIANT: { InfoVariant* info = (InfoVariant*) &header; + #if UF_COMMAND_BUFFER_USERDATA auto& userdata = infos.emplace_back(); userdata.create( *info ); info = &userdata.get(); + #else + info = new InfoVariant(*info); + infos.emplace_back(info); + #endif info->type = enums::Command::VARIANT; info->next = NULL; } break; case ext::opengl::enums::Command::DRAW: { InfoDraw* info = (InfoDraw*) &header; + #if UF_COMMAND_BUFFER_USERDATA auto& userdata = infos.emplace_back(); userdata.create( *info ); info = &userdata.get(); + #else + info = new InfoDraw(*info); + infos.emplace_back(info); + #endif info->type = enums::Command::DRAW; info->next = NULL; } break; @@ -95,7 +111,11 @@ void ext::opengl::CommandBuffer::submit() { mutex->lock(); //UF_TIMER_MULTITRACE_START("Starting command buffer submission: " << this); for ( auto& info : infos ) { + #if UF_COMMAND_BUFFER_USERDATA CommandBuffer::Info* header = (CommandBuffer::Info*) (void*) info; + #else + CommandBuffer::Info* header = (CommandBuffer::Info*) info; + #endif switch ( header->type ) { case ext::opengl::enums::Command::CLEAR: { InfoClear* info = (InfoClear*) header; @@ -128,6 +148,31 @@ void ext::opengl::CommandBuffer::submit() { } void ext::opengl::CommandBuffer::flush() { mutex->lock(); + #if !UF_COMMAND_BUFFER_USERDATA + for ( auto& info : infos ) { + CommandBuffer::Info* header = (CommandBuffer::Info*) info; + switch ( header->type ) { + case ext::opengl::enums::Command::CLEAR: { + InfoClear* info = (InfoClear*) header; + delete info; + } break; + case ext::opengl::enums::Command::VIEWPORT: { + InfoViewport* info = (InfoViewport*) header; + delete info; + } break; + case ext::opengl::enums::Command::VARIANT: { + InfoVariant* info = (InfoVariant*) header; + delete info; + } break; + case ext::opengl::enums::Command::DRAW: { + InfoDraw* info = (InfoDraw*) header; + delete info; + } break; + default: { + } break; + } + } + #endif infos.clear(); state = 0; mutex->unlock(); @@ -451,7 +496,7 @@ void ext::opengl::CommandBuffer::drawIndexed( const ext::opengl::CommandBuffer:: if ( drawInfo.descriptor.inputs.index.count ) { GL_ERROR_CHECK(glDrawElements(GL_TRIANGLES, drawInfo.descriptor.inputs.index.count, indicesType, (static_cast(drawInfo.attributes.index.pointer) + drawInfo.attributes.index.stride * drawInfo.descriptor.inputs.index.first))); } else { - #if UF_ENV_DREAMCAST + #if 0 && UF_ENV_DREAMCAST // GLdc has a "regression" where glDrawArrays does not work // everything should be using indices anyways so this path shouldn't really ever be taken uf::stl::vector indices(drawInfo.descriptor.inputs.vertex.count); diff --git a/engine/src/ext/reactphysics/reactphysics.cpp b/engine/src/ext/reactphysics/reactphysics.cpp index 0933d4b8..f290e9ab 100644 --- a/engine/src/ext/reactphysics/reactphysics.cpp +++ b/engine/src/ext/reactphysics/reactphysics.cpp @@ -15,8 +15,12 @@ namespace { rp3d::PhysicsCommon common; rp3d::PhysicsWorld* world; - reactphysics3d::TriangleMesh* createTriangleMesh( const uf::Mesh& mesh ) { + // i was wrong to assume that RP3D handles deleting these per the documentation + uf::stl::unordered_map> triangleParts; + + reactphysics3d::TriangleMesh* createTriangleMesh( const uf::Mesh& mesh, const uf::Object& object ) { auto* rMesh = ::common.createTriangleMesh(); + auto& parts = ::triangleParts[object.getUid()]; uf::Mesh::Input vertexInput = mesh.vertex; uf::Mesh::Input indexInput = mesh.index; @@ -73,7 +77,7 @@ namespace { if ( vertexInput.count == 0 || indexInput.count == 0 ) continue; if ( normalAttribute.descriptor.name == "normal" ) { - rMesh->addSubpart(new rp3d::TriangleVertexArray( + auto* part = new rp3d::TriangleVertexArray( vertexInput.count, (const uint8_t*) (vertexAttribute.pointer) + vertexAttribute.stride * vertexInput.first, vertexAttribute.stride, @@ -88,9 +92,11 @@ namespace { vertexType, normalType, indexType - )); + ); + parts.emplace_back(part); + rMesh->addSubpart(part); } else { - rMesh->addSubpart(new rp3d::TriangleVertexArray( + auto* part = new rp3d::TriangleVertexArray( vertexInput.count, (const uint8_t*) (vertexAttribute.pointer) + vertexAttribute.stride * vertexInput.first, vertexAttribute.stride, @@ -101,12 +107,14 @@ namespace { vertexType, indexType - )); + ); + parts.emplace_back(part); + rMesh->addSubpart(part); } } } else if ( vertexInput.count > 0 && indexInput.count > 0 ) { if ( normalAttribute.descriptor.name == "normal" ) { - rMesh->addSubpart(new rp3d::TriangleVertexArray( + auto* part = new rp3d::TriangleVertexArray( vertexInput.count, (const uint8_t*) (vertexAttribute.pointer) + vertexAttribute.stride * vertexInput.first, vertexAttribute.stride, @@ -121,9 +129,11 @@ namespace { vertexType, normalType, indexType - )); + ); + parts.emplace_back(part); + rMesh->addSubpart(part); } else { - rMesh->addSubpart(new rp3d::TriangleVertexArray( + auto* part = new rp3d::TriangleVertexArray( vertexInput.count, (const uint8_t*) (vertexAttribute.pointer) + vertexAttribute.stride * vertexInput.first, vertexAttribute.stride, @@ -134,7 +144,9 @@ namespace { vertexType, indexType - )); + ); + parts.emplace_back(part); + rMesh->addSubpart(part); } } @@ -420,6 +432,15 @@ pod::PhysicsState& ext::reactphysics::create( uf::Object& object ) { void ext::reactphysics::destroy( uf::Object& object ) { auto& state = object.getComponent(); ext::reactphysics::destroy( state ); + + auto uid = object.getUid(); + if ( ::triangleParts.count( uid ) > 0 ) { + auto& parts = ::triangleParts[uid]; + for ( auto* part : parts ) { + delete part; + } + ::triangleParts.erase( uid ); + } } void ext::reactphysics::destroy( pod::PhysicsState& state ) { ext::reactphysics::detach( state ); @@ -468,6 +489,7 @@ void ext::reactphysics::detach( pod::PhysicsState& state ) { if ( !state.body || !state.world ) return; // auto& scene = uf::scene::getCurrentScene(); // auto& world = ext::reactphysics::globalStorage ? ::world : scene.getComponent(); + state.world->destroyRigidBody(state.body); state.body = NULL; @@ -478,7 +500,7 @@ void ext::reactphysics::detach( pod::PhysicsState& state ) { pod::PhysicsState& ext::reactphysics::create( uf::Object& object, const uf::Mesh& mesh, bool dynamic ) { UF_ASSERT( mesh.index.count ); - auto* rMesh = ::createTriangleMesh( mesh ); + auto* rMesh = ::createTriangleMesh( mesh, object ); auto& state = ext::reactphysics::create( object ); state.shape = ::common.createConcaveMeshShape( rMesh ); diff --git a/makefiles/default/arch b/makefiles/default/arch index a08e1f35..6ec5a6aa 100644 --- a/makefiles/default/arch +++ b/makefiles/default/arch @@ -1 +1 @@ -linux +win64 \ No newline at end of file