]> git.tdb.fi Git - gldbg.git/blob - source/grabber.cpp
Add an errata system to avoid the need of putting special cases in code
[gldbg.git] / source / grabber.cpp
1 /* $Id$
2
3 This file is part of gldbg
4 Copyright © 2011  Mikko Rasa, Mikkosoft Productions
5 Distributed under the GPL
6 */
7
8 #include <algorithm>
9 #include <stdexcept>
10 #include <png.h>
11 #include "arraysize.h"
12 #include "breakpoint.h"
13 #include "functions.h"
14 #include "gldbg.h"
15 #include "grabber.h"
16 #include "strformat.h"
17
18 using namespace std;
19
20 Grabber::Grabber(GlDbg &g):
21         gldbg(g),
22         decoder(gldecoder_new(this, NULL)),
23         frame_num(1),
24         grab_num(1),
25         pending_grab(0),
26         autograb(false)
27 {
28         fill(viewport, viewport+4, 0);
29
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",
34                         "autograb frame\n"
35                         "  Grab at the end of every frame\n\n"
36                         "autograb draw\n"
37                         "  Grab after every draw call\n\n"
38                         "autograb off\n"
39                         "  Turn off automatic grabbing\n");
40
41         decoder->glViewport = glViewport;
42         decoder->glGetIntegerv = glGetIntegerv;
43         decoder->glReadPixels = glReadPixels;
44         decoder->gldBreak = gldBreak;
45
46         flavor_init();
47 }
48
49 void Grabber::decode(const char *data, unsigned len)
50 {
51         gldecoder_decode(decoder, data, len);
52 }
53
54 void Grabber::process_started()
55 {
56         if(autograb)
57         {
58                 gldbg.set_breakpoint(break_funcs[0], BREAK_CALL, this);
59                 if(autograb==2)
60                 {
61                         for(unsigned i=1; break_funcs[i]; ++i)
62                                 gldbg.set_breakpoint(break_funcs[i], BREAK_RETURN, this);
63                 }
64         }
65
66         frame_num = 1;
67         grab_num = 1;
68 }
69
70 void Grabber::process_stopped(int reason)
71 {
72         if((reason>>8)==3 && pending_grab)
73         {
74                 grab();
75                 if(pending_grab==2)
76                 {
77                         gldbg.hold();
78                         gldbg.get_process().resume();
79                 }
80                 else
81                         gldbg.resume_from_break(this);
82                 pending_grab = 0;
83         }
84 }
85
86 void Grabber::cmd_grab(const string &)
87 {
88         grab();
89         if(pending_grab)
90                 ++pending_grab;
91         else
92         {
93                 gldbg.hold();
94                 gldbg.get_process().resume();
95         }
96 }
97
98 void Grabber::cmd_autograb(const string &args)
99 {
100         if(args=="off")
101         {
102                 autograb = 0;
103                 if(gldbg.get_process().get_state()!=Process::INACTIVE)
104                 {
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);
108                 }
109         }
110         else if(args=="frame")
111         {
112                 autograb = 1;
113                 if(gldbg.get_process().get_state()!=Process::INACTIVE)
114                         gldbg.set_breakpoint(break_funcs[0], BREAK_CALL, this);
115         }
116         else if(args=="draw")
117         {
118                 autograb = 2;
119                 if(gldbg.get_process().get_state()!=Process::INACTIVE)
120                 {
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);
124                 }
125         }
126         else
127                 throw runtime_error("Invalid autograb mode");
128 }
129
130 void Grabber::grab()
131 {
132         if(!viewport[2] || !viewport[3])
133         {
134                 GlPacket *pkt = packet_begin(FUNC_GLDQUERYVIEWPORT);
135                 gldbg.send(pkt);
136
137                 gldbg.hold();
138                 gldbg.get_process().resume();
139
140                 pending_grab = 1;
141                 return;
142         }
143
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);
149         gldbg.send(pkt);
150 }
151
152 int Grabber::write_png(const string &fn, unsigned width, unsigned height, void *data)
153 {
154         FILE *out = fopen(fn.c_str(), "w");
155         if(!out)
156                 return -1;
157
158         png_struct *png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
159         if(!png)
160         {
161                 fclose(out);
162                 return -1;
163         }
164
165         png_info *info = png_create_info_struct(png);
166         if(!info)
167         {
168                 png_destroy_write_struct(&png, 0);
169                 fclose(out);
170                 return -1;
171         }
172
173         if(setjmp(png_jmpbuf(png)))
174         {
175                 png_destroy_write_struct(&png, &info);
176                 fclose(out);
177                 return -1;
178         }
179
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);
182
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());
187
188         png_write_png(png, info, PNG_TRANSFORM_IDENTITY, 0);
189         png_write_end(png, info);
190
191         png_destroy_write_struct(&png, &info);
192         fclose(out);
193
194         return 0;
195 }
196
197 void Grabber::next_frame()
198 {
199         ++frame_num;
200         grab_num = 1;
201 }
202
203 void Grabber::glViewport(void *user_data, int x, int y, int width, int height)
204 {
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;
210 }
211
212 void Grabber::glGetIntegerv(void *user_data, GLenum pname, int *params)
213 {
214         Grabber *self = reinterpret_cast<Grabber *>(user_data);
215         if(pname==GL_VIEWPORT)
216                 copy(params, params+4, self->viewport);
217 }
218
219 void Grabber::glReadPixels(void *user_data, int, int, int width, int height, GLenum format, GLenum type, void *data)
220 {
221         Grabber *self = reinterpret_cast<Grabber *>(user_data);
222         
223         string fn = strformat("grab-%04d-%04d.png", self->frame_num, self->grab_num);
224         if(format==GL_RGBA && type==GL_UNSIGNED_BYTE)
225         {
226                 if(self->write_png(fn, width, height, data)<0)
227                         printf("Failed to write png file %s\n", fn.c_str());
228                 else
229                         printf("Grabbed %dx%d png image to %s\n", width, height, fn.c_str());
230         }
231
232         ++self->grab_num;
233 }
234
235 void Grabber::gldBreak(void *user_data, unsigned short func, unsigned char flag)
236 {
237         Grabber *self = reinterpret_cast<Grabber *>(user_data);
238         if(!self->autograb)
239                 return;
240
241         if(flag==BREAK_CALL && func==break_funcs[0])
242                 self->pending_grab = 1;
243         else if(flag==BREAK_RETURN && self->autograb>=2)
244         {
245                 for(unsigned i=1; (!self->pending_grab && break_funcs[i]); ++i)
246                         self->pending_grab = (func==break_funcs[i]);
247         }
248 }