/* $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
*/
#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)
{
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;
{
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:;
}
}
{
int breakpoint;
+ if(no_break)
+ return;
+
receive();
breakpoint = (breakpoints[func]|break_any)&flag;
--- /dev/null
+/* $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]);
+ }
+}
--- /dev/null
+/* $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