From ca49785159e6a7cfd2d999a99041fa1567575a24 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 28 Jan 2010 16:07:52 +0000 Subject: [PATCH] Add a performance profiler Support specifying an alternative OpenGL library Only return non-null from glXGetProcAddress if the function is found from libGL --- Makefile | 8 +-- source/commandinterpreter.cpp | 16 ++++++ source/commandinterpreter.h | 1 + source/gldbg.cpp | 1 + source/gldbg.h | 3 ++ source/glwrap.c | 21 ++++++-- source/profiler.cpp | 96 +++++++++++++++++++++++++++++++++++ source/profiler.h | 31 +++++++++++ 8 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 source/profiler.cpp create mode 100644 source/profiler.h diff --git a/Makefile b/Makefile index 60da9b2..d06ea67 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # $Id$ -CFLAGS = -Igensrc -ggdb -Wall -Wextra -Werror -CXXFLAGS = -Igensrc -ggdb -Wall -Wextra -Werror +CFLAGS = -Igensrc -ggdb -Wall -Wextra +CXXFLAGS = -Igensrc -ggdb -Wall -Wextra PACKAGES_gldbg = mspcore mspstrings mspio mspfs CXXFLAGS_gldbg = $(shell pkg-config --cflags $(PACKAGES_gldbg)) @@ -15,7 +15,7 @@ glwrap.so: source/glwrap.o source/arraysize.o gldump: source/gldecoder.o source/gldump.o source/glprint.o source/enums.o source/arraysize.o source/tmpalloc.o $(CC) -o $@ $^ $(LIBS) $(LDFLAGS) -gldbg: source/gldbg.o source/gldecoder.o source/glprint.o source/commandinterpreter.o source/tracer.o source/process.o source/enums.o source/arraysize.o source/tmpalloc.o source/glstate.o source/texturestate.o source/bufferstate.o +gldbg: source/gldbg.o source/gldecoder.o source/glprint.o source/commandinterpreter.o source/tracer.o source/process.o source/enums.o source/arraysize.o source/tmpalloc.o source/glstate.o source/texturestate.o source/bufferstate.o source/profiler.o $(CXX) -o $@ $^ $(LIBS_gldbg) $(LIBS) $(LDFLAGS) source/glwrap.o: source/functions.h gensrc/functions.enum gensrc/glwrap.funcs @@ -23,7 +23,7 @@ source/gldecoder.o: source/functions.h gensrc/gldecoder.struct gensrc/gldecoder. source/gldump.o: source/gldecoder.h gensrc/gldecoder.struct source/glprint.h source/glprint.o: source/arraysize.h gensrc/glprint.funcs gensrc/gldecoder.struct source/enums.o: gensrc/enums.table -source/gldbg.o: source/gldbg.h source/gldecoder.h source/tracer.h source/commandinterpreter.h source/glstate.h +source/gldbg.o: source/gldbg.h source/gldecoder.h source/tracer.h source/commandinterpreter.h source/glstate.h source/profiler.h source/tracer.o: source/gldecoder.h source/glprint.h source/commandinterpreter.o: source/gldbg.h source/glstate.h source/process.o: source/process.h diff --git a/source/commandinterpreter.cpp b/source/commandinterpreter.cpp index abc9dda..c684979 100644 --- a/source/commandinterpreter.cpp +++ b/source/commandinterpreter.cpp @@ -57,6 +57,11 @@ CommandInterpreter::CommandInterpreter(GlDbg &d): "trace end\n" " Terminate trace, closing the file.\n"); + commands["profile"] = Command(&CommandInterpreter::cmd_profile, + "Profiles GL usage and performance", + "profile {on|off}\n" + " Enables or disables profiling\n"); + commands["state"] = Command(&CommandInterpreter::cmd_state, "Inspects general GL state", "state vertex\n" @@ -202,6 +207,17 @@ void CommandInterpreter::cmd_trace(const string &args) } } +void CommandInterpreter::cmd_profile(const string &args) +{ + Profiler &profiler = gldbg.get_profiler(); + if(args.empty() || args=="on") + profiler.enable(); + else if(args=="off") + profiler.disable(); + else + throw InvalidParameterValue("Invalid argument"); +} + void CommandInterpreter::cmd_state(const string &args) { const GlState &glstate = gldbg.get_glstate(); diff --git a/source/commandinterpreter.h b/source/commandinterpreter.h index 14d8eb9..852ba65 100644 --- a/source/commandinterpreter.h +++ b/source/commandinterpreter.h @@ -46,6 +46,7 @@ private: void cmd_kill(const std::string &); void cmd_exit(const std::string &); void cmd_trace(const std::string &); + void cmd_profile(const std::string &); void cmd_state(const std::string &); void cmd_texture(const std::string &); void cmd_buffer(const std::string &); diff --git a/source/gldbg.cpp b/source/gldbg.cpp index 34e616f..d18ff51 100644 --- a/source/gldbg.cpp +++ b/source/gldbg.cpp @@ -135,6 +135,7 @@ void GlDbg::read_stream() break; tracer.decode(data, len); glstate.decode(data, len); + profiler.decode(data, len); buf_offset += size; } if(buf_offset>8192) diff --git a/source/gldbg.h b/source/gldbg.h index 5654715..e42e4aa 100644 --- a/source/gldbg.h +++ b/source/gldbg.h @@ -15,6 +15,7 @@ Distributed under the GPL #include "commandinterpreter.h" #include "glstate.h" #include "process.h" +#include "profiler.h" #include "tracer.h" class GlDbg: public Msp::Application @@ -28,6 +29,7 @@ private: bool flushing; Tracer tracer; GlState glstate; + Profiler profiler; bool got_sigchld; static RegApp reg; @@ -37,6 +39,7 @@ public: int main(); Tracer &get_tracer() { return tracer; } GlState &get_glstate() { return glstate; } + Profiler &get_profiler() { return profiler; } Process &get_process() { return process; } void launch(); void quit(bool); diff --git a/source/glwrap.c b/source/glwrap.c index d51eb31..bf05214 100644 --- a/source/glwrap.c +++ b/source/glwrap.c @@ -21,10 +21,13 @@ static inline void *glsym(const char *sym) static void *libgl = NULL; if(!libgl) { - libgl = dlopen("libGL.so", RTLD_NOW); + const char *libgl_name = getenv("GLWRAP_LIBGL"); + if(!libgl_name) + libgl_name = "libGL.so"; + libgl = dlopen(libgl_name, RTLD_NOW); if(!libgl) { - fprintf(stderr, "Could not open libGL: %s\n", dlerror()); + fprintf(stderr, "Could not open %s: %s\n", libgl_name, dlerror()); abort(); } } @@ -188,12 +191,20 @@ GLenum APIENTRY glGetError() void (*glXGetProcAddress(const GLubyte *procname))(void) { - void *handle = dlopen(NULL, RTLD_LAZY); - void (*ret)() = dlsym(handle, (const char *)procname); + void *handle = 0; + void (*ret)() = 0; + + if(glsym((const char *)procname)) + { + handle = dlopen(NULL, RTLD_LAZY); + ret = dlsym(handle, (const char *)procname); + } + begin_packet(FUNC_GLXGETPROCADDRESS); write_pointer(ret); - write_string(procname); + write_string((const char *)procname); send_packet(); + return ret; } diff --git a/source/profiler.cpp b/source/profiler.cpp new file mode 100644 index 0000000..ec9138d --- /dev/null +++ b/source/profiler.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include "profiler.h" + +using namespace Msp; + +Profiler::Profiler(): + decoder(gldecoder_new(this, 0)), + enabled(false) +{ + decoder->glDrawArrays = glDrawArrays; + decoder->glDrawElements = glDrawElements; + decoder->glDrawRangeElements = glDrawRangeElements; + decoder->glXSwapBuffers = glXSwapBuffers; +} + +void Profiler::enable() +{ + enabled = true; + frames = 0; + draw_calls = 0; + vertices = 0; + triangles = 0; + start = Msp::Time::now(); +} + +void Profiler::disable() +{ + enabled = false; +} + +int Profiler::decode(const char *data, unsigned len) +{ + if(enabled) + return gldecoder_decode(decoder, data, len); + else + return 0; +} + +void Profiler::glDrawArrays(void *user_data, GLenum mode, int, int count) +{ + glDrawElements(user_data, mode, count, 0, 0); +} + +void Profiler::glDrawElements(void *user_data, GLenum mode, int count, GLenum, const void *) +{ + Profiler *self = reinterpret_cast(user_data); + + ++self->draw_calls; + self->vertices += count; + + switch(mode) + { + case GL_TRIANGLES: + self->triangles += count/3; + break; + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + if(count>2) + self->triangles += count-2; + break; + case GL_QUADS: + self->triangles += count/4; + break; + case GL_QUAD_STRIP: + if(count>3) + self->triangles += count/2-1; + break; + } +} + +void Profiler::glDrawRangeElements(void *user_data, GLenum mode, unsigned, unsigned, int count, GLenum type, const void *data) +{ + glDrawElements(user_data, mode, count, type, data); +} + +void Profiler::glXSwapBuffers(void *user_data, Display *, GLXDrawable) +{ + Profiler *self = reinterpret_cast(user_data); + + IO::print("%d draw calls, %d vertices, %d triangles\n", self->draw_calls, self->vertices, self->triangles); + self->draw_calls = 0; + self->vertices = 0; + self->triangles = 0; + + ++self->frames; + Msp::Time::TimeStamp t = Msp::Time::now(); + Msp::Time::TimeDelta dt = t-self->start; + if(dt>Msp::Time::sec) + { + Msp::IO::print("%d frames in %s seconds\n", self->frames, dt); + self->start = t; + self->frames = 0; + } +} diff --git a/source/profiler.h b/source/profiler.h new file mode 100644 index 0000000..fa6bcac --- /dev/null +++ b/source/profiler.h @@ -0,0 +1,31 @@ +#ifndef PROFILER_H_ +#define PROFILER_H_ + +#include +#include "gldecoder.h" + +class Profiler +{ +private: + GlDecoder *decoder; + bool enabled; + Msp::Time::TimeStamp start; + unsigned frames; + unsigned draw_calls; + unsigned vertices; + unsigned triangles; + +public: + Profiler(); + + void enable(); + void disable(); + int decode(const char *, unsigned); +private: + static void glDrawArrays(void *, GLenum, int, int); + static void glDrawElements(void *, GLenum, int, GLenum, const void *); + static void glDrawRangeElements(void *, GLenum, unsigned, unsigned, int, GLenum, const void *); + static void glXSwapBuffers(void *, Display *, GLXDrawable); +}; + +#endif -- 2.43.0