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):
84 app = os.path.join(options.mesa_demos, 'src', demo)
86 dirname, basename = os.path.split(app)
88 trace = os.path.abspath(os.path.join(options.results, demo.replace('/', '-') + '.trace'))
90 env = os.environ.copy()
91 env['LD_PRELOAD'] = os.path.abspath(os.path.join(options.build, 'glxtrace.so'))
92 env['TRACE_FILE'] = trace
94 args = [os.path.join('.', basename)]
95 p = popen(args, env=env, cwd=dirname)
101 if subprocess.call(['xwininfo', '-name', window_name], stdout=subprocess.PIPE) == 0:
104 ref_image = os.path.join(options.results, demo.replace('/', '-') + '.ref.png')
105 subprocess.call("xwd -name '%s' | xwdtopnm | pnmtopng > '%s'" % (args[0], ref_image), shell=True)
108 p.send_signal(signal.SIGTERM)
110 p = popen([os.path.join(options.build, 'tracedump'), trace], stdout=subprocess.PIPE)
111 stdout, _ = p.communicate()
113 call_re = re.compile('^([0-9]+) (\w+)\(')
114 double_buffer = False
115 for orig_line in stdout.split('\n'):
116 line = ansi_strip(orig_line)
117 mo = call_re.match(line)
119 call_no = int(mo.group(1))
120 function_name = mo.group(2)
121 if function_name in ignored_function_names:
123 if function_name == 'glXSwapBuffers':
127 args = [os.path.join(options.build, 'glretrace')]
132 snapshot_prefix = os.path.join(options.results, demo.replace('/', '-') + '.')
133 args += ['-s', snapshot_prefix]
135 p = popen(args, stdout=subprocess.PIPE)
136 stdout, _ = p.communicate()
137 image_re = re.compile('^Wrote (.*\.png)$')
139 for line in stdout.split('\n'):
140 mo = image_re.match(line)
145 delta_image = os.path.join(options.results, demo.replace('/', '-') + '.diff.png')
151 '-dissimilarity-threshold', '1',
152 ref_image, image, delta_image
153 ], stderr = subprocess.PIPE)
154 _, stderr = p.communicate()
162 html.write(' <tr>\n')
163 image_tag(html, ref_image)
164 image_tag(html, image)
165 image_tag(html, delta_image)
166 html.write(' </tr>\n')
169 sys.stdout.write('FAIL\n')
171 sys.stdout.write('PASS\n')
175 'trivial/clear-color',
177 'trivial/clear-fbo-scissor',
178 'trivial/clear-fbo-tex',
179 'trivial/clear-random',
180 #'trivial/clear-repeat', # XXX: animated
181 'trivial/clear-scissor',
182 'trivial/clear-undefined',
184 'trivial/dlist-begin-call-end',
185 'trivial/dlist-dangling',
186 'trivial/dlist-degenerate',
187 'trivial/dlist-edgeflag',
188 'trivial/dlist-edgeflag-dangling',
189 'trivial/dlist-flat-tri',
190 'trivial/dlist-mat-tri',
191 'trivial/dlist-recursive-call',
192 'trivial/dlist-tri-flat-tri',
193 'trivial/dlist-tri-mat-tri',
194 'trivial/draw2arrays',
195 'trivial/drawarrays',
196 'trivial/drawelements',
197 'trivial/drawelements-large',
205 'trivial/line-smooth',
206 'trivial/line-stipple-wide',
207 'trivial/line-userclip',
208 'trivial/line-userclip-clip',
209 'trivial/line-userclip-nop',
210 'trivial/line-userclip-nop-clip',
214 'trivial/lineloop-clip',
215 'trivial/lineloop-elts',
217 'trivial/linestrip-clip',
218 'trivial/linestrip-flat-stipple',
219 'trivial/linestrip-stipple',
220 'trivial/linestrip-stipple-wide',
221 'trivial/long-fixed-func',
224 'trivial/point-clip',
225 'trivial/point-param',
226 'trivial/point-sprite',
227 'trivial/point-wide',
228 'trivial/point-wide-smooth',
231 'trivial/poly-flat-clip',
232 'trivial/poly-flat-unfilled-clip',
233 'trivial/poly-unfilled',
236 'trivial/quad-clip-all-vertices',
237 'trivial/quad-clip-nearplane',
238 'trivial/quad-degenerate',
240 'trivial/quad-offset-factor',
241 'trivial/quad-offset-unfilled',
242 'trivial/quad-offset-units',
243 'trivial/quad-tex-2d',
244 'trivial/quad-tex-3d',
245 'trivial/quad-tex-alpha',
246 'trivial/quad-tex-pbo',
247 'trivial/quad-tex-sub',
248 'trivial/quad-unfilled',
249 'trivial/quad-unfilled-clip',
250 'trivial/quad-unfilled-stipple',
253 'trivial/quadstrip-clip',
254 #'trivial/quadstrip-cont', # XXX: animated
255 'trivial/quadstrip-flat',
256 'trivial/readpixels',
261 'trivial/tri-alpha-tex',
262 'trivial/tri-array-interleaved',
264 'trivial/tri-blend-color',
265 'trivial/tri-blend-max',
266 'trivial/tri-blend-min',
267 'trivial/tri-blend-revsub',
268 'trivial/tri-blend-sub',
272 'trivial/tri-cull-both',
274 'trivial/tri-edgeflag',
275 'trivial/tri-edgeflag-array',
277 'trivial/tri-fbo-tex',
278 'trivial/tri-fbo-tex-mip',
280 'trivial/tri-flat-clip',
283 'trivial/tri-fp-const-imm',
285 'trivial/tri-lit-material',
286 'trivial/tri-logicop-none',
287 'trivial/tri-logicop-xor',
288 'trivial/tri-mask-tri',
289 'trivial/tri-multitex-vbo',
291 'trivial/tri-point-line-clipped',
293 #'trivial/tri-repeat', # XXX: animated
294 'trivial/tri-scissor-tri',
295 'trivial/tri-square',
296 'trivial/tri-stencil',
297 'trivial/tri-stipple',
299 'trivial/tri-tex-1d',
300 'trivial/tri-tex-3d',
302 'trivial/tri-unfilled',
303 'trivial/tri-unfilled-clip',
304 'trivial/tri-unfilled-edgeflag',
305 'trivial/tri-unfilled-fog',
306 'trivial/tri-unfilled-point',
307 'trivial/tri-unfilled-smooth',
308 'trivial/tri-unfilled-tri',
309 'trivial/tri-unfilled-tri-lit',
310 'trivial/tri-unfilled-userclip',
311 'trivial/tri-unfilled-userclip-stip',
312 'trivial/tri-userclip',
313 'trivial/tri-viewport',
318 'trivial/trifan-flat',
319 'trivial/trifan-flat-clip',
320 'trivial/trifan-flat-unfilled-clip',
321 'trivial/trifan-unfilled',
323 'trivial/tristrip-clip',
324 'trivial/tristrip-flat',
325 'trivial/vbo-drawarrays',
326 'trivial/vbo-drawelements',
327 'trivial/vbo-drawrange',
328 'trivial/vbo-noninterleaved',
331 'trivial/vp-array-hf',
332 'trivial/vp-array-int',
334 'trivial/vp-line-clip',
337 'trivial/vp-tri-cb-pos',
338 'trivial/vp-tri-cb-tex',
339 'trivial/vp-tri-imm',
340 'trivial/vp-tri-invariant',
341 'trivial/vp-tri-swap',
342 'trivial/vp-tri-tex',
343 'trivial/vp-unfilled',
348 #'demos/arbocclude2',
357 #'demos/fbo_firecube',
384 #'demos/singlebuffer',
386 #'demos/spriteblast',
400 #'fp/point-position',
403 #'fp/tri-depthwrite',
404 #'fp/tri-depthwrite2',
413 #'glsl/convolutions',
417 #'glsl/geom-sprites',
418 #'glsl/geom-stipple-lines',
419 #'glsl/geom-wide-lines',
430 #'glsl/shadow_sampler',
438 #'glsl/vert-or-frag-only',
443 #'perf/drawoverhead',
466 #'redbook/convolution',
479 #'redbook/histogram',
488 #'redbook/movelight',
489 #'redbook/multisamp',
493 #'redbook/pickdepth',
494 #'redbook/picksquare',
502 #'redbook/sccolorlight',
504 #'redbook/scenebamb',
505 #'redbook/sceneflat',
507 #'redbook/shadowmap',
512 #'redbook/surfpoints',
513 #'redbook/teaambient',
521 #'redbook/texture3d',
522 #'redbook/texturesurf',
525 #'redbook/unproject',
561 #'tests/afsmultiarb', # XXX: animated
562 #'tests/antialias', # XXX: animated
565 'tests/arbfptexture',
569 'tests/arbnpot-mipmap',
572 #'tests/arbvptorus', # XXX: animated
573 #'tests/arbvpwarpmesh', # XXX: animated
574 'tests/arraytexture',
578 #'tests/bufferobj', # XXX: animated
581 #'tests/bug_3195', # XXX: animated
582 'tests/bug_texstore_i8',
584 #'tests/calibrate_rast', # XXX: animated
586 #'tests/copypixrate', # XXX: animated
591 'tests/drawbuffers2',
594 'tests/ext422square',
596 #'tests/fbotest2', # XXX: animated
598 #'tests/fillrate', # XXX: animated
609 #'tests/manytex', # XXX: animated
614 'tests/mipmap_comp_tests',
615 'tests/mipmap_limits',
616 'tests/mipmap_tunnel',
619 'tests/multitexarray',
620 #'tests/multiwindow', # XXX: animated
623 'tests/packedpixels',
627 'tests/prog_parameter',
628 #'tests/quads', # XXX: animated
629 #'tests/random', # XXX: animated
630 #'tests/readrate', # XXX: animated
633 'tests/scissor-viewport',
635 'tests/shader-interp',
637 'tests/shadow-sample',
638 #'tests/sharedtex', # XXX: animated
639 'tests/stencilreaddraw',
640 #'tests/stencilwrap', # XXX: animated
642 #'tests/streaming_rect', # XXX: animated
644 #'tests/subtexrate', # XXX: animated
646 #'tests/texcmp', # XXX: animated
647 'tests/texcompress2',
654 #'tests/texobj', # XXX: animated
657 'tests/unfilledclip',
676 #'xdemos/glxcontexts',
679 #'xdemos/glxgears_fbconfig',
680 #'xdemos/glxgears_pixmap',
686 #'xdemos/glxswapcontrol',
692 #'xdemos/opencloseopen',
698 #'xdemos/sharedtex_mt',
699 #'xdemos/texture_from_pixmap',
702 #'xdemos/xrotfontdemo',
703 #'xdemos/yuvrect_client',
716 # Parse command line options
717 optparser = optparse.OptionParser(
718 usage='\n\t%prog [options] [demo] ...',
720 optparser.add_option(
721 '--build', metavar='PATH',
722 type='string', dest='build', default='.',
723 help='path to apitrace build [default=%default]')
724 optparser.add_option(
725 '--results', metavar='PATH',
726 type='string', dest='results', default='.',
727 help='results directory [default=%default]')
728 optparser.add_option(
729 '--mesa-demos', metavar='PATH',
730 type='string', dest='mesa_demos', default=os.environ.get('MESA_DEMOS'),
731 help='path to Mesa demos [default=%default]')
733 (options, args) = optparser.parse_args(sys.argv[1:])
735 if not options.mesa_demos:
736 optparser.error('path to Mesa demos not specified')
737 if not os.path.exists(options.results):
738 os.makedirs(options.results)
740 html = open(os.path.join(options.results, 'index.html'), 'wt')
741 html.write('<html>\n')
742 html.write(' <body>\n')
743 html.write(' <table border="1">\n')
744 html.write(' <tr><th>Ref</th><th>Src</th><th>Δ</th></tr>\n')
749 html.write(' </table>\n')
750 html.write(' </body>\n')
751 html.write('</html>\n')
754 if __name__ == '__main__':