X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Fgrabber.cpp;fp=source%2Fgrabber.cpp;h=e56622f4d9949f21936358550a278d7a023e7cbd;hb=a51c2557622ea93944e24f58845609526eb46ec1;hp=0000000000000000000000000000000000000000;hpb=6d297b506314c07bff3d77c2853a5f59380cfcb0;p=gldbg.git 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]); + } +}