]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
Improved GPU profiling code.
[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
34
35 namespace glretrace {
36
37 bool insideList = false;
38 bool insideGlBeginEnd = false;
39
40
41 void
42 checkGlError(trace::Call &call) {
43     GLenum error = glGetError();
44     if (error == GL_NO_ERROR) {
45         return;
46     }
47
48     std::ostream & os = retrace::warning(call);
49
50     os << "glGetError(";
51     os << call.name();
52     os << ") = ";
53
54     switch (error) {
55     case GL_INVALID_ENUM:
56         os << "GL_INVALID_ENUM";
57         break;
58     case GL_INVALID_VALUE:
59         os << "GL_INVALID_VALUE";
60         break;
61     case GL_INVALID_OPERATION:
62         os << "GL_INVALID_OPERATION";
63         break;
64     case GL_STACK_OVERFLOW:
65         os << "GL_STACK_OVERFLOW";
66         break;
67     case GL_STACK_UNDERFLOW:
68         os << "GL_STACK_UNDERFLOW";
69         break;
70     case GL_OUT_OF_MEMORY:
71         os << "GL_OUT_OF_MEMORY";
72         break;
73     case GL_INVALID_FRAMEBUFFER_OPERATION:
74         os << "GL_INVALID_FRAMEBUFFER_OPERATION";
75         break;
76     case GL_TABLE_TOO_LARGE:
77         os << "GL_TABLE_TOO_LARGE";
78         break;
79     default:
80         os << error;
81         break;
82     }
83     os << "\n";
84 }
85
86 struct CallQuery
87 {
88     GLuint ids[2];
89     unsigned call;
90     const trace::FunctionSig *sig;
91 };
92
93 static const int maxActiveCallQueries = 256;
94 static std::list<CallQuery> callQueries;
95 static bool firstFrame = true;
96
97 static GLuint64
98 getTimestamp() {
99     GLuint query;
100     GLuint64 timestamp;
101
102     glGenQueries(1, &query);
103
104     glQueryCounter(query, GL_TIMESTAMP);
105     glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT, &timestamp);
106
107     glDeleteQueries(1, &query);
108
109     return timestamp;
110 }
111
112 static void
113 completeCallQuery(CallQuery& query) {
114     /* Get call start and duration */
115     GLuint64 timestamp, duration;
116     glGetQueryObjectui64vEXT(query.ids[0], GL_QUERY_RESULT, &timestamp);
117     glGetQueryObjectui64vEXT(query.ids[1], GL_QUERY_RESULT, &duration);
118     glDeleteQueries(2, query.ids);
119
120     /* Add call to profile */
121     retrace::profiler.addCall(query.call, query.sig->name, timestamp, duration);
122 }
123
124 void
125 beginProfileGPU(trace::Call &call) {
126     if (firstFrame) {
127         frame_start();
128     }
129
130     /* Ensure we don't have TOO many queries waiting for results */
131     if (callQueries.size() >= maxActiveCallQueries) {
132         completeCallQuery(callQueries.front());
133         callQueries.pop_front();
134     }
135
136     /* Create call query */
137     CallQuery query;
138     query.call = call.no;
139     query.sig = call.sig;
140
141     glGenQueries(2, query.ids);
142     glQueryCounter(query.ids[0], GL_TIMESTAMP);
143     glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
144
145     callQueries.push_back(query);
146 }
147
148 void
149 endProfileGPU(trace::Call &call) {
150     glEndQuery(GL_TIME_ELAPSED);
151 }
152
153 void
154 frame_start() {
155     firstFrame = false;
156
157     if (retrace::profileGPU) {
158         retrace::profiler.addFrameStart(retrace::frameNo, getTimestamp());
159     }
160 }
161
162 void
163 frame_complete(trace::Call &call) {
164     if (retrace::profileGPU) {
165         /* Complete any remaining queries */
166         for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
167             completeCallQuery(*itr);
168         }
169
170         callQueries.clear();
171
172         /* Indicate end of current frame */
173         retrace::profiler.addFrameEnd(getTimestamp());
174     }
175
176     retrace::frameComplete(call);
177
178     /* Indicate start of next frame */
179     frame_start();
180
181     if (!currentDrawable) {
182         return;
183     }
184
185     if (retrace::debug && !currentDrawable->visible) {
186         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
187     }
188 }
189
190 } /* namespace glretrace */
191
192
193 void
194 retrace::setUp(void) {
195     glws::init();
196 }
197
198
199 void
200 retrace::addCallbacks(retrace::Retracer &retracer)
201 {
202     retracer.addCallbacks(glretrace::gl_callbacks);
203     retracer.addCallbacks(glretrace::glx_callbacks);
204     retracer.addCallbacks(glretrace::wgl_callbacks);
205     retracer.addCallbacks(glretrace::cgl_callbacks);
206     retracer.addCallbacks(glretrace::egl_callbacks);
207 }
208
209
210 image::Image *
211 retrace::getSnapshot(void) {
212     if (!glretrace::currentDrawable) {
213         return NULL;
214     }
215
216     return glstate::getDrawBufferImage();
217 }
218
219
220 bool
221 retrace::dumpState(std::ostream &os)
222 {
223     if (glretrace::insideGlBeginEnd ||
224         !glretrace::currentDrawable ||
225         !glretrace::currentContext) {
226         return false;
227     }
228
229     glstate::dumpCurrentContext(os);
230
231     return true;
232 }
233
234 void
235 retrace::flushRendering(void) {
236     glFlush();
237 }
238
239 void
240 retrace::waitForInput(void) {
241     while (glws::processEvents()) {
242     }
243 }
244
245 void
246 retrace::cleanUp(void) {
247     glws::cleanup();
248 }