]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
Improved profiling capabilities.
[apitrace] / retrace / glretrace_main.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <string.h>
28
29 #include "retrace.hpp"
30 #include "glproc.hpp"
31 #include "glstate.hpp"
32 #include "glretrace.hpp"
33 #include "os_time.hpp"
34
35
36 namespace glretrace {
37
38 bool insideList = false;
39 bool insideGlBeginEnd = false;
40
41 struct CallQuery
42 {
43     GLuint ids[3];
44     unsigned call;
45     GLuint program;
46     const trace::FunctionSig *sig;
47     uint64_t start;
48     uint64_t duration;
49 };
50
51 static bool firstFrame = true;
52 static std::list<CallQuery> callQueries;
53 static const int maxActiveCallQueries = 128;
54 static std::map<glws::Context*, GLuint> activePrograms;
55
56
57 void
58 checkGlError(trace::Call &call) {
59     GLenum error = glGetError();
60     if (error == GL_NO_ERROR) {
61         return;
62     }
63
64     std::ostream & os = retrace::warning(call);
65
66     os << "glGetError(";
67     os << call.name();
68     os << ") = ";
69
70     switch (error) {
71     case GL_INVALID_ENUM:
72         os << "GL_INVALID_ENUM";
73         break;
74     case GL_INVALID_VALUE:
75         os << "GL_INVALID_VALUE";
76         break;
77     case GL_INVALID_OPERATION:
78         os << "GL_INVALID_OPERATION";
79         break;
80     case GL_STACK_OVERFLOW:
81         os << "GL_STACK_OVERFLOW";
82         break;
83     case GL_STACK_UNDERFLOW:
84         os << "GL_STACK_UNDERFLOW";
85         break;
86     case GL_OUT_OF_MEMORY:
87         os << "GL_OUT_OF_MEMORY";
88         break;
89     case GL_INVALID_FRAMEBUFFER_OPERATION:
90         os << "GL_INVALID_FRAMEBUFFER_OPERATION";
91         break;
92     case GL_TABLE_TOO_LARGE:
93         os << "GL_TABLE_TOO_LARGE";
94         break;
95     default:
96         os << error;
97         break;
98     }
99     os << "\n";
100 }
101
102 static GLuint64
103 getGpuTimestamp() {
104     GLuint query = 0;
105     GLuint64 timestamp = 0;
106
107     if (retrace::profilingGpuTimes) {
108         glGenQueries(1, &query);
109         glQueryCounter(query, GL_TIMESTAMP);
110         glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT, &timestamp);
111         glDeleteQueries(1, &query);
112     }
113
114     return timestamp;
115 }
116
117 static GLuint64
118 getCpuTimestamp() {
119     if (retrace::profilingCpuTimes) {
120         return os::getTime() * (1.0E9 / os::timeFrequency);
121     } else {
122         return 0;
123     }
124 }
125
126 static void
127 completeCallQuery(CallQuery& query) {
128     /* Get call start and duration */
129     GLuint64 timestamp = 0, duration = 0, samples = 0;
130
131     if (retrace::profilingGpuTimes) {
132         glGetQueryObjectui64vEXT(query.ids[0], GL_QUERY_RESULT, &timestamp);
133         glGetQueryObjectui64vEXT(query.ids[1], GL_QUERY_RESULT, &duration);
134     }
135
136     if (retrace::profilingPixelsDrawn) {
137         glGetQueryObjectui64vEXT(query.ids[2], GL_QUERY_RESULT, &samples);
138     }
139
140     glDeleteQueries(3, query.ids);
141
142     /* Add call to profile */
143     retrace::profiler.addCall(query.call, query.sig->name, query.program, samples, timestamp, duration, query.start, query.duration);
144 }
145
146 void
147 flushQueries() {
148     for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
149         completeCallQuery(*itr);
150     }
151
152     callQueries.clear();
153 }
154
155 void setActiveProgram(GLuint program)
156 {
157     activePrograms[glretrace::currentContext] = program;
158 }
159
160 static GLuint
161 getActiveProgram()
162 {
163     std::map<glws::Context*, GLuint>::iterator it;
164     it = activePrograms.find(glretrace::currentContext);
165     if (it == activePrograms.end())
166         return 0;
167
168     return it->second;
169 }
170
171 void
172 beginProfile(trace::Call &call) {
173     if (firstFrame) {
174         frame_start();
175     }
176
177     /* Ensure we don't have TOO many queries waiting for results */
178     if (callQueries.size() >= maxActiveCallQueries) {
179         completeCallQuery(callQueries.front());
180         callQueries.pop_front();
181     }
182
183     /* Create call query */
184     CallQuery query;
185     query.call = call.no;
186     query.sig = call.sig;
187     query.program = getActiveProgram();
188
189     glGenQueries(3, query.ids);
190
191     if (retrace::profilingGpuTimes) {
192         glQueryCounter(query.ids[0], GL_TIMESTAMP);
193         glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
194     }
195
196     if (retrace::profilingPixelsDrawn) {
197         glBeginQuery(GL_SAMPLES_PASSED, query.ids[2]);
198     }
199
200     callQueries.push_back(query);
201
202     if (retrace::profilingCpuTimes) {
203         query.start = os::getTime();
204     }
205 }
206
207 void
208 endProfile(trace::Call &call) {
209     if (retrace::profilingCpuTimes) {
210         CallQuery& query = callQueries.back();
211         query.duration = (os::getTime() - query.start) * (1.0E9 / os::timeFrequency);
212     }
213
214     if (retrace::profilingGpuTimes) {
215         glEndQuery(GL_TIME_ELAPSED);
216     }
217
218     if (retrace::profilingPixelsDrawn) {
219         glEndQuery(GL_SAMPLES_PASSED);
220     }
221 }
222
223 void
224 frame_start() {
225     firstFrame = false;
226
227     if (retrace::profiling) {
228         retrace::profiler.addFrameStart(retrace::frameNo, getGpuTimestamp(), getCpuTimestamp());
229     }
230 }
231
232 void
233 frame_complete(trace::Call &call) {
234     if (retrace::profiling) {
235         /* Complete any remaining queries */
236         flushQueries();
237
238         /* Indicate end of current frame */
239         retrace::profiler.addFrameEnd(getGpuTimestamp(), getCpuTimestamp());
240     }
241
242     retrace::frameComplete(call);
243
244     /* Indicate start of next frame */
245     frame_start();
246
247     if (!currentDrawable) {
248         return;
249     }
250
251     if (retrace::debug && !currentDrawable->visible) {
252         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
253     }
254 }
255
256 } /* namespace glretrace */
257
258
259 void
260 retrace::setUp(void) {
261     glws::init();
262 }
263
264
265 void
266 retrace::addCallbacks(retrace::Retracer &retracer)
267 {
268     retracer.addCallbacks(glretrace::gl_callbacks);
269     retracer.addCallbacks(glretrace::glx_callbacks);
270     retracer.addCallbacks(glretrace::wgl_callbacks);
271     retracer.addCallbacks(glretrace::cgl_callbacks);
272     retracer.addCallbacks(glretrace::egl_callbacks);
273 }
274
275
276 image::Image *
277 retrace::getSnapshot(void) {
278     if (!glretrace::currentDrawable) {
279         return NULL;
280     }
281
282     return glstate::getDrawBufferImage();
283 }
284
285
286 bool
287 retrace::dumpState(std::ostream &os)
288 {
289     if (glretrace::insideGlBeginEnd ||
290         !glretrace::currentDrawable ||
291         !glretrace::currentContext) {
292         return false;
293     }
294
295     glstate::dumpCurrentContext(os);
296
297     return true;
298 }
299
300 void
301 retrace::flushRendering(void) {
302     glretrace::flushQueries();
303     glFlush();
304 }
305
306 void
307 retrace::waitForInput(void) {
308     while (glws::processEvents()) {
309     }
310 }
311
312 void
313 retrace::cleanUp(void) {
314     glws::cleanup();
315 }