3 This file is part of gldbg
4 Copyright © 2011 Mikko Rasa, Mikkosoft Productions
5 Distributed under the GPL
11 #include "arraysize.h"
12 #include "breakpoint.h"
13 #include "functions.h"
16 #include "strformat.h"
20 Grabber::Grabber(GlDbg &g):
22 decoder(gldecoder_new(this, NULL)),
28 fill(viewport, viewport+4, 0);
30 gldbg.get_command_interpreter().register_command("grab", this, &Grabber::cmd_grab)
31 .set_help("Grab framebuffer contents");
32 gldbg.get_command_interpreter().register_command("autograb", this, &Grabber::cmd_autograb)
33 .set_help("Automatically grab framebuffer contents",
35 " Grab at the end of every frame\n\n"
37 " Grab after every draw call\n\n"
39 " Turn off automatic grabbing\n");
41 decoder->glViewport = glViewport;
42 decoder->glGetIntegerv = glGetIntegerv;
43 decoder->glReadPixels = glReadPixels;
44 decoder->gldBreak = gldBreak;
49 void Grabber::decode(const char *data, unsigned len)
51 gldecoder_decode(decoder, data, len);
54 void Grabber::process_started()
58 gldbg.set_breakpoint(break_funcs[0], BREAK_CALL, this);
61 for(unsigned i=1; break_funcs[i]; ++i)
62 gldbg.set_breakpoint(break_funcs[i], BREAK_RETURN, this);
70 void Grabber::process_stopped(int reason)
72 if((reason>>8)==3 && pending_grab)
78 gldbg.get_process().resume();
81 gldbg.resume_from_break(this);
86 void Grabber::cmd_grab(const string &)
94 gldbg.get_process().resume();
98 void Grabber::cmd_autograb(const string &args)
103 if(gldbg.get_process().get_state()!=Process::INACTIVE)
105 gldbg.clear_breakpoint(break_funcs[0], BREAK_CALL, this);
106 for(unsigned i=1; break_funcs[i]; ++i)
107 gldbg.clear_breakpoint(break_funcs[i], BREAK_RETURN, this);
110 else if(args=="frame")
113 if(gldbg.get_process().get_state()!=Process::INACTIVE)
114 gldbg.set_breakpoint(break_funcs[0], BREAK_CALL, this);
116 else if(args=="draw")
119 if(gldbg.get_process().get_state()!=Process::INACTIVE)
121 gldbg.set_breakpoint(break_funcs[0], BREAK_CALL, this);
122 for(unsigned i=1; break_funcs[i]; ++i)
123 gldbg.set_breakpoint(break_funcs[i], BREAK_RETURN, this);
127 throw runtime_error("Invalid autograb mode");
132 if(!viewport[2] || !viewport[3])
134 GlPacket *pkt = packet_begin(FUNC_GLDQUERYVIEWPORT);
138 gldbg.get_process().resume();
144 GlPacket *pkt = packet_begin(FUNC_GLDREADPIXELS);
145 for(unsigned i=0; i<4; ++i)
146 packet_write_int(pkt, viewport[i]);
147 packet_write_int(pkt, GL_RGBA);
148 packet_write_int(pkt, GL_UNSIGNED_BYTE);
152 int Grabber::write_png(const string &fn, unsigned width, unsigned height, void *data)
154 FILE *out = fopen(fn.c_str(), "w");
158 png_struct *png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
165 png_info *info = png_create_info_struct(png);
168 png_destroy_write_struct(&png, 0);
173 if(setjmp(png_jmpbuf(png)))
175 png_destroy_write_struct(&png, &info);
180 png_init_io(png, out);
181 png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
183 vector<png_byte *> row_ptrs(height);
184 for(unsigned i=0; i<height; ++i)
185 row_ptrs[i] = reinterpret_cast<png_byte *>(data)+(height-1-i)*width*4;
186 png_set_rows(png, info, &row_ptrs.front());
188 png_write_png(png, info, PNG_TRANSFORM_IDENTITY, 0);
189 png_write_end(png, info);
191 png_destroy_write_struct(&png, &info);
197 void Grabber::next_frame()
203 void Grabber::glViewport(void *user_data, int x, int y, int width, int height)
205 Grabber *self = reinterpret_cast<Grabber *>(user_data);
206 self->viewport[0] = x;
207 self->viewport[1] = y;
208 self->viewport[2] = width;
209 self->viewport[3] = height;
212 void Grabber::glGetIntegerv(void *user_data, GLenum pname, int *params)
214 Grabber *self = reinterpret_cast<Grabber *>(user_data);
215 if(pname==GL_VIEWPORT)
216 copy(params, params+4, self->viewport);
219 void Grabber::glReadPixels(void *user_data, int, int, int width, int height, GLenum format, GLenum type, void *data)
221 Grabber *self = reinterpret_cast<Grabber *>(user_data);
223 string fn = strformat("grab-%04d-%04d.png", self->frame_num, self->grab_num);
224 if(format==GL_RGBA && type==GL_UNSIGNED_BYTE)
226 if(self->write_png(fn, width, height, data)<0)
227 printf("Failed to write png file %s\n", fn.c_str());
229 printf("Grabbed %dx%d png image to %s\n", width, height, fn.c_str());
235 void Grabber::gldBreak(void *user_data, unsigned short func, unsigned char flag)
237 Grabber *self = reinterpret_cast<Grabber *>(user_data);
241 if(flag==BREAK_CALL && func==break_funcs[0])
242 self->pending_grab = 1;
243 else if(flag==BREAK_RETURN && self->autograb>=2)
245 for(unsigned i=1; (!self->pending_grab && break_funcs[i]); ++i)
246 self->pending_grab = (func==break_funcs[i]);