]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
Add support for basic GPU profiling of draw calls to retrace.
[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 std::vector<CallQuery> callQueries;
94 static GLuint frameQueries[2] = { 0, 0 };
95
96 void frame_start() {
97    if (retrace::profileGPU) {
98       glGenQueries(2, frameQueries);
99
100       /* Query frame start time */
101       glQueryCounter(frameQueries[0], GL_TIMESTAMP);
102    }
103 }
104
105 void frame_complete(trace::Call &call) {
106     if (retrace::profileGPU) {
107         /* Query frame end time */
108        glQueryCounter(frameQueries[1], GL_TIMESTAMP);
109
110        completeQueries();
111     }
112
113     retrace::frameComplete(call);
114
115     /* Indicate start of next frame */
116     frame_start();
117
118     if (!currentDrawable) {
119         return;
120     }
121
122     if (retrace::debug && !currentDrawable->visible) {
123         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
124     }
125 }
126
127 void
128 completeQueries()
129 {
130    if (callQueries.size() == 0)
131       return;
132
133    GLint available;
134    GLuint64 frameBegin, frameEnd;
135
136    /* Wait for frame to finish */
137    do {
138       glGetQueryObjectiv(frameQueries[1], GL_QUERY_RESULT_AVAILABLE, &available);
139    } while(!available);
140
141    /* Get frame start and end */
142    glGetQueryObjectui64vEXT(frameQueries[0], GL_QUERY_RESULT, &frameBegin);
143    glGetQueryObjectui64vEXT(frameQueries[1], GL_QUERY_RESULT, &frameEnd);
144    glDeleteQueries(2, frameQueries);
145
146    /* Add frame to profile */
147    retrace::profiler.addFrame(trace::Profiler::Frame(retrace::frameNo, frameBegin, frameEnd - frameBegin));
148
149    /* Loop through all active call queries */
150    for (std::vector<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
151       CallQuery& query = *itr;
152       GLuint64 timestamp, duration;
153
154       /* Get queue start and duration */
155       glGetQueryObjectui64vEXT(query.ids[0], GL_QUERY_RESULT, &timestamp);
156       glGetQueryObjectui64vEXT(query.ids[1], GL_QUERY_RESULT, &duration);
157       glDeleteQueries(2, query.ids);
158
159       /* Add call to profile */
160       retrace::profiler.addCall(trace::Profiler::Call(query.call, query.sig->name, timestamp, duration));
161    }
162
163    callQueries.clear();
164 }
165
166 void
167 beginProfileGPU(trace::Call &call) {
168    if (frameQueries[0] == 0) {
169       frame_start();
170    }
171
172    CallQuery query;
173    query.call = call.no;
174    query.sig = call.sig;
175
176    /* Create start and duration queries */
177    glGenQueries(2, query.ids);
178    glQueryCounter(query.ids[0], GL_TIMESTAMP);
179    glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
180
181    callQueries.push_back(query);
182 }
183
184 void
185 endProfileGPU(trace::Call &call) {
186    glEndQuery(GL_TIME_ELAPSED);
187 }
188
189 } /* namespace glretrace */
190
191
192 void
193 retrace::setUp(void) {
194     glws::init();
195 }
196
197
198 void
199 retrace::addCallbacks(retrace::Retracer &retracer)
200 {
201     retracer.addCallbacks(glretrace::gl_callbacks);
202     retracer.addCallbacks(glretrace::glx_callbacks);
203     retracer.addCallbacks(glretrace::wgl_callbacks);
204     retracer.addCallbacks(glretrace::cgl_callbacks);
205     retracer.addCallbacks(glretrace::egl_callbacks);
206 }
207
208
209 image::Image *
210 retrace::getSnapshot(void) {
211     if (!glretrace::currentDrawable) {
212         return NULL;
213     }
214
215     return glstate::getDrawBufferImage();
216 }
217
218
219 bool
220 retrace::dumpState(std::ostream &os)
221 {
222     if (glretrace::insideGlBeginEnd ||
223         !glretrace::currentDrawable ||
224         !glretrace::currentContext) {
225         return false;
226     }
227
228     glstate::dumpCurrentContext(os);
229
230     return true;
231 }
232
233 void
234 retrace::flushRendering(void) {
235     glFlush();
236 }
237
238 void
239 retrace::waitForInput(void) {
240     while (glws::processEvents()) {
241     }
242 }
243
244 void
245 retrace::cleanUp(void) {
246     glws::cleanup();
247 }