]> git.tdb.fi Git - gldbg.git/commitdiff
Add gldQueryViewport and gldReadPixels calls
authorMikko Rasa <tdb@tdb.fi>
Wed, 12 Jan 2011 12:04:32 +0000 (12:04 +0000)
committerMikko Rasa <tdb@tdb.fi>
Wed, 12 Jan 2011 12:04:32 +0000 (12:04 +0000)
Add GL_VIEWPORT to arraysize.c
Add frame grabber tool

Makefile
flavors/gl/flavor.mk
flavors/gl/source/arraysize.c
flavors/gl/source/grabber_flavor.cpp [new file with mode: 0644]
flavors/gles2/flavor.mk
flavors/gles2/source/arraysize.c
flavors/gles2/source/grabber_flavor.cpp [new file with mode: 0644]
source/functions.enum.t
source/glwrap.c
source/grabber.cpp [new file with mode: 0644]
source/grabber.h [new file with mode: 0644]

index 010fe2851ec37281753482d8d565ccd0cc001240..4b171977e024674198ffa12efe6a22c34d90774f 100644 (file)
--- 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 \
index ae074f32c6c69686a179c056d302bc80f645807c..458f780c9b86bf53e16399f7d3b95f40dc2db4e2 100644 (file)
@@ -1,4 +1,4 @@
 # $Id$
 
 APIS := $(FLAVOR_ROOT)/gl.api $(FLAVOR_ROOT)/glx.api
-TOOLS := tracer profiler inspector
+TOOLS := tracer profiler inspector grabber
index 00d6a794d3e708fd1c9400a1402f3fec9a112392..e83ab63df413a60e66e938022fcf31735f76d5a0 100644 (file)
@@ -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 (file)
index 0000000..ef017d5
--- /dev/null
@@ -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 (Grabber::*next_frame)()>
+void glXSwapBuffers(void *user_data, Display *, GLXDrawable)
+{
+       (reinterpret_cast<Grabber *>(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>;
+}
index d6ab2cb50f22297467759c14d39c85062ad33ea4..22328215fb8bf444824c68b2bf31ea2f9a9bda9e 100644 (file)
@@ -2,4 +2,4 @@
 
 CPPFLAGS += -DAPIENTRY=
 APIS := $(FLAVOR_ROOT)/gles2.api $(FLAVOR_ROOT)/egl.api
-TOOLS := tracer
+TOOLS := tracer grabber
index 076347f1ec9b433ab50ba553a78bd8cf5c76edde..fe2648421e0db6dc7be42a42da11c69045f613b6 100644 (file)
@@ -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 (file)
index 0000000..94986fd
--- /dev/null
@@ -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 (Grabber::*next_frame)()>
+void eglSwapBuffers(void *user_data, unsigned, void *, void *)
+{
+       (reinterpret_cast<Grabber *>(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>;
+}
index c1c0db7a927e9779ec7a30b4b245e8f096a6fd63..3c3a39ddfb313afa0946550ca8cb815539b084ca 100644 (file)
@@ -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
 :};
index c121e22709c819eb3e9040245818e419023bd49e..970e61923a611754e555d2901c382e322513e21e 100644 (file)
@@ -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 <dlfcn.h>
 #include <fcntl.h>
 #include <signal.h>
+#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 (file)
index 0000000..e56622f
--- /dev/null
@@ -0,0 +1,248 @@
+/* $Id$
+
+This file is part of gldbg
+Copyright © 2011  Mikko Rasa, Mikkosoft Productions
+Distributed under the GPL
+*/
+
+#include <algorithm>
+#include <png.h>
+#include <msp/io/print.h>
+#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<png_byte *> row_ptrs(height);
+       for(unsigned i=0; i<height; ++i)
+               row_ptrs[i] = reinterpret_cast<png_byte *>(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<Grabber *>(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<Grabber *>(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<Grabber *>(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<Grabber *>(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 (file)
index 0000000..822c584
--- /dev/null
@@ -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 <string>
+#include "gldecoder.h"
+#include "tool.h"
+
+class Grabber: public RegisteredTool<Grabber>
+{
+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