mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-04 23:35:43 +01:00
perf(debug): memory of the process and of its parts in the debug panel: resident, peak and private memory, card memory (GL_NVX_gpu_memory_info), the copies kept by the textures, geometry banks, models, scenery shapes and lines, sleepers and sound buffers
This commit is contained in:
@@ -95,6 +95,7 @@ set(SOURCES
|
|||||||
"model/Texture.cpp"
|
"model/Texture.cpp"
|
||||||
"utilities/Timer.cpp"
|
"utilities/Timer.cpp"
|
||||||
"utilities/workerpool.cpp"
|
"utilities/workerpool.cpp"
|
||||||
|
"utilities/memorystats.cpp"
|
||||||
"world/Track.cpp"
|
"world/Track.cpp"
|
||||||
"world/TrackSleepers.cpp"
|
"world/TrackSleepers.cpp"
|
||||||
"world/TrackBlend.cpp"
|
"world/TrackBlend.cpp"
|
||||||
|
|||||||
@@ -299,6 +299,21 @@ buffer_manager::buffer( audio::buffer_handle const Buffer ) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// creates AL buffers for files decoded in the background so far, or for all of them if requested
|
// creates AL buffers for files decoded in the background so far, or for all of them if requested
|
||||||
|
// the AL buffers made and the bytes of their sound data, for the debug panel
|
||||||
|
memory_count
|
||||||
|
buffer_manager::memory() const {
|
||||||
|
|
||||||
|
memory_count result;
|
||||||
|
for( auto const &buffer : m_buffers ) {
|
||||||
|
if( buffer.id == null_resource ) { continue; }
|
||||||
|
ALint size { 0 };
|
||||||
|
::alGetBufferi( buffer.id, AL_SIZE, &size );
|
||||||
|
++result.count;
|
||||||
|
result.bytes += static_cast<std::uint64_t>( std::max( size, 0 ) );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
buffer_manager::update( bool const Wait ) {
|
buffer_manager::update( bool const Wait ) {
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "utilities/memorystats.h"
|
||||||
|
|
||||||
#if defined(__APPLE__) && !__has_include(<AL/al.h>)
|
#if defined(__APPLE__) && !__has_include(<AL/al.h>)
|
||||||
#include <OpenAL/al.h>
|
#include <OpenAL/al.h>
|
||||||
#include <OpenAL/alc.h>
|
#include <OpenAL/alc.h>
|
||||||
@@ -60,6 +62,9 @@ public:
|
|||||||
// creates AL buffers for files decoded in the background so far, or for all of them if requested
|
// creates AL buffers for files decoded in the background so far, or for all of them if requested
|
||||||
void
|
void
|
||||||
update( bool const Wait = false );
|
update( bool const Wait = false );
|
||||||
|
// the AL buffers made and the bytes of their sound data, which the AL keeps in the process, for the debug panel
|
||||||
|
memory_count
|
||||||
|
memory() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// types
|
// types
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ public:
|
|||||||
audio::openal_buffer const &buffer( audio::buffer_handle const Buffer ) const;
|
audio::openal_buffer const &buffer( audio::buffer_handle const Buffer ) const;
|
||||||
// makes buffers of all sound files still decoded in the background (sound.asyncload), waiting for them
|
// makes buffers of all sound files still decoded in the background (sound.asyncload), waiting for them
|
||||||
void complete_buffers();
|
void complete_buffers();
|
||||||
|
// the sound buffers and the bytes of their data, for the debug panel
|
||||||
|
memory_count buffers_memory() const { return m_buffers.memory(); }
|
||||||
|
|
||||||
bool init();
|
bool init();
|
||||||
|
|
||||||
@@ -164,4 +166,4 @@ private:
|
|||||||
extern openal_renderer renderer;
|
extern openal_renderer renderer;
|
||||||
extern bool event_volume_change;
|
extern bool event_volume_change;
|
||||||
|
|
||||||
} // namespace audio
|
} // namespace audio
|
||||||
@@ -40,6 +40,29 @@ TMdlContainer::LoadModel(std::string const &Name, bool const Dynamic) {
|
|||||||
TModelsManager::modelcontainer_sequence TModelsManager::m_models { 1, TMdlContainer{} };
|
TModelsManager::modelcontainer_sequence TModelsManager::m_models { 1, TMdlContainer{} };
|
||||||
TModelsManager::stringmodelcontainerindex_map TModelsManager::m_modelsmap;
|
TModelsManager::stringmodelcontainerindex_map TModelsManager::m_modelsmap;
|
||||||
|
|
||||||
|
// what the loaded models keep in memory, for the debug panel
|
||||||
|
TModelsManager::memory_data
|
||||||
|
TModelsManager::memory() {
|
||||||
|
|
||||||
|
memory_data result;
|
||||||
|
std::vector<TSubModel const *> pending;
|
||||||
|
for( auto const &container : m_models ) {
|
||||||
|
if( container.Model == nullptr ) { continue; }
|
||||||
|
++result.models;
|
||||||
|
pending.assign( 1, container.Model->Root );
|
||||||
|
while( false == pending.empty() ) {
|
||||||
|
auto const *submodel { pending.back() };
|
||||||
|
pending.pop_back();
|
||||||
|
if( submodel == nullptr ) { continue; }
|
||||||
|
++result.submodels.count;
|
||||||
|
result.submodels.bytes += memory_of( submodel->Vertices ) + memory_of( submodel->Userdata ) + memory_of( submodel->Indices );
|
||||||
|
pending.push_back( submodel->Child );
|
||||||
|
pending.push_back( submodel->Next );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// wczytanie modelu do tablicy
|
// wczytanie modelu do tablicy
|
||||||
TModel3d *
|
TModel3d *
|
||||||
TModelsManager::LoadModel(std::string const &Name, std::string const &virtualName, bool dynamic) {
|
TModelsManager::LoadModel(std::string const &Name, std::string const &virtualName, bool dynamic) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "utilities/Classes.h"
|
#include "utilities/Classes.h"
|
||||||
|
#include "utilities/memorystats.h"
|
||||||
|
|
||||||
class TMdlContainer {
|
class TMdlContainer {
|
||||||
friend class TModelsManager;
|
friend class TModelsManager;
|
||||||
@@ -22,6 +23,13 @@ class TModelsManager {
|
|||||||
public:
|
public:
|
||||||
// McZapkie: dodalem sciezke, notabene Path!=Patch :)
|
// McZapkie: dodalem sciezke, notabene Path!=Patch :)
|
||||||
static TModel3d *GetModel(std::string const &Name, bool const dynamic = false, bool const Logerrors = true , int uid = 0);
|
static TModel3d *GetModel(std::string const &Name, bool const dynamic = false, bool const Logerrors = true , int uid = 0);
|
||||||
|
// what the loaded models keep in memory, for the debug panel: models, submodels with the bytes of their own copies of the
|
||||||
|
// geometry (vertices, user data, indices; the gpu gets theirs from the geometry banks)
|
||||||
|
struct memory_data {
|
||||||
|
std::uint64_t models { 0 };
|
||||||
|
memory_count submodels;
|
||||||
|
};
|
||||||
|
static memory_data memory();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// types:
|
// types:
|
||||||
|
|||||||
@@ -1484,6 +1484,25 @@ texture_manager::update() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// what the textures keep in memory, for the debug panel
|
||||||
|
texture_manager::memory_data
|
||||||
|
texture_manager::memory() const {
|
||||||
|
|
||||||
|
memory_data result;
|
||||||
|
for( auto const &texture : m_textures ) {
|
||||||
|
if( texture.first == nullptr ) { continue; }
|
||||||
|
if( false == texture.first->data.empty() ) {
|
||||||
|
++result.cpu.count;
|
||||||
|
result.cpu.bytes += memory_of( texture.first->data );
|
||||||
|
}
|
||||||
|
if( texture.first->id != opengl_texture::invalid_id ) {
|
||||||
|
++result.gpu.count;
|
||||||
|
result.gpu.bytes += static_cast<std::uint64_t>( texture.first->size ) * 1024;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// debug performance string
|
// debug performance string
|
||||||
std::string
|
std::string
|
||||||
texture_manager::info() const {
|
texture_manager::info() const {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include <istream>
|
#include <istream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include "winheaders.h"
|
#include "winheaders.h"
|
||||||
|
#include "utilities/memorystats.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "model/ResourceManager.h"
|
#include "model/ResourceManager.h"
|
||||||
#include "gl/ubo.h"
|
#include "gl/ubo.h"
|
||||||
@@ -180,6 +181,14 @@ public:
|
|||||||
// debug performance string
|
// debug performance string
|
||||||
std::string
|
std::string
|
||||||
info() const;
|
info() const;
|
||||||
|
// what the textures keep in memory, for the debug panel: copies of the data kept in the process (count, bytes), and the
|
||||||
|
// textures made on the gpu with the size of their data (count, bytes)
|
||||||
|
struct memory_data {
|
||||||
|
memory_count cpu;
|
||||||
|
memory_count gpu;
|
||||||
|
};
|
||||||
|
memory_data
|
||||||
|
memory() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// types:
|
// types:
|
||||||
|
|||||||
@@ -420,6 +420,32 @@ geometry_bank::userdata( gfx::geometry_handle const &Geometry ) const {
|
|||||||
// geometry bank manager, holds collection of geometry banks
|
// geometry bank manager, holds collection of geometry banks
|
||||||
|
|
||||||
// performs a resource sweep
|
// performs a resource sweep
|
||||||
|
// what the chunks keep in the process, added to the counts
|
||||||
|
void
|
||||||
|
geometry_bank::count_memory( memory_count &Vertices, memory_count &Indices, memory_count &Userdata ) const {
|
||||||
|
|
||||||
|
for( auto const &chunk : m_chunks ) {
|
||||||
|
++Vertices.count;
|
||||||
|
Vertices.bytes += memory_of( chunk.vertices );
|
||||||
|
Indices.bytes += memory_of( chunk.indices );
|
||||||
|
Userdata.bytes += memory_of( chunk.userdata );
|
||||||
|
}
|
||||||
|
Indices.count = Userdata.count = Vertices.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// what the banks keep in the process, for the debug panel
|
||||||
|
auto
|
||||||
|
geometrybank_manager::memory() const -> memory_data {
|
||||||
|
|
||||||
|
memory_data result;
|
||||||
|
for( auto const &bank : m_geometrybanks ) {
|
||||||
|
if( bank.first == nullptr ) { continue; }
|
||||||
|
++result.banks;
|
||||||
|
bank.first->count_memory( result.vertices, result.indices, result.userdata );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
geometrybank_manager::update() {
|
geometrybank_manager::update() {
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "model/ResourceManager.h"
|
#include "model/ResourceManager.h"
|
||||||
|
#include "utilities/memorystats.h"
|
||||||
|
|
||||||
struct world_vertex;
|
struct world_vertex;
|
||||||
|
|
||||||
@@ -161,6 +162,9 @@ public:
|
|||||||
auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &;
|
auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &;
|
||||||
// primitive type of specified chunk, 0 if there's no such chunk
|
// primitive type of specified chunk, 0 if there's no such chunk
|
||||||
auto type( gfx::geometry_handle const &Geometry ) const -> unsigned int;
|
auto type( gfx::geometry_handle const &Geometry ) const -> unsigned int;
|
||||||
|
// what the chunks keep in the process (a copy of what went to the gpu), added to the counts: chunks and vertex bytes, index bytes,
|
||||||
|
// user data bytes
|
||||||
|
void count_memory( memory_count &Vertices, memory_count &Indices, memory_count &Userdata ) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// types:
|
// types:
|
||||||
@@ -272,6 +276,14 @@ public:
|
|||||||
auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &;
|
auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &;
|
||||||
// provides direct access to vertex data of specfied chunk
|
// provides direct access to vertex data of specfied chunk
|
||||||
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
|
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
|
||||||
|
// what the banks keep in the process, for the debug panel: banks, chunks with their vertex bytes, index bytes and user data bytes
|
||||||
|
struct memory_data {
|
||||||
|
std::uint64_t banks { 0 };
|
||||||
|
memory_count vertices;
|
||||||
|
memory_count indices;
|
||||||
|
memory_count userdata;
|
||||||
|
};
|
||||||
|
auto memory() const -> memory_data;
|
||||||
// provides direct access to vertex data of specfied chunk
|
// provides direct access to vertex data of specfied chunk
|
||||||
auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &;
|
auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &;
|
||||||
// primitive type of specified chunk, 0 if there's no such chunk
|
// primitive type of specified chunk, 0 if there's no such chunk
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "vehicle/Train.h"
|
#include "vehicle/Train.h"
|
||||||
#include "vehicle/Camera.h"
|
#include "vehicle/Camera.h"
|
||||||
#include "simulation/simulation.h"
|
#include "simulation/simulation.h"
|
||||||
|
#include "model/MdlMngr.h"
|
||||||
|
#include "audio/audiorenderer.h"
|
||||||
|
#include "world/Track.h"
|
||||||
|
#include "utilities/memorystats.h"
|
||||||
#include "utilities/Logs.h"
|
#include "utilities/Logs.h"
|
||||||
#include "simulation/simulationtime.h"
|
#include "simulation/simulationtime.h"
|
||||||
#include "application/application.h"
|
#include "application/application.h"
|
||||||
@@ -805,6 +809,7 @@ void opengl46_renderer::SwapBuffers()
|
|||||||
|
|
||||||
// the debug data, in sections for the panel. info_times() and info_stats() hold the same, joined
|
// the debug data, in sections for the panel. info_times() and info_stats() hold the same, joined
|
||||||
m_debugsections.clear();
|
m_debugsections.clear();
|
||||||
|
Update_Memory_Info();
|
||||||
// a line of a table of times: the name indented and padded to a column, then the time(s)
|
// a line of a table of times: the name indented and padded to a column, then the time(s)
|
||||||
auto const timeline = []( int const Indent, std::string const &Name, std::string const &Values ) {
|
auto const timeline = []( int const Indent, std::string const &Name, std::string const &Values ) {
|
||||||
auto line { std::string( static_cast<std::size_t>( Indent ), ' ' ) + Name };
|
auto line { std::string( static_cast<std::size_t>( Indent ), ' ' ) + Name };
|
||||||
@@ -1133,6 +1138,7 @@ void opengl46_renderer::SwapBuffers()
|
|||||||
culltext += Light_Shadow_Cull_Info( false );
|
culltext += Light_Shadow_Cull_Info( false );
|
||||||
m_debugsections.push_back( { "Scene statistics", std::move( statstext ), false } );
|
m_debugsections.push_back( { "Scene statistics", std::move( statstext ), false } );
|
||||||
m_debugsections.push_back( { "GPU culling", std::move( culltext ), false } );
|
m_debugsections.push_back( { "GPU culling", std::move( culltext ), false } );
|
||||||
|
m_debugsections.push_back( { "Memory", m_memorytext, true } );
|
||||||
|
|
||||||
// the parts of the sections start with a line break here and there, and end with one or not
|
// the parts of the sections start with a line break here and there, and end with one or not
|
||||||
for( auto §ion : m_debugsections ) {
|
for( auto §ion : m_debugsections ) {
|
||||||
@@ -1149,6 +1155,105 @@ void opengl46_renderer::SwapBuffers()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// what the process and its parts keep in memory, for the debug panel (m_memorytext). gathered every 2 s: it goes through every
|
||||||
|
// texture, geometry chunk, model and scenery shape
|
||||||
|
void opengl46_renderer::Update_Memory_Info()
|
||||||
|
{
|
||||||
|
auto const now { std::chrono::steady_clock::now() };
|
||||||
|
if( ( false == m_memorytext.empty() ) && ( now - m_memorytime < std::chrono::seconds( 2 ) ) ) { return; }
|
||||||
|
m_memorytime = now;
|
||||||
|
|
||||||
|
auto const mb = []( std::uint64_t const Bytes ) {
|
||||||
|
return to_string( static_cast<double>( Bytes ) / ( 1024.0 * 1024.0 ), 1 ) + " MB"; };
|
||||||
|
auto const counted = []( memory_count const &Count, char const *Name ) {
|
||||||
|
return std::to_string( Count.count ) + " " + Name; };
|
||||||
|
std::string text;
|
||||||
|
|
||||||
|
auto const process { query_process_memory() };
|
||||||
|
if( process.valid ) {
|
||||||
|
text += "process: " + mb( process.resident ) + " resident (peak " + mb( process.peak ) + ")"
|
||||||
|
+ ( process.committed > 0 ? ", " + mb( process.committed ) + " private" : std::string{} ) + "\n";
|
||||||
|
}
|
||||||
|
// the memory of the card, all processes: GL_NVX_gpu_memory_info (in kB)
|
||||||
|
if( m_nvxmemory < 0 ) {
|
||||||
|
m_nvxmemory = 0;
|
||||||
|
GLint extensions { 0 };
|
||||||
|
::glGetIntegerv( GL_NUM_EXTENSIONS, &extensions );
|
||||||
|
for( GLint index = 0; index < extensions; ++index ) {
|
||||||
|
auto const *name { reinterpret_cast<char const *>( ::glGetStringi( GL_EXTENSIONS, static_cast<GLuint>( index ) ) ) };
|
||||||
|
if( ( name != nullptr ) && ( std::strcmp( name, "GL_NVX_gpu_memory_info" ) == 0 ) ) {
|
||||||
|
m_nvxmemory = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( m_nvxmemory > 0 ) {
|
||||||
|
GLint dedicated { 0 }, available { 0 }, evicted { 0 };
|
||||||
|
::glGetIntegerv( 0x9047, &dedicated ); // GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX
|
||||||
|
::glGetIntegerv( 0x9049, &available ); // GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX
|
||||||
|
::glGetIntegerv( 0x904B, &evicted ); // GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX
|
||||||
|
text += "card memory, all programs: " + mb( static_cast<std::uint64_t>( std::max( dedicated - available, 0 ) ) * 1024 ) + " used of "
|
||||||
|
+ mb( static_cast<std::uint64_t>( dedicated ) * 1024 ) + ( evicted > 0 ? ", " + mb( static_cast<std::uint64_t>( evicted ) * 1024 ) + " evicted" : std::string{} ) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t accounted { 0 };
|
||||||
|
// textures: the copies of the data kept in the process after the upload
|
||||||
|
auto const textures { m_textures.memory() };
|
||||||
|
accounted += textures.cpu.bytes;
|
||||||
|
text += "textures: " + std::to_string( textures.cpu.count ) + " keep their data in ram, " + mb( textures.cpu.bytes ) + "; "
|
||||||
|
+ std::to_string( textures.gpu.count ) + " on the card, ~" + mb( textures.gpu.bytes ) + "\n";
|
||||||
|
// geometry: the copies of the chunks sent to the card
|
||||||
|
auto const geometry { m_geometry.memory() };
|
||||||
|
accounted += geometry.vertices.bytes + geometry.indices.bytes + geometry.userdata.bytes;
|
||||||
|
text += "geometry banks: " + std::to_string( geometry.banks ) + ", " + counted( geometry.vertices, "chunks" ) + ": vertices " + mb( geometry.vertices.bytes )
|
||||||
|
+ ", indices " + mb( geometry.indices.bytes ) + ", user data " + mb( geometry.userdata.bytes ) + "\n";
|
||||||
|
// models: the geometry their submodels keep of their own
|
||||||
|
auto const models { TModelsManager::memory() };
|
||||||
|
accounted += models.submodels.bytes;
|
||||||
|
text += "models: " + std::to_string( models.models ) + ", " + counted( models.submodels, "submodels" ) + ", their own geometry " + mb( models.submodels.bytes ) + "\n";
|
||||||
|
// scenery: the world space vertices the shapes and lines keep, the transforms of the sleepers, the model instances
|
||||||
|
memory_count shapes, lines, sleepers;
|
||||||
|
std::uint64_t instances { 0 };
|
||||||
|
if( simulation::Region != nullptr ) {
|
||||||
|
auto const shape = [ &shapes ]( scene::shape_node const &Shape ) {
|
||||||
|
++shapes.count;
|
||||||
|
shapes.bytes += memory_of( Shape.data().vertices ) + memory_of( Shape.data().userdata ); };
|
||||||
|
for( auto const *section : simulation::Region->m_sections ) {
|
||||||
|
if( section == nullptr ) { continue; }
|
||||||
|
for( auto const &item : section->m_shapes ) { shape( item ); }
|
||||||
|
for( auto const &cell : section->m_cells ) {
|
||||||
|
for( auto const &item : cell.m_shapesopaque ) { shape( item ); }
|
||||||
|
for( auto const &item : cell.m_shapestranslucent ) { shape( item ); }
|
||||||
|
for( auto const &item : cell.m_lines ) {
|
||||||
|
++lines.count;
|
||||||
|
lines.bytes += memory_of( item.data().vertices );
|
||||||
|
}
|
||||||
|
for( auto const *track : cell.m_paths ) {
|
||||||
|
if( track->m_sleeper_local_transforms.empty() ) { continue; }
|
||||||
|
++sleepers.count;
|
||||||
|
sleepers.bytes += memory_of( track->m_sleeper_local_transforms );
|
||||||
|
}
|
||||||
|
instances += cell.m_instancesopaque.size() + cell.m_instancetranslucent.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accounted += shapes.bytes + lines.bytes + sleepers.bytes;
|
||||||
|
text += "scenery: " + counted( shapes, "shapes" ) + " " + mb( shapes.bytes ) + ", " + counted( lines, "lines" ) + " " + mb( lines.bytes ) + ", sleepers of "
|
||||||
|
+ counted( sleepers, "tracks" ) + " " + mb( sleepers.bytes ) + ", " + std::to_string( instances ) + " model instances\n";
|
||||||
|
// sounds: the data of the buffers, which the AL keeps in the process
|
||||||
|
auto const sounds { audio::renderer.buffers_memory() };
|
||||||
|
accounted += sounds.bytes;
|
||||||
|
text += "sounds: " + counted( sounds, "buffers" ) + " " + mb( sounds.bytes ) + "\n";
|
||||||
|
text += "vehicles: " + std::to_string( simulation::Vehicles.sequence().size() ) + "\n";
|
||||||
|
if( process.valid ) {
|
||||||
|
auto const total { process.committed > 0 ? process.committed : process.resident };
|
||||||
|
text += "counted above: " + mb( accounted ) + " of " + mb( total ) + ( process.committed > 0 ? " private" : " resident" ) + ", the rest "
|
||||||
|
+ mb( total > accounted ? total - accounted : 0 ) + " (other data, the heaps, the driver)\n";
|
||||||
|
}
|
||||||
|
text += "gathered in " + to_string( std::chrono::duration<double, std::milli>( std::chrono::steady_clock::now() - now ).count(), 2 ) + " ms, every 2 s";
|
||||||
|
m_memorytext = std::move( text );
|
||||||
|
}
|
||||||
|
|
||||||
void opengl46_renderer::cpu_timeline::begin( char const *Name )
|
void opengl46_renderer::cpu_timeline::begin( char const *Name )
|
||||||
{
|
{
|
||||||
if( active ) { end(); }
|
if( active ) { end(); }
|
||||||
|
|||||||
@@ -617,6 +617,12 @@ class opengl46_renderer : public gfx_renderer {
|
|||||||
//debug_stats m_debugstats;
|
//debug_stats m_debugstats;
|
||||||
std::string m_debugstatstext;
|
std::string m_debugstatstext;
|
||||||
std::vector<info_section> m_debugsections; // the same data in sections, for the debug panel
|
std::vector<info_section> m_debugsections; // the same data in sections, for the debug panel
|
||||||
|
// what the process and its parts keep in memory, for the debug panel, gathered every 2 s; whether the card tells its memory
|
||||||
|
// (GL_NVX_gpu_memory_info), -1 not asked yet
|
||||||
|
void Update_Memory_Info();
|
||||||
|
std::string m_memorytext;
|
||||||
|
std::chrono::steady_clock::time_point m_memorytime {};
|
||||||
|
int m_nvxmemory { -1 };
|
||||||
struct simulation_state {
|
struct simulation_state {
|
||||||
std::string weather;
|
std::string weather;
|
||||||
std::string season;
|
std::string season;
|
||||||
|
|||||||
70
utilities/memorystats.cpp
Normal file
70
utilities/memorystats.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "utilities/memorystats.h"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <psapi.h>
|
||||||
|
#pragma comment(lib, "psapi.lib")
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#else
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
process_memory query_process_memory()
|
||||||
|
{
|
||||||
|
process_memory result;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
PROCESS_MEMORY_COUNTERS_EX counters{};
|
||||||
|
counters.cb = sizeof(counters);
|
||||||
|
if (::GetProcessMemoryInfo(::GetCurrentProcess(), reinterpret_cast<PROCESS_MEMORY_COUNTERS *>(&counters), sizeof(counters)))
|
||||||
|
{
|
||||||
|
result.valid = true;
|
||||||
|
result.resident = counters.WorkingSetSize;
|
||||||
|
result.peak = counters.PeakWorkingSetSize;
|
||||||
|
result.committed = counters.PrivateUsage;
|
||||||
|
}
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
mach_task_basic_info info{};
|
||||||
|
mach_msg_type_number_t count{MACH_TASK_BASIC_INFO_COUNT};
|
||||||
|
if (::task_info(::mach_task_self(), MACH_TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&info), &count) == KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
result.valid = true;
|
||||||
|
result.resident = info.resident_size;
|
||||||
|
result.peak = info.resident_size_max;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// the lines of /proc/self/status give kB
|
||||||
|
std::ifstream status("/proc/self/status");
|
||||||
|
std::string line;
|
||||||
|
auto const value = [&line](char const *Key, std::uint64_t &Target) {
|
||||||
|
auto const length{std::char_traits<char>::length(Key)};
|
||||||
|
if (line.compare(0, length, Key) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Target = std::stoull(line.substr(length)) * 1024;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
while (std::getline(status, line))
|
||||||
|
{
|
||||||
|
if (value("VmRSS:", result.resident))
|
||||||
|
{
|
||||||
|
result.valid = true;
|
||||||
|
}
|
||||||
|
value("VmHWM:", result.peak);
|
||||||
|
// resident private memory: the closest this has to private bytes
|
||||||
|
value("RssAnon:", result.committed);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
44
utilities/memorystats.h
Normal file
44
utilities/memorystats.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// memory of the process, as the system counts it, for the debug panel
|
||||||
|
struct process_memory
|
||||||
|
{
|
||||||
|
bool valid{false};
|
||||||
|
std::uint64_t resident{0}; // in physical memory now (working set, resident set)
|
||||||
|
std::uint64_t peak{0}; // the most it was in physical memory
|
||||||
|
std::uint64_t committed{0}; // private memory the process asked for, in physical memory or not (private bytes; 0 where not known)
|
||||||
|
};
|
||||||
|
|
||||||
|
process_memory query_process_memory();
|
||||||
|
|
||||||
|
// what a part of the program keeps in memory: things and bytes
|
||||||
|
struct memory_count
|
||||||
|
{
|
||||||
|
std::uint64_t count{0};
|
||||||
|
std::uint64_t bytes{0};
|
||||||
|
|
||||||
|
memory_count &operator+=(memory_count const &Other)
|
||||||
|
{
|
||||||
|
count += Other.count;
|
||||||
|
bytes += Other.bytes;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// bytes a vector keeps for its elements
|
||||||
|
template <typename Type_> std::uint64_t memory_of(std::vector<Type_> const &Vector)
|
||||||
|
{
|
||||||
|
return static_cast<std::uint64_t>(Vector.capacity()) * sizeof(Type_);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user