2 ##########################################################################
4 # Copyright 2011 Jose Fonseca
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # of this software and associated documentation files (the "Software"), to deal
9 # in the Software without restriction, including without limitation the rights
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 # copies of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 ##########################################################################/
28 '''Apitrace test suite based on Mesa demos.'''
41 ansi_re = re.compile('\x1b\[[0-9]{1,2}(;[0-9]{1,2}){0,2}m')
45 # http://www.theeggeadventure.com/wikimedia/index.php/Linux_Tips#Use_sed_to_remove_ANSI_colors
46 return ansi_re.sub('', s)
49 def popen(command, *args, **kwargs):
51 sys.stdout.write('cd %s && ' % kwargs['cwd'])
53 for name, value in kwargs['env'].iteritems():
54 if value != os.environ.get(name, None):
55 sys.stdout.write('%s=%s ' % (name, value))
56 sys.stdout.write(' '.join(command) + '\n')
58 return subprocess.Popen(command, *args, **kwargs)
61 ignored_function_names = set([
63 'glXGetCurrentDisplay',
66 'glXGetProcAddressARB',
68 'glXGetVisualFromFBConfig',
70 'glXCreateNewContext',
71 'glXMakeContextCurrent',
77 def image_tag(html, image):
78 url = os.path.relpath(image, options.results)
79 html.write(' <td><a href="%s"><img src="%s"/></a></td>\n' % (url, url))
82 def runtest(html, demo):
85 app = os.path.join(options.mesa_demos, 'src', demo)
87 dirname, basename = os.path.split(app)
89 trace = os.path.abspath(os.path.join(options.results, demo.replace('/', '-') + '.trace'))
91 env = os.environ.copy()
92 env['LD_PRELOAD'] = os.path.abspath(os.path.join(options.build, 'glxtrace.so'))
93 env['TRACE_FILE'] = trace
95 args = [os.path.join('.', basename)]
96 p = popen(args, env=env, cwd=dirname)
100 # http://stackoverflow.com/questions/151407/how-to-get-an-x11-window-from-a-process-id
101 ref_image = os.path.join(options.results, demo.replace('/', '-') + '.ref.png')
102 #subprocess.call(['xwininfo', '-root', '-tree'])
103 subprocess.call("xwd -name '%s' | xwdtopnm | pnmtopng > '%s'" % (args[0], ref_image), shell=True)
106 os.kill(p.pid, signal.SIGTERM)
108 p = popen([os.path.join(options.build, 'tracedump'), trace], stdout=subprocess.PIPE)
109 stdout, _ = p.communicate()
111 call_re = re.compile('^([0-9]+) (\w+)\(')
112 double_buffer = False
113 for orig_line in stdout.split('\n'):
114 line = ansi_strip(orig_line)
115 mo = call_re.match(line)
117 call_no = int(mo.group(1))
118 function_name = mo.group(2)
119 if function_name in ignored_function_names:
121 if function_name == 'glXSwapBuffers':
125 args = [os.path.join(options.build, 'glretrace')]
130 snapshot_prefix = os.path.join(options.results, demo.replace('/', '-') + '.')
131 args += ['-s', snapshot_prefix]
133 p = popen(args, stdout=subprocess.PIPE)
134 stdout, _ = p.communicate()
135 image_re = re.compile('^Wrote (.*\.png)$')
137 for line in stdout.split('\n'):
138 mo = image_re.match(line)
142 image_tag(html, ref_image)
144 delta_image = os.path.join(options.results, demo.replace('/', '-') + '.diff.png')
145 p = popen(["compare", '-alpha', 'opaque', '-metric', 'AE', '-fuzz', '5%', ref_image, image, delta_image])
146 _, stderr = p.communicate()
147 image_tag(html, image)
148 image_tag(html, delta_image)
150 html.write(' </tr>\n')
155 'trivial/clear-color',
157 'trivial/clear-fbo-scissor',
158 'trivial/clear-fbo-tex',
159 'trivial/clear-random',
160 #'trivial/clear-repeat',
161 'trivial/clear-scissor',
162 'trivial/clear-undefined',
164 'trivial/dlist-begin-call-end',
165 'trivial/dlist-dangling',
166 'trivial/dlist-degenerate',
167 'trivial/dlist-edgeflag',
168 'trivial/dlist-edgeflag-dangling',
169 'trivial/dlist-flat-tri',
170 'trivial/dlist-mat-tri',
171 'trivial/dlist-recursive-call',
172 'trivial/dlist-tri-flat-tri',
173 'trivial/dlist-tri-mat-tri',
174 'trivial/draw2arrays',
175 'trivial/drawarrays',
176 'trivial/drawelements',
177 'trivial/drawelements-large',
185 'trivial/line-smooth',
186 'trivial/line-stipple-wide',
187 'trivial/line-userclip',
188 'trivial/line-userclip-clip',
189 'trivial/line-userclip-nop',
190 'trivial/line-userclip-nop-clip',
194 'trivial/lineloop-clip',
195 'trivial/lineloop-elts',
197 'trivial/linestrip-clip',
198 'trivial/linestrip-flat-stipple',
199 'trivial/linestrip-stipple',
200 'trivial/linestrip-stipple-wide',
201 'trivial/long-fixed-func',
204 'trivial/point-clip',
205 'trivial/point-param',
206 'trivial/point-sprite',
207 'trivial/point-wide',
208 'trivial/point-wide-smooth',
211 'trivial/poly-flat-clip',
212 'trivial/poly-flat-unfilled-clip',
213 'trivial/poly-unfilled',
216 'trivial/quad-clip-all-vertices',
217 'trivial/quad-clip-nearplane',
218 'trivial/quad-degenerate',
220 'trivial/quad-offset-factor',
221 'trivial/quad-offset-unfilled',
222 'trivial/quad-offset-units',
223 'trivial/quad-tex-2d',
224 'trivial/quad-tex-3d',
225 'trivial/quad-tex-alpha',
226 'trivial/quad-tex-pbo',
227 'trivial/quad-tex-sub',
228 'trivial/quad-unfilled',
229 'trivial/quad-unfilled-clip',
230 'trivial/quad-unfilled-stipple',
233 'trivial/quadstrip-clip',
234 'trivial/quadstrip-cont',
235 'trivial/quadstrip-flat',
236 'trivial/readpixels',
241 'trivial/tri-alpha-tex',
242 'trivial/tri-array-interleaved',
244 'trivial/tri-blend-color',
245 'trivial/tri-blend-max',
246 'trivial/tri-blend-min',
247 'trivial/tri-blend-revsub',
248 'trivial/tri-blend-sub',
252 'trivial/tri-cull-both',
254 'trivial/tri-edgeflag',
255 'trivial/tri-edgeflag-array',
257 'trivial/tri-fbo-tex',
258 'trivial/tri-fbo-tex-mip',
260 'trivial/tri-flat-clip',
263 'trivial/tri-fp-const-imm',
265 'trivial/tri-lit-material',
266 'trivial/tri-logicop-none',
267 'trivial/tri-logicop-xor',
268 'trivial/tri-mask-tri',
269 'trivial/tri-multitex-vbo',
271 'trivial/tri-point-line-clipped',
273 'trivial/tri-repeat',
274 'trivial/tri-scissor-tri',
275 'trivial/tri-square',
276 'trivial/tri-stencil',
277 'trivial/tri-stipple',
279 'trivial/tri-tex-1d',
280 'trivial/tri-tex-3d',
282 'trivial/tri-unfilled',
283 'trivial/tri-unfilled-clip',
284 'trivial/tri-unfilled-edgeflag',
285 'trivial/tri-unfilled-fog',
286 'trivial/tri-unfilled-point',
287 'trivial/tri-unfilled-smooth',
288 'trivial/tri-unfilled-tri',
289 'trivial/tri-unfilled-tri-lit',
290 'trivial/tri-unfilled-userclip',
291 'trivial/tri-unfilled-userclip-stip',
292 'trivial/tri-userclip',
293 'trivial/tri-viewport',
298 'trivial/trifan-flat',
299 'trivial/trifan-flat-clip',
300 'trivial/trifan-flat-unfilled-clip',
301 'trivial/trifan-unfilled',
303 'trivial/tristrip-clip',
304 'trivial/tristrip-flat',
305 'trivial/vbo-drawarrays',
306 'trivial/vbo-drawelements',
307 'trivial/vbo-drawrange',
308 'trivial/vbo-noninterleaved',
311 'trivial/vp-array-hf',
312 'trivial/vp-array-int',
314 'trivial/vp-line-clip',
317 'trivial/vp-tri-cb-pos',
318 'trivial/vp-tri-cb-tex',
319 'trivial/vp-tri-imm',
320 'trivial/vp-tri-invariant',
321 'trivial/vp-tri-swap',
322 'trivial/vp-tri-tex',
323 'trivial/vp-unfilled',
328 #'demos/arbocclude2',
337 #'demos/fbo_firecube',
364 #'demos/singlebuffer',
366 #'demos/spriteblast',
380 #'fp/point-position',
383 #'fp/tri-depthwrite',
384 #'fp/tri-depthwrite2',
393 #'glsl/convolutions',
397 #'glsl/geom-sprites',
398 #'glsl/geom-stipple-lines',
399 #'glsl/geom-wide-lines',
410 #'glsl/shadow_sampler',
418 #'glsl/vert-or-frag-only',
423 #'perf/drawoverhead',
446 #'redbook/convolution',
459 #'redbook/histogram',
468 #'redbook/movelight',
469 #'redbook/multisamp',
473 #'redbook/pickdepth',
474 #'redbook/picksquare',
482 #'redbook/sccolorlight',
484 #'redbook/scenebamb',
485 #'redbook/sceneflat',
487 #'redbook/shadowmap',
492 #'redbook/surfpoints',
493 #'redbook/teaambient',
501 #'redbook/texture3d',
502 #'redbook/texturesurf',
505 #'redbook/unproject',
545 'tests/arbfptexture',
549 'tests/arbnpot-mipmap',
553 'tests/arbvpwarpmesh',
554 'tests/arraytexture',
562 'tests/bug_texstore_i8',
564 'tests/calibrate_rast',
571 'tests/drawbuffers2',
574 'tests/ext422square',
594 'tests/mipmap_comp_tests',
595 'tests/mipmap_limits',
596 'tests/mipmap_tunnel',
599 'tests/multitexarray',
603 'tests/packedpixels',
607 'tests/prog_parameter',
613 'tests/scissor-viewport',
615 'tests/shader-interp',
617 'tests/shadow-sample',
619 'tests/stencilreaddraw',
622 'tests/streaming_rect',
627 'tests/texcompress2',
637 'tests/unfilledclip',
656 #'xdemos/glxcontexts',
659 #'xdemos/glxgears_fbconfig',
660 #'xdemos/glxgears_pixmap',
666 #'xdemos/glxswapcontrol',
672 #'xdemos/opencloseopen',
678 #'xdemos/sharedtex_mt',
679 #'xdemos/texture_from_pixmap',
682 #'xdemos/xrotfontdemo',
683 #'xdemos/yuvrect_client',
696 # Parse command line options
697 optparser = optparse.OptionParser(
698 usage='\n\t%prog [options] [demo] ...',
700 optparser.add_option(
701 '--build', metavar='PATH',
702 type='string', dest='build', default='.',
703 help='path to apitrace build [default=%default]')
704 optparser.add_option(
705 '--results', metavar='PATH',
706 type='string', dest='results', default='.',
707 help='results directory [default=%default]')
708 optparser.add_option(
709 '--mesa-demos', metavar='PATH',
710 type='string', dest='mesa_demos', default=os.environ.get('MESA_DEMOS'),
711 help='path to Mesa demos [default=%default]')
713 (options, args) = optparser.parse_args(sys.argv[1:])
715 if not options.mesa_demos:
716 optparser.error('path to Mesa demos not specified')
717 if not os.path.exists(options.results):
718 os.makedirs(options.results)
720 html = open(os.path.join(options.results, 'index.html'), 'wt')
721 html.write('<html>\n')
722 html.write(' <body>\n')
723 html.write(' <table border="1">\n')
724 html.write(' <tr><th>Ref</th><th>Src</th><th>Δ</th></tr>\n')
729 html.write(' </table>\n')
730 html.write(' </body>\n')
731 html.write('</html>\n')
734 if __name__ == '__main__':