From a51c2557622ea93944e24f58845609526eb46ec1 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 12 Jan 2011 12:04:32 +0000 Subject: [PATCH] Add gldQueryViewport and gldReadPixels calls Add GL_VIEWPORT to arraysize.c Add frame grabber tool --- Makefile | 4 +- flavors/gl/flavor.mk | 2 +- flavors/gl/source/arraysize.c | 5 +- flavors/gl/source/grabber_flavor.cpp | 34 ++++ flavors/gles2/flavor.mk | 2 +- flavors/gles2/source/arraysize.c | 5 +- flavors/gles2/source/grabber_flavor.cpp | 32 +++ source/functions.enum.t | 4 +- source/glwrap.c | 44 ++++- source/grabber.cpp | 248 ++++++++++++++++++++++++ source/grabber.h | 51 +++++ 11 files changed, 423 insertions(+), 8 deletions(-) create mode 100644 flavors/gl/source/grabber_flavor.cpp create mode 100644 flavors/gles2/source/grabber_flavor.cpp create mode 100644 source/grabber.cpp create mode 100644 source/grabber.h diff --git a/Makefile b/Makefile index 010fe28..4b17197 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ CPPFLAGS := -I. -Isource -Iflavors/$(FLAVOR)/source CFLAGS := -ggdb -Wall -Wextra CXXFLAGS := $(CFLAGS) -PACKAGES_gldbg := mspcore mspstrings mspio mspfs +PACKAGES_gldbg := mspcore mspstrings mspio mspfs libpng12 FLAVOR_ROOT := flavors/$(FLAVOR) VPATH := $(FLAVOR_ROOT) @@ -37,6 +37,8 @@ SOURCES_inspector := source/inspector.cpp \ source/arraystate.cpp \ source/texturestate.cpp \ source/bufferstate.cpp +SOURCES_grabber := source/grabber.cpp \ + source/grabber_flavor.cpp TEMPLATES := source/functions.enum.t \ source/functions.table.t \ source/gldecoder.funcs.t \ diff --git a/flavors/gl/flavor.mk b/flavors/gl/flavor.mk index ae074f3..458f780 100644 --- a/flavors/gl/flavor.mk +++ b/flavors/gl/flavor.mk @@ -1,4 +1,4 @@ # $Id$ APIS := $(FLAVOR_ROOT)/gl.api $(FLAVOR_ROOT)/glx.api -TOOLS := tracer profiler inspector +TOOLS := tracer profiler inspector grabber diff --git a/flavors/gl/source/arraysize.c b/flavors/gl/source/arraysize.c index 00d6a79..e83ab63 100644 --- a/flavors/gl/source/arraysize.c +++ b/flavors/gl/source/arraysize.c @@ -1,7 +1,7 @@ /* $Id$ This file is part of gldbg -Copyright © 2009-2010 Mikko Rasa, Mikkosoft Productions +Copyright © 2009-2011 Mikko Rasa, Mikkosoft Productions Distributed under the GPL */ @@ -49,6 +49,8 @@ int paramsize(GLenum pname) { switch(pname) { + case GL_VIEWPORT: return 4; + // Lighting and material case GL_AMBIENT: return 4; case GL_DIFFUSE: return 4; @@ -85,6 +87,7 @@ int paramsize(GLenum pname) case GL_TEXTURE_COMPARE_MODE: return 1; case GL_TEXTURE_COMPARE_FUNC: return 1; case GL_GENERATE_MIPMAP: return 1; + default: return 1; } } diff --git a/flavors/gl/source/grabber_flavor.cpp b/flavors/gl/source/grabber_flavor.cpp new file mode 100644 index 0000000..ef017d5 --- /dev/null +++ b/flavors/gl/source/grabber_flavor.cpp @@ -0,0 +1,34 @@ +/* $Id$ + +This file is part of gldbg +Copyright © 2011 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#include "functions.h" +#include "grabber.h" + +namespace { + +template +void glXSwapBuffers(void *user_data, Display *, GLXDrawable) +{ + (reinterpret_cast(user_data)->*next_frame)(); +} + +} + +unsigned short Grabber::break_funcs[] = +{ + FUNC_GLXSWAPBUFFERS, + FUNC_GLEND, + FUNC_GLDRAWARRAYS, + FUNC_GLDRAWELEMENTS, + FUNC_GLDRAWRANGEELEMENTS, + 0 +}; + +void Grabber::flavor_init() +{ + decoder->glXSwapBuffers = glXSwapBuffers<&Grabber::next_frame>; +} diff --git a/flavors/gles2/flavor.mk b/flavors/gles2/flavor.mk index d6ab2cb..2232821 100644 --- a/flavors/gles2/flavor.mk +++ b/flavors/gles2/flavor.mk @@ -2,4 +2,4 @@ CPPFLAGS += -DAPIENTRY= APIS := $(FLAVOR_ROOT)/gles2.api $(FLAVOR_ROOT)/egl.api -TOOLS := tracer +TOOLS := tracer grabber diff --git a/flavors/gles2/source/arraysize.c b/flavors/gles2/source/arraysize.c index 076347f..fe26484 100644 --- a/flavors/gles2/source/arraysize.c +++ b/flavors/gles2/source/arraysize.c @@ -1,7 +1,7 @@ /* $Id$ This file is part of gldbg -Copyright © 2010 Mikko Rasa, Mikkosoft Productions +Copyright © 2010-2011 Mikko Rasa, Mikkosoft Productions Distributed under the GPL */ @@ -42,11 +42,14 @@ int paramsize(GLenum pname) { switch(pname) { + case GL_VIEWPORT: return 4; + // Texture case GL_TEXTURE_WRAP_S: return 1; case GL_TEXTURE_WRAP_T: return 1; case GL_TEXTURE_MIN_FILTER: return 1; case GL_TEXTURE_MAG_FILTER: return 1; + default: return 1; } } diff --git a/flavors/gles2/source/grabber_flavor.cpp b/flavors/gles2/source/grabber_flavor.cpp new file mode 100644 index 0000000..94986fd --- /dev/null +++ b/flavors/gles2/source/grabber_flavor.cpp @@ -0,0 +1,32 @@ +/* $Id$ + +This file is part of gldbg +Copyright © 2011 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#include "functions.h" +#include "grabber.h" + +namespace { + +template +void eglSwapBuffers(void *user_data, unsigned, void *, void *) +{ + (reinterpret_cast(user_data)->*next_frame)(); +} + +} + +unsigned short Grabber::break_funcs[] = +{ + FUNC_EGLSWAPBUFFERS, + FUNC_GLDRAWARRAYS, + FUNC_GLDRAWELEMENTS, + 0 +}; + +void Grabber::flavor_init() +{ + decoder->eglSwapBuffers = eglSwapBuffers<&Grabber::next_frame>; +} diff --git a/source/functions.enum.t b/source/functions.enum.t index c1c0db7..3c3a39d 100644 --- a/source/functions.enum.t +++ b/source/functions.enum.t @@ -6,5 +6,7 @@ wl(' FUNC_%s,', func.name.upper()) : N_FUNCS, : FUNC_GLDERROR = 0x8000, : FUNC_GLDBREAK, -: FUNC_GLDHOLD +: FUNC_GLDHOLD, +: FUNC_GLDQUERYVIEWPORT, +: FUNC_GLDREADPIXELS :}; diff --git a/source/glwrap.c b/source/glwrap.c index c121e22..970e619 100644 --- a/source/glwrap.c +++ b/source/glwrap.c @@ -1,7 +1,7 @@ /* $Id$ This file is part of gldbg -Copyright © 2009-2010 Mikko Rasa, Mikkosoft Productions +Copyright © 2009-2011 Mikko Rasa, Mikkosoft Productions Distributed under the GPL */ @@ -12,13 +12,18 @@ Distributed under the GPL #include #include #include +#include "arraysize.h" #include "breakpoint.h" #include "functions.h" +#include "opengl.h" #include "packet.h" +#define UNUSED __attribute__((unused)) + static unsigned char breakpoints[N_FUNCS] = { }; static char break_any = 0; static char hold = 0; +static char no_break = 0; static const char *get_lib_names(void) { @@ -146,11 +151,41 @@ static void receive_gldBreak(GlPacket *pkt) break_any = flags_set; } -static void receive_gldHold(GlPacket *pkt __attribute__((unused))) +static void receive_gldHold(GlPacket *pkt UNUSED) { hold = 1; } +static void receive_gldQueryViewport(GlPacket *pkt UNUSED) +{ + int viewport[4]; + + no_break = 1; + glGetIntegerv(GL_VIEWPORT, viewport); + no_break = 0; +} + +static void receive_gldReadPixels(GlPacket *pkt) +{ + GLint x, y; + GLsizei width, height; + GLenum format, type; + char *data; + + packet_read_int(pkt, &x); + packet_read_int(pkt, &y); + packet_read_int(pkt, &width); + packet_read_int(pkt, &height); + packet_read_int(pkt, (int *)&format); + packet_read_int(pkt, (int *)&type); + + data = (char *)malloc(width*height*typesize(type)*formatsize(format)); + no_break = 1; + glReadPixels(x, y, width, height, format, type, data); + no_break = 0; + free(data); +} + static void receive(void) { GlPacket *pkt; @@ -164,6 +199,8 @@ static void receive(void) { case FUNC_GLDBREAK: receive_gldBreak(pkt); break; case FUNC_GLDHOLD: receive_gldHold(pkt); break; + case FUNC_GLDQUERYVIEWPORT: receive_gldQueryViewport(pkt); break; + case FUNC_GLDREADPIXELS: receive_gldReadPixels(pkt); break; default:; } } @@ -173,6 +210,9 @@ void tracepoint(unsigned short func, int flag) { int breakpoint; + if(no_break) + return; + receive(); breakpoint = (breakpoints[func]|break_any)&flag; diff --git a/source/grabber.cpp b/source/grabber.cpp new file mode 100644 index 0000000..e56622f --- /dev/null +++ b/source/grabber.cpp @@ -0,0 +1,248 @@ +/* $Id$ + +This file is part of gldbg +Copyright © 2011 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#include +#include +#include +#include "arraysize.h" +#include "breakpoint.h" +#include "functions.h" +#include "gldbg.h" +#include "grabber.h" + +using namespace std; +using namespace Msp; + +Grabber::Grabber(GlDbg &g): + gldbg(g), + decoder(gldecoder_new(this, NULL)), + frame_num(1), + grab_num(1), + pending_grab(0), + autograb(false) +{ + fill(viewport, viewport+4, 0); + + gldbg.get_command_interpreter().register_command("grab", this, &Grabber::cmd_grab) + .set_help("Grab framebuffer contents"); + gldbg.get_command_interpreter().register_command("autograb", this, &Grabber::cmd_autograb) + .set_help("Automatically grab framebuffer contents", + "autograb frame\n" + " Grab at the end of every frame\n\n" + "autograb draw\n" + " Grab after every draw call\n\n" + "autograb off\n" + " Turn off automatic grabbing\n"); + + decoder->glViewport = glViewport; + decoder->glGetIntegerv = glGetIntegerv; + decoder->glReadPixels = glReadPixels; + decoder->gldBreak = gldBreak; + + flavor_init(); +} + +void Grabber::decode(const char *data, unsigned len) +{ + gldecoder_decode(decoder, data, len); +} + +void Grabber::process_started() +{ + if(autograb) + { + gldbg.set_breakpoint(break_funcs[0], BREAK_CALL, this); + if(autograb==2) + { + for(unsigned i=1; break_funcs[i]; ++i) + gldbg.set_breakpoint(break_funcs[i], BREAK_RETURN, this); + } + } + + frame_num = 1; + grab_num = 1; +} + +void Grabber::process_stopped(int reason) +{ + if((reason>>8)==3 && pending_grab) + { + grab(); + if(pending_grab==2) + { + gldbg.hold(); + gldbg.get_process().resume(); + } + else + gldbg.resume_from_break(this); + pending_grab = 0; + } +} + +void Grabber::cmd_grab(const string &) +{ + grab(); + if(pending_grab) + ++pending_grab; + else + { + gldbg.hold(); + gldbg.get_process().resume(); + } +} + +void Grabber::cmd_autograb(const string &args) +{ + if(args=="off") + { + autograb = 0; + if(gldbg.get_process().get_state()!=Process::INACTIVE) + { + gldbg.clear_breakpoint(break_funcs[0], BREAK_CALL, this); + for(unsigned i=1; break_funcs[i]; ++i) + gldbg.clear_breakpoint(break_funcs[i], BREAK_RETURN, this); + } + } + else if(args=="frame") + { + autograb = 1; + if(gldbg.get_process().get_state()!=Process::INACTIVE) + gldbg.set_breakpoint(break_funcs[0], BREAK_CALL, this); + } + else if(args=="draw") + { + autograb = 2; + if(gldbg.get_process().get_state()!=Process::INACTIVE) + { + gldbg.set_breakpoint(break_funcs[0], BREAK_CALL, this); + for(unsigned i=1; break_funcs[i]; ++i) + gldbg.set_breakpoint(break_funcs[i], BREAK_RETURN, this); + } + } + else + throw InvalidParameterValue("Invalid autograb mode"); +} + +void Grabber::grab() +{ + if(!viewport[2] || !viewport[3]) + { + GlPacket *pkt = packet_begin(FUNC_GLDQUERYVIEWPORT); + gldbg.send(pkt); + + gldbg.hold(); + gldbg.get_process().resume(); + + pending_grab = 1; + return; + } + + GlPacket *pkt = packet_begin(FUNC_GLDREADPIXELS); + for(unsigned i=0; i<4; ++i) + packet_write_int(pkt, viewport[i]); + packet_write_int(pkt, GL_RGBA); + packet_write_int(pkt, GL_UNSIGNED_BYTE); + gldbg.send(pkt); +} + +int Grabber::write_png(const string &fn, unsigned width, unsigned height, void *data) +{ + FILE *out = fopen(fn.c_str(), "w"); + if(!out) + return -1; + + png_struct *png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if(!png) + { + fclose(out); + return -1; + } + + png_info *info = png_create_info_struct(png); + if(!info) + { + png_destroy_write_struct(&png, 0); + fclose(out); + return -1; + } + + if(setjmp(png_jmpbuf(png))) + { + png_destroy_write_struct(&png, &info); + fclose(out); + return -1; + } + + png_init_io(png, out); + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + vector row_ptrs(height); + for(unsigned i=0; i(data)+(height-1-i)*width*4; + png_set_rows(png, info, &row_ptrs.front()); + + png_write_png(png, info, PNG_TRANSFORM_IDENTITY, 0); + png_write_end(png, info); + + png_destroy_write_struct(&png, &info); + fclose(out); + + return 0; +} + +void Grabber::next_frame() +{ + ++frame_num; + grab_num = 1; +} + +void Grabber::glViewport(void *user_data, int x, int y, int width, int height) +{ + Grabber *self = reinterpret_cast(user_data); + self->viewport[0] = x; + self->viewport[1] = y; + self->viewport[2] = width; + self->viewport[3] = height; +} + +void Grabber::glGetIntegerv(void *user_data, GLenum pname, int *params) +{ + Grabber *self = reinterpret_cast(user_data); + if(pname==GL_VIEWPORT) + copy(params, params+4, self->viewport); +} + +void Grabber::glReadPixels(void *user_data, int, int, int width, int height, GLenum format, GLenum type, void *data) +{ + Grabber *self = reinterpret_cast(user_data); + + string fn = Msp::format("grab-%04d-%04d.png", self->frame_num, self->grab_num); + if(format==GL_RGBA && type==GL_UNSIGNED_BYTE) + { + if(self->write_png(fn, width, height, data)<0) + IO::print("Failed to write png file %s\n", fn); + else + IO::print("Grabbed %dx%d png image to %s\n", width, height, fn); + } + + ++self->grab_num; +} + +void Grabber::gldBreak(void *user_data, unsigned short func, unsigned char flag) +{ + Grabber *self = reinterpret_cast(user_data); + if(!self->autograb) + return; + + if(flag==BREAK_CALL && func==break_funcs[0]) + self->pending_grab = 1; + else if(flag==BREAK_RETURN && self->autograb>=2) + { + for(unsigned i=1; (!self->pending_grab && break_funcs[i]); ++i) + self->pending_grab = (func==break_funcs[i]); + } +} diff --git a/source/grabber.h b/source/grabber.h new file mode 100644 index 0000000..822c584 --- /dev/null +++ b/source/grabber.h @@ -0,0 +1,51 @@ +/* $Id$ + +This file is part of gldbg +Copyright © 2011 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#ifndef GRABBERBASE_H_ +#define GRABBERBASE_H_ + +#include +#include "gldecoder.h" +#include "tool.h" + +class Grabber: public RegisteredTool +{ +private: + GlDbg &gldbg; + GlDecoder *decoder; + int viewport[4]; + unsigned frame_num; + unsigned grab_num; + int pending_grab; + int autograb; + + static unsigned short break_funcs[]; + +public: + Grabber(GlDbg &); +private: + void flavor_init(); + +public: + virtual void decode(const char *, unsigned); + virtual void process_started(); + virtual void process_stopped(int); +private: + void cmd_grab(const std::string &); + void cmd_autograb(const std::string &); + + void grab(); + int write_png(const std::string &, unsigned, unsigned, void *); + void next_frame(); + + static void glViewport(void *, int, int, int, int); + static void glGetIntegerv(void *, GLenum, int *); + static void glReadPixels(void *, int, int, int, int, GLenum, GLenum, void *); + static void gldBreak(void *, unsigned short, unsigned char); +}; + +#endif -- 2.43.0