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