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

[utils] Support CPU/GPU stats analytics #1295

Closed
raysan5 opened this issue Jun 29, 2020 · 19 comments
Closed

[utils] Support CPU/GPU stats analytics #1295

raysan5 opened this issue Jun 29, 2020 · 19 comments
Labels
new feature This is an addition to the library

Comments

@raysan5
Copy link
Owner

raysan5 commented Jun 29, 2020

It could be useful to keep track of some CPU/GPU analytics, like memory usage.

Info already displayed on initialization: GPU vendor, renderer, version, GLSL version

Data loading memory usage:

  • CPU: Images / Sounds / MusicStream / Font.chars
  • GPU: Textures / Shaders / Meshes / Font.texture

Data loading can be registered with: RL_MALLOC() / RL_CALLOC() / RL_FREE()

enum StatsMemType {
    CPU_RAW_DATA,
    CPU_VERTEX_DATA, 
    CPU_IMAGE, 
    CPU_TEXT, 
    CPU_FONT_DATA,
    CPU_AUDIO_BUFFER, 
    GPU_VERTEX_BUFFER, 
    GPU_TEXTURE, 
    GPU_SHADER, 
    GPU_FBO
} StatsMemType;

// Global memory usage stats
struct StatsMemAlloc {
    int id;
    int type;               // StatsMemType 
    unsigned int size;      // Size in bytes
} StatsMemAlloc;

#define STATS_MEM_RECORD(int type, int size) StatsMemAlloc(type, size)
  • GPU memory allocations (VRAM): glBufferData(), glTexImage2D(), glShaderSource()
  • Frame times: updateTime, drawTime, idleTime
  • Draw calls counter (and batching)
  • Textures information (# textures, id, format, samplerState)

This issue is a draft, just some notes. Need to think about the best way to organize/record this information.

typedef struct GpuStats {	// 1 frame? General? → FrameStats?
	int drawsCount;
	int vertexCount;
	int trianglesCount;
	int texturesCount;	// total? used in frame?
	int texturesMemory;	// in bytes
	int vertexMemory;	// in bytes, in CPU/GPU?
	float timeUpdate;	// in seconds
	float timeDraw;		
	float timeIdle;
} GpuStats;
@raysan5 raysan5 added the new feature This is an addition to the library label Jul 5, 2020
@Obscure2020
Copy link

Most games on Steam list their minimum required RAM and VRAM. It would be handy to have some sort of way to get a raylib program to spit out the maximum amount of RAM and VRAM it ever used while running, so that we know what to put for the minimum required stats for games we publish that were made using raylib. What do you think?

@raysan5
Copy link
Owner Author

raysan5 commented Aug 8, 2020

@Obscure2020 Actually that's some of the information intended to be provided by this new feature.

@Obscure2020
Copy link

Wonderful. I am in support of these features!

@ghost
Copy link

ghost commented Sep 25, 2020

@raysan5 Maybe the big GPU and CPU profiler https://github.com/wolfpld/tracy/ can give some impressions.

@jmorel33
Copy link
Contributor

I up vote this, and would enjoy a way to know what OpenGL/GLSL support the system has prior to saying; yes the game will run fine.

@tokorv
Copy link

tokorv commented Jan 4, 2021

This sounds like a good idea.

@raysan5 raysan5 pinned this issue Feb 15, 2021
@raysan5 raysan5 unpinned this issue Feb 15, 2021
@chriscamacho
Copy link
Contributor

chriscamacho commented Mar 12, 2021

It would be useful to get the monitor frame rate if possible...

@jmorel33
Copy link
Contributor

jmorel33 commented Apr 5, 2021

It would be useful to get the monitor frame rate if possible...

I personally have found an easy way to get the Monitor Refresh rate;
current_monitor = GetCurrentMonitor();
GetMonitorRefreshRate(current_monitor);

@raysan5
Copy link
Owner Author

raysan5 commented Apr 15, 2021

I've been investigating this improvement a bit further. Supporting this kind of stats data requires hooking multiple functions and designing an specific system just for it. Here some ideas:

CPU RAM memory usage

Every time RL_*ALLOC is called, some memory is allocated. Functions involved:

// core
    LoadFileData();
    LoadFileText();

// textures
    LoadImage();
    LoadImageRaw();
    LoadImageAnim();
    LoadImageColors();
    LoadImagePalette();
    GenImage*();
    LoadTexture();
    LoadTextureFromImage();
    LoadTextureCubemap();
    LoadRenderTexture();

// text
    LoadFontEx();
    LoadFontFromImage();
    LoadFontData();
    
// models
    LoadModel();
    LoadModelFromMesh();
    UploadMesh();
    GenMesh*();
    LoadMaterials();
    LoadMaterialDefault();
    LoadModelAnimations();

// audio 
    LoadWave();
    LoadSound();
    LoadSoundFromWave();
    LoadWaveSamples();
    LoadMusicStream();
    LoadAudioBuffer();

GPU VRAM memory usage

All GPU memory allocations go through rlgl module:

    // Vertex buffers
    rlLoadVertexBuffer();            // glBufferData()
    rlLoadVertexBufferElement();     // glBufferData()

    // Textures
    rlLoadTexture();                 // glTexImage2D()
    rlLoadTextureDepth();            // glTexImage2D()
    rlLoadTextureCubemap();          // glTexImage2D()

    // FBO use texture attachments

    // Shaders
    rlLoadShaderProgram();           // glLinkProgram()

GPU one frame analytics

  • Total time (delta time) -> CORE.Time
    • Update time
    • Draw time
    • Wait/idle time
  • Draw passes info
    • Draw calls count -> RLGL.currentBatch->drawsCounter, only batching system!
    • Textures used
    • Shaders used
    • Fbo used
    • VBO used (total vertex processed)

GPU capabilities -> rlLoadExtensions() ✔️

  • GPU model (vendor/renderer)
  • OpenGL/GLSL version
  • GL extensions supported
  • GL limits supported
    • Max texture count/size
    • Max shader uniforms
    • Anisotropic levels
    • ETC.

This is some of the data that can be analyzed, the problem is that it requires lot of code automation to grab this data and a bunch of extra memory and structs to store this data properly. Also, most users probably don't care for this data, so, It should be possible to disabled the stats system if required.

@raysan5 raysan5 closed this as completed Apr 15, 2021
@raysan5 raysan5 reopened this Apr 15, 2021
@jmorel33
Copy link
Contributor

Knowing gpu memory I find a useful metric as it can inflate fast if not careful or if out of control.

The other metric imho would be all of the features the GPU supports in order to know fi we can use either OpenGL 1.1 or 2.1 or 3.3 or greater.

@raysan5
Copy link
Owner Author

raysan5 commented Apr 18, 2021

@jmorel33 Just edited my previous comment to add additional info. Agree that GPU data is probably the most important one.

As commented, supporting it requires LOT of code automation and some redesigns to store all required stats. At this point I'm evaluating the real benefits for the standard raylib users.

raysan5 added a commit that referenced this issue Apr 18, 2021
Added config flag: SUPPORT_GL_DETAILS_INFO
@jmorel33
Copy link
Contributor

jmorel33 commented Apr 18, 2021

Not sure of the automation that is required? One that allows or disallows features because of an OpenGL version? Essentially removing this burden from the programmer? Of course that would be great but I would not necessarily expect this but would be great.

That said I would prefer a more recent openGL version support than the type of automation as to me it is more important. For for the beginner, having an automated way of enabling features is great.

@Crydsch
Copy link
Contributor

Crydsch commented May 20, 2021

Some questions/ideas regarding this:

  1. How is the Stats-System supposed to be used?
    rlgl now checks if SUPPORT_GL_DETAILS_INFO is defined and prints information if so.
    Should the entire Stats-System work like this?
    Should there be multiple defines like SUPPORT_RAM_INFO, SUPPORT_VRAM_INFO, etc...
    or just one ENABLE_STATS_SYSTEM?

  2. How is the information from Stats-System to be accessed?
    Only print a summery on shutdown?
    Print every change? (Might be very console intensive, if we print info for every malloc)
    Access at runtime? Might be interesting to build interactive profiling into a game!

  3. Any plans on how the gathering itself is handled.
    We could add something like this wherever necessary:

#if defined(ENABLE_STATS_SYSTEM) 
   stats.vram.textures += X;
   stats.vram.total += X;
#endif

@Obscure2020
Copy link

Other people might have different preferences, but as I envisioned it, one ENABLE_STATS_SYSTEM might be the easiest to use. That way, it would be a single thing to turn on, compile, run, get stats. Then you could remove that single setting afterwards.

I also envisioned "Only print a summary on shutdown." You're right that printing every change could become quite console intensive, and while possibly useful for some people, access at runtime might not be completely necessary. The way I saw it, I'd like to program a game, turn on ENABLE_STATS_SYSTEM, compile, run, and then while running the program keep track of how much RAM and VRAM (and other stats) it's currently using. Then, when the program shuts down, it would print out a stats summary including the highest RAM and VRAM usage it reached while running, and the results of other stats tracking.

@jmorel33
Copy link
Contributor

I just want to be able to pull the live stats of how much GDDR is used and how many triangles processed, just like how many FPS is used. I do not need a function to print the value on the screen I just need the value and let me do what I want with it.

@Crydsch
Copy link
Contributor

Crydsch commented May 30, 2021

Maybe something like:

  • stats_system.h for struct definitions, captured data, functions etc.. (basically its own module)
  • can enabled with RAYLIB_MODULE_STATS (header file has #define ENABLE_STATS_SYSTEM)
  • provides direct access to captured data at runtime
  • provides functions like void StatsPrintSummary()

That way we get the best of both worlds.

@630Studios
Copy link
Contributor

630Studios commented Jul 17, 2021

My short answer is that while I like this Idea. I think if you were going to actually do this, and do it properly, more consideration would need to be given to the API functionality as presented around the creation of the underlying data types, and that more than likely some sort of thin management layer would need to be presented.

Without the proper management layer and api calls, there is entirely to much assumption that the only way data will be created and memory will be allocated is through the function calls currently present, or the RL_ allocation/deallocation/reallocation methods. Any creation of these data types by the end user that falls outside of these functions would be entirely dependent on the user to properly implement hooks into this stat tracking system, which IMHO makes it entirely to prone to user error/mistakes to make it worthwhile. It would be easy to blame the end user for not knowing or doing something right, but it still does not change the end result that the system would then be inaccurate.

@jmorel33
Copy link
Contributor

jmorel33 commented Jul 27, 2021

The reason why this is an important to have feature is as anything else;

  • you want to know if you have a memory leak
  • you want to know if your textures are in gpu ram
  • you want to know if expected triangles are rendered
  • you want to know if you are hitting a gpu bottleneck for what reason
  • want to know...

etc etc

We are making games, game engines, etc and we need tools.

Half of my development time is concentrated on tracing, debugging tools, and analytics...

@raysan5
Copy link
Owner Author

raysan5 commented Sep 22, 2021

Some more thoughts on this feature:

We can differentiate two types of analysis:

  1. Global stats analysis (CPU and GPU)
  2. Single Frame stats analysis (GPU)

1. Global stats analysis (CPU and GPU)

Analyze: CPU memory usage

Memory allocations should be recorded and properly tagged with data type, it would require some additional identifier for each piece of data loaded. Ideally, extra information for each piece of data should be saved...

  • Possible data types: CPU_RAW_DATA, CPU_IMAGE_DATA, CPU_VERTEX_DATA, CPU_GLYPH_DATA, CPU_AUDIO_DATA.

Analyze: GPU memory usage

GPU memory allocations should be recorded and properly tagged and identified. Extra information should be saved for every piece of data.

  • Possible data types: GPU_VERTEX_BUFFER, GPU_TEXTURE, GPU_SHADER, GPU_FBO

IMPLICATIONS:

  • Those processes will add some overhead to data loading/unloading.
  • ALL places in code where an allocation happens, a recording must be issued, using a MACRO for that is probably the best option, to allow completely disabling that code in case of not required.
  • ALL places in code where a de-allocation happens, a un-recording must be issued. This process implies dealing with a list of MemoryRecords and despite the identifiers, in most cases (i.e. Image) we have no way to identify the unloaded image. It could imply many raylib changes to basic structures just to support this feature. -> No plans for the moment.

2. Single Frame stats analysis (GPU)

It will imply analyzing the state for a single frame:

  • Analyze: Timming: Update, Draw, Idle

Indepent time values depend on user use of the library (i.e. RenderTexture usage outside the game loop or in the Update) and actually, only the global frame time could be accurately reported; GetFrameTime() already reports that value.

  • Analyze: Draws: Draw calls and Batching

Draw calls should be recorded individually as well as the draws saved by batching.

  • Analyze: Resources: VBO/VAO, Textures, Shaders, FBO

Resource usage (OpenGL active state) should be recorded by draw call. That implies A LOT of information recorded to be managed every frame.

After this analysis, I decided that at this moment this feature is out-of-scope for raylib, it seems more proper for a higher level engine that has more control over the game scenegraph.

Anyone with the need for a single-frame stats analysis could use RenderDoc, actually, that's the tool I used for raylib performance analysis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature This is an addition to the library
Projects
None yet
Development

No branches or pull requests

7 participants