Merge branch '7.8'
[mesa.git] / src / gallium / tests / python / retrace / interpreter.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
5 # All Rights Reserved.
6 #
7 # Permission is hereby granted, free of charge, to any person obtaining a
8 # copy of this software and associated documentation files (the
9 # "Software"), to deal in the Software without restriction, including
10 # without limitation the rights to use, copy, modify, merge, publish,
11 # distribute, sub license, and/or sell copies of the Software, and to
12 # permit persons to whom the Software is furnished to do so, subject to
13 # the following conditions:
14 #
15 # The above copyright notice and this permission notice (including the
16 # next paragraph) shall be included in all copies or substantial portions
17 # of the Software.
18 #
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 # IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
23 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #
27 ##########################################################################
28
29
30 import sys
31 import struct
32
33 import gallium
34 import model
35 import parse as parser
36
37
38 try:
39 from struct import unpack_from
40 except ImportError:
41 def unpack_from(fmt, buf, offset=0):
42 size = struct.calcsize(fmt)
43 return struct.unpack(fmt, buf[offset:offset + size])
44
45
46 def make_image(surface, x=None, y=None, w=None, h=None):
47 if x is None:
48 x = 0
49 if y is None:
50 y = 0
51 if w is None:
52 w = surface.width - x
53 if h is None:
54 h = surface.height - y
55 data = surface.get_tile_rgba8(x, y, surface.width, surface.height)
56
57 import Image
58 outimage = Image.fromstring('RGBA', (w, h), data, "raw", 'RGBA', 0, 1)
59 return outimage
60
61 def save_image(filename, surface, x=None, y=None, w=None, h=None):
62 outimage = make_image(surface, x, y, w, h)
63 outimage.save(filename, "PNG")
64
65 def show_image(surface, title, x=None, y=None, w=None, h=None):
66 outimage = make_image(surface, x, y, w, h)
67
68 import Tkinter as tk
69 from PIL import Image, ImageTk
70 root = tk.Tk()
71
72 root.title(title)
73
74 image1 = ImageTk.PhotoImage(outimage)
75 w = image1.width()
76 h = image1.height()
77 x = 100
78 y = 100
79 root.geometry("%dx%d+%d+%d" % (w, h, x, y))
80 panel1 = tk.Label(root, image=image1)
81 panel1.pack(side='top', fill='both', expand='yes')
82 panel1.image = image1
83 root.mainloop()
84
85
86 class Struct:
87 """C-like struct"""
88
89 # A basic Python class can pass as a C-like structure
90 pass
91
92
93 struct_factories = {
94 "pipe_blend_color": gallium.BlendColor,
95 "pipe_blend_state": gallium.Blend,
96 #"pipe_clip_state": gallium.Clip,
97 #"pipe_buffer": gallium.Buffer,
98 "pipe_depth_state": gallium.Depth,
99 "pipe_stencil_state": gallium.Stencil,
100 "pipe_alpha_state": gallium.Alpha,
101 "pipe_depth_stencil_alpha_state": gallium.DepthStencilAlpha,
102 #"pipe_framebuffer_state": gallium.Framebuffer,
103 "pipe_poly_stipple": gallium.PolyStipple,
104 "pipe_rasterizer_state": gallium.Rasterizer,
105 "pipe_sampler_state": gallium.Sampler,
106 "pipe_scissor_state": gallium.Scissor,
107 #"pipe_shader_state": gallium.Shader,
108 #"pipe_vertex_buffer": gallium.VertexBuffer,
109 "pipe_vertex_element": gallium.VertexElement,
110 "pipe_viewport_state": gallium.Viewport,
111 #"pipe_texture": gallium.Texture,
112 'pipe_subresource': gallium.pipe_subresource,
113 'pipe_box': gallium.pipe_box,
114 }
115
116
117 member_array_factories = {
118 #"pipe_rasterizer_state": {"sprite_coord_mode": gallium.ByteArray},
119 "pipe_poly_stipple": {"stipple": gallium.UnsignedArray},
120 "pipe_viewport_state": {"scale": gallium.FloatArray, "translate": gallium.FloatArray},
121 #"pipe_clip_state": {"ucp": gallium.FloatArray},
122 "pipe_depth_stencil_alpha_state": {"stencil": gallium.StencilArray},
123 "pipe_blend_color": {"color": gallium.FloatArray},
124 "pipe_sampler_state": {"border_color": gallium.FloatArray},
125 }
126
127
128 class Translator(model.Visitor):
129 """Translate model arguments into regular Python objects"""
130
131 def __init__(self, interpreter):
132 self.interpreter = interpreter
133 self.result = None
134
135 def visit(self, node):
136 self.result = None
137 node.visit(self)
138 return self.result
139
140 def visit_literal(self, node):
141 self.result = node.value
142
143 def visit_named_constant(self, node):
144 # lookup the named constant in the gallium module
145 self.result = getattr(gallium, node.name)
146
147 def visit_array(self, node):
148 array = []
149 for element in node.elements:
150 array.append(self.visit(element))
151 self.result = array
152
153 def visit_struct(self, node):
154 struct_factory = struct_factories.get(node.name, Struct)
155 struct = struct_factory()
156 for member_name, member_node in node.members:
157 member_value = self.visit(member_node)
158 try:
159 array_factory = member_array_factories[node.name][member_name]
160 except KeyError:
161 pass
162 else:
163 assert isinstance(member_value, list)
164 array = array_factory(len(member_value))
165 for i in range(len(member_value)):
166 array[i] = member_value[i]
167 member_value = array
168 #print node.name, member_name, member_value
169 assert isinstance(struct, Struct) or hasattr(struct, member_name)
170 setattr(struct, member_name, member_value)
171 self.result = struct
172
173 def visit_pointer(self, node):
174 self.result = self.interpreter.lookup_object(node.address)
175
176
177 class Object:
178
179 def __init__(self, interpreter, real):
180 self.interpreter = interpreter
181 self.real = real
182
183
184 class Global(Object):
185
186 def __init__(self, interpreter, real):
187 self.interpreter = interpreter
188 self.real = real
189
190 def pipe_screen_create(self):
191 real = gallium.Device()
192 return Screen(self.interpreter, real)
193
194 def pipe_context_create(self, screen):
195 context = screen.real.context_create()
196 return Context(self.interpreter, context)
197
198
199 class Transfer:
200
201 def __init__(self, resource, usage, subresource, box):
202 self.resource = resource
203 self.usage = usage
204 self.subresource = subresource
205 self.box = box
206
207
208 class Screen(Object):
209
210 def destroy(self):
211 pass
212
213 def get_name(self):
214 pass
215
216 def get_vendor(self):
217 pass
218
219 def get_param(self, param):
220 pass
221
222 def get_paramf(self, param):
223 pass
224
225 def context_create(self):
226 context = self.real.context_create()
227 return Context(self.interpreter, context)
228
229 def is_format_supported(self, format, target, bind, geom_flags):
230 return self.real.is_format_supported(format, target, bind, geom_flags)
231
232 def resource_create(self, templat):
233 return self.real.resource_create(
234 format = templat.format,
235 width = templat.width,
236 height = templat.height,
237 depth = templat.depth,
238 last_level = templat.last_level,
239 target = templat.target,
240 bind = templat.bind,
241 )
242
243 def texture_destroy(self, texture):
244 self.interpreter.unregister_object(texture)
245
246 def texture_release(self, surface):
247 pass
248
249 def get_tex_surface(self, texture, face, level, zslice, usage):
250 if texture is None:
251 return None
252 return texture.get_surface(face, level, zslice)
253
254 def tex_surface_destroy(self, surface):
255 self.interpreter.unregister_object(surface)
256
257 def tex_surface_release(self, surface):
258 pass
259
260 def user_buffer_create(self, data, size, bind):
261 # We don't really care to distinguish between user and regular buffers
262 buffer = self.real.buffer_create(size, bind)
263 assert size == len(data)
264 buffer.write(data)
265 return buffer
266
267 def buffer_create(self, alignment, usage, size):
268 return self.real.buffer_create(size, alignment, usage)
269
270 def buffer_destroy(self, buffer):
271 pass
272
273 def fence_finish(self, fence, flags):
274 pass
275
276 def fence_reference(self, dst, src):
277 pass
278
279 def flush_frontbuffer(self, surface):
280 pass
281
282
283 class Context(Object):
284
285 def __init__(self, interpreter, real):
286 Object.__init__(self, interpreter, real)
287 self.cbufs = []
288 self.zsbuf = None
289 self.vbufs = []
290 self.velems = []
291 self.dirty = False
292
293 def destroy(self):
294 pass
295
296 def create_blend_state(self, state):
297 if isinstance(state, str):
298 state = gallium.Blend(state)
299 sys.stdout.write('\t%s\n' % state)
300 return state
301
302 def bind_blend_state(self, state):
303 if state is not None:
304 self.real.set_blend(state)
305
306 def delete_blend_state(self, state):
307 pass
308
309 def create_sampler_state(self, state):
310 return state
311
312 def delete_sampler_state(self, state):
313 pass
314
315 def bind_vertex_sampler_states(self, num_states, states):
316 for i in range(num_states):
317 self.real.set_vertex_sampler(i, states[i])
318
319 def bind_fragment_sampler_states(self, num_states, states):
320 for i in range(num_states):
321 self.real.set_fragment_sampler(i, states[i])
322
323 def create_rasterizer_state(self, state):
324 return state
325
326 def bind_rasterizer_state(self, state):
327 if state is not None:
328 self.real.set_rasterizer(state)
329
330 def delete_rasterizer_state(self, state):
331 pass
332
333 def create_depth_stencil_alpha_state(self, state):
334 return state
335
336 def bind_depth_stencil_alpha_state(self, state):
337 if state is not None:
338 self.real.set_depth_stencil_alpha(state)
339
340 def delete_depth_stencil_alpha_state(self, state):
341 pass
342
343 def create_fs_state(self, state):
344 tokens = str(state.tokens)
345 shader = gallium.Shader(tokens)
346 return shader
347
348 create_vs_state = create_fs_state
349
350 def bind_fs_state(self, state):
351 self.real.set_fragment_shader(state)
352
353 def bind_vs_state(self, state):
354 self.real.set_vertex_shader(state)
355
356 def delete_fs_state(self, state):
357 pass
358
359 delete_vs_state = delete_fs_state
360
361 def set_blend_color(self, state):
362 self.real.set_blend_color(state)
363
364 def set_stencil_ref(self, state):
365 self.real.set_stencil_ref(state)
366
367 def set_clip_state(self, state):
368 _state = gallium.Clip()
369 _state.nr = state.nr
370 if state.nr:
371 # FIXME
372 ucp = gallium.FloatArray(gallium.PIPE_MAX_CLIP_PLANES*4)
373 for i in range(len(state.ucp)):
374 for j in range(len(state.ucp[i])):
375 ucp[i*4 + j] = state.ucp[i][j]
376 _state.ucp = ucp
377 self.real.set_clip(_state)
378
379 def dump_constant_buffer(self, buffer):
380 if not self.interpreter.verbosity(2):
381 return
382
383 data = buffer.read()
384 format = '4f'
385 index = 0
386 for offset in range(0, len(data), struct.calcsize(format)):
387 x, y, z, w = unpack_from(format, data, offset)
388 sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))
389 index += 1
390 sys.stdout.flush()
391
392 def set_constant_buffer(self, shader, index, buffer):
393 if buffer is not None:
394 self.real.set_constant_buffer(shader, index, buffer)
395
396 self.dump_constant_buffer(buffer)
397
398 def set_framebuffer_state(self, state):
399 _state = gallium.Framebuffer()
400 _state.width = state.width
401 _state.height = state.height
402 _state.nr_cbufs = state.nr_cbufs
403 for i in range(len(state.cbufs)):
404 _state.set_cbuf(i, state.cbufs[i])
405 _state.set_zsbuf(state.zsbuf)
406 self.real.set_framebuffer(_state)
407
408 self.cbufs = state.cbufs
409 self.zsbuf = state.zsbuf
410
411 def set_polygon_stipple(self, state):
412 self.real.set_polygon_stipple(state)
413
414 def set_scissor_state(self, state):
415 self.real.set_scissor(state)
416
417 def set_viewport_state(self, state):
418 self.real.set_viewport(state)
419
420 def create_sampler_view(self, texture, templ):
421 return self.real.create_sampler_view(texture,
422 format = templ.format,
423 first_level = templ.first_level,
424 last_level = templ.last_level,
425 swizzle_r = templ.swizzle_r,
426 swizzle_g = templ.swizzle_r,
427 swizzle_b = templ.swizzle_g,
428 swizzle_a = templ.swizzle_a)
429
430 def set_fragment_sampler_views(self, num, views):
431 for i in range(num):
432 self.real.set_fragment_sampler_view(i, views[i])
433
434 def set_vertex_sampler_views(self, num, views):
435 for i in range(num):
436 self.real.set_vertex_sampler_view(i, views[i])
437
438 def set_vertex_buffers(self, num_buffers, buffers):
439 self.vbufs = buffers[0:num_buffers]
440 for i in range(num_buffers):
441 vbuf = buffers[i]
442 self.real.set_vertex_buffer(
443 i,
444 stride = vbuf.stride,
445 max_index = vbuf.max_index,
446 buffer_offset = vbuf.buffer_offset,
447 buffer = vbuf.buffer,
448 )
449
450 def create_vertex_elements_state(self, num_elements, elements):
451 return elements[0:num_elements]
452
453 def bind_vertex_elements_state(self, state):
454 elements = state
455 num_elements = len(elements)
456 self.velems = elements
457 for i in range(num_elements):
458 self.real.set_vertex_element(i, elements[i])
459 self.real.set_vertex_elements(num_elements)
460
461 def delete_vertex_elements_state(self, state):
462 pass
463
464 def dump_vertices(self, start, count):
465 if not self.interpreter.verbosity(2):
466 return
467
468 for index in range(start, start + count):
469 if index >= start + 16:
470 sys.stdout.write('\t...\n')
471 break
472 sys.stdout.write('\t{\n')
473 for velem in self.velems:
474 vbuf = self.vbufs[velem.vertex_buffer_index]
475
476 offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index
477 format = {
478 gallium.PIPE_FORMAT_R32_FLOAT: 'f',
479 gallium.PIPE_FORMAT_R32G32_FLOAT: '2f',
480 gallium.PIPE_FORMAT_R32G32B32_FLOAT: '3f',
481 gallium.PIPE_FORMAT_R32G32B32A32_FLOAT: '4f',
482 gallium.PIPE_FORMAT_A8R8G8B8_UNORM: '4B',
483 gallium.PIPE_FORMAT_R8G8B8A8_UNORM: '4B',
484 gallium.PIPE_FORMAT_R16G16B16_SNORM: '3h',
485 }[velem.src_format]
486
487 data = vbuf.buffer.read()
488 values = unpack_from(format, data, offset)
489 sys.stdout.write('\t\t{' + ', '.join(map(str, values)) + '},\n')
490 sys.stdout.write('\t},\n')
491 sys.stdout.flush()
492
493 def dump_indices(self, ibuf, isize, start, count):
494 if not self.interpreter.verbosity(2):
495 return
496
497 format = {
498 1: 'B',
499 2: 'H',
500 4: 'I',
501 }[isize]
502
503 assert struct.calcsize(format) == isize
504
505 data = ibuf.read()
506 maxindex, minindex = 0, 0xffffffff
507
508 sys.stdout.write('\t{\n')
509 for i in range(start, start + count):
510 if i >= start + 16 and not self.interpreter.verbosity(3):
511 sys.stdout.write('\t...\n')
512 break
513 offset = i*isize
514 index, = unpack_from(format, data, offset)
515 sys.stdout.write('\t\t%u,\n' % index)
516 minindex = min(minindex, index)
517 maxindex = max(maxindex, index)
518 sys.stdout.write('\t},\n')
519 sys.stdout.flush()
520
521 return minindex, maxindex
522
523 def draw_arrays(self, mode, start, count):
524 self.dump_vertices(start, count)
525
526 self.real.draw_arrays(mode, start, count)
527 self._set_dirty()
528
529 def draw_elements(self, indexBuffer, indexSize, mode, start, count):
530 if self.interpreter.verbosity(2):
531 minindex, maxindex = self.dump_indices(indexBuffer, indexSize, start, count)
532 self.dump_vertices(minindex, maxindex - minindex)
533
534 self.real.draw_elements(indexBuffer, indexSize, mode, start, count)
535 self._set_dirty()
536
537 def draw_range_elements(self, indexBuffer, indexSize, minIndex, maxIndex, mode, start, count):
538 if self.interpreter.verbosity(2):
539 minindex, maxindex = self.dump_indices(indexBuffer, indexSize, start, count)
540 minindex = min(minindex, minIndex)
541 maxindex = min(maxindex, maxIndex)
542 self.dump_vertices(minindex, maxindex - minindex)
543
544 self.real.draw_range_elements(indexBuffer, indexSize, minIndex, maxIndex, mode, start, count)
545 self._set_dirty()
546
547 def surface_copy(self, dest, destx, desty, src, srcx, srcy, width, height):
548 if dest is not None and src is not None:
549 if self.interpreter.options.all:
550 self.interpreter.present(src, 'surface_copy_src', srcx, srcy, width, height)
551 self.real.surface_copy(dest, destx, desty, src, srcx, srcy, width, height)
552 if dest in self.cbufs:
553 self._set_dirty()
554 flags = gallium.PIPE_FLUSH_FRAME
555 else:
556 flags = 0
557 self.flush(flags)
558 if self.interpreter.options.all:
559 self.interpreter.present(dest, 'surface_copy_dest', destx, desty, width, height)
560
561 def is_texture_referenced(self, texture, face, level):
562 #return self.real.is_texture_referenced(format, texture, face, level)
563 pass
564
565 def is_buffer_referenced(self, buf):
566 #return self.real.is_buffer_referenced(format, buf)
567 pass
568
569 def buffer_write(self, buffer, data, size, offset=0):
570 assert size == len(data)
571 self.buffer_write(buffer, data)
572
573 def surface_write(self, surface, data, stride, size):
574 if surface is None:
575 return
576 # assert surface.nblocksy * stride == size
577 surface.put_tile_raw(0, 0, surface.width, surface.height, data, stride)
578
579 def get_transfer(self, texture, sr, usage, box):
580 if texture is None:
581 return None
582 transfer = Transfer(texture, sr, usage, box)
583 if transfer and usage & gallium.PIPE_TRANSFER_READ:
584 if self.interpreter.options.all:
585 surface = texture.get_surface(sr.face, sr.level, box.z)
586 self.interpreter.present(transfer.surface, 'transf_read', box.x, box.y, box.w, box.h)
587 return transfer
588
589 def tex_transfer_destroy(self, transfer):
590 self.interpreter.unregister_object(transfer)
591
592 def transfer_write(self, transfer, data, size):
593 if transfer is None:
594 return
595 self.real.transfer_inline_write(resource, sr, usage, box, data, stride, slice_stride)
596 self.transfertransfer.surface.put_tile_raw(transfer.x, transfer.y, transfer.w, transfer.h, data, transfer.stride)
597 if self.interpreter.options.all:
598 box = transfer.box
599 surface = transfer.resource.get_surface(sr.face, sr.level, box.z)
600 self.interpreter.present(transfer.surface, 'transf_write', box.x, box.y, box.w, box.h)
601
602 def transfer_inline_write(self, resource, sr, usage, box, stride, slice_stride, data):
603 self.real.transfer_inline_write(resource, sr, usage, box, data, stride, slice_stride)
604
605 def _set_dirty(self):
606 if self.interpreter.options.step:
607 self._present()
608 else:
609 self.dirty = True
610
611 def flush(self, flags):
612 self.real.flush(flags)
613 if self.dirty:
614 if flags & gallium.PIPE_FLUSH_FRAME:
615 self._present()
616 self.dirty = False
617 return None
618
619 def clear(self, buffers, rgba, depth, stencil):
620 _rgba = gallium.FloatArray(4)
621 for i in range(4):
622 _rgba[i] = rgba[i]
623 self.real.clear(buffers, _rgba, depth, stencil)
624
625 def _present(self):
626 self.real.flush()
627
628 if self.cbufs and self.cbufs[0]:
629 self.interpreter.present(self.cbufs[0], "cbuf")
630 if self.zsbuf:
631 if self.interpreter.options.all:
632 self.interpreter.present(self.zsbuf, "zsbuf")
633
634
635 class Interpreter(parser.TraceDumper):
636
637 ignore_calls = set((
638 ('pipe_screen', 'is_format_supported'),
639 ('pipe_screen', 'get_param'),
640 ('pipe_screen', 'get_paramf'),
641 ))
642
643 def __init__(self, stream, options):
644 parser.TraceDumper.__init__(self, stream)
645 self.options = options
646 self.objects = {}
647 self.result = None
648 self.globl = Global(self, None)
649 self.call_no = None
650
651 def register_object(self, address, object):
652 self.objects[address] = object
653
654 def unregister_object(self, object):
655 # FIXME:
656 pass
657
658 def lookup_object(self, address):
659 return self.objects[address]
660
661 def interpret(self, trace):
662 for call in trace.calls:
663 self.interpret_call(call)
664
665 def handle_call(self, call):
666 if self.options.stop and call.no > self.options.stop:
667 sys.exit(0)
668
669 if (call.klass, call.method) in self.ignore_calls:
670 return
671
672 self.call_no = call.no
673
674 if self.verbosity(1):
675 parser.TraceDumper.handle_call(self, call)
676 sys.stdout.flush()
677
678 args = [(str(name), self.interpret_arg(arg)) for name, arg in call.args]
679
680 if call.klass:
681 name, obj = args[0]
682 args = args[1:]
683 else:
684 obj = self.globl
685
686 method = getattr(obj, call.method)
687 ret = method(**dict(args))
688
689 if call.ret and isinstance(call.ret, model.Pointer):
690 if ret is None:
691 sys.stderr.write('warning: NULL returned\n')
692 self.register_object(call.ret.address, ret)
693
694 self.call_no = None
695
696 def interpret_arg(self, node):
697 translator = Translator(self)
698 return translator.visit(node)
699
700 def verbosity(self, level):
701 return self.options.verbosity >= level
702
703 def present(self, surface, description, x=None, y=None, w=None, h=None):
704 if self.call_no < self.options.start:
705 return
706
707 if self.options.images:
708 filename = '%04u_%s.png' % (self.call_no, description)
709 save_image(filename, surface, x, y, w, h)
710 else:
711 title = '%u. %s' % (self.call_no, description)
712 show_image(surface, title, x, y, w, h)
713
714
715 class Main(parser.Main):
716
717 def get_optparser(self):
718 optparser = parser.Main.get_optparser(self)
719 optparser.add_option("-q", "--quiet", action="store_const", const=0, dest="verbosity", help="no messages")
720 optparser.add_option("-v", "--verbose", action="count", dest="verbosity", default=1, help="increase verbosity level")
721 optparser.add_option("-i", "--images", action="store_true", dest="images", default=False, help="save images instead of showing them")
722 optparser.add_option("-a", "--all", action="store_true", dest="all", default=False, help="show depth, stencil, and transfers")
723 optparser.add_option("-s", "--step", action="store_true", dest="step", default=False, help="step trhough every draw")
724 optparser.add_option("-f", "--from", action="store", type="int", dest="start", default=0, help="from call no")
725 optparser.add_option("-t", "--to", action="store", type="int", dest="stop", default=0, help="until call no")
726 return optparser
727
728 def process_arg(self, stream, options):
729 parser = Interpreter(stream, options)
730 parser.parse()
731
732
733 if __name__ == '__main__':
734 Main().main()