2 ##########################################################################
4 # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
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:
15 # The above copyright notice and this permission notice (including the
16 # next paragraph) shall be included in all copies or substantial portions
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.
27 ##########################################################################
35 import parse as parser
39 from struct import unpack_from
41 def unpack_from(fmt, buf, offset=0):
42 size = struct.calcsize(fmt)
43 return struct.unpack(fmt, buf[offset:offset + size])
46 def make_image(surface, x=None, y=None, w=None, h=None):
54 h = surface.height - y
55 data = surface.get_tile_rgba8(0, 0, surface.width, surface.height)
58 outimage = Image.fromstring('RGBA', (surface.width, surface.height), data, "raw", 'RGBA', 0, 1)
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")
65 def show_image(surface, title, x=None, y=None, w=None, h=None):
66 outimage = make_image(surface, x, y, w, h)
69 from PIL import Image, ImageTk
74 image1 = ImageTk.PhotoImage(outimage)
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')
89 # A basic Python class can pass as a C-like structure
94 "pipe_blend_color": gallium.BlendColor,
95 "pipe_blend_state": gallium.Blend,
96 #"pipe_clip_state": gallium.Clip,
97 #"pipe_constant_buffer": gallium.ConstantBuffer,
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_format_block": gallium.FormatBlock,
103 #"pipe_framebuffer_state": gallium.Framebuffer,
104 "pipe_poly_stipple": gallium.PolyStipple,
105 "pipe_rasterizer_state": gallium.Rasterizer,
106 "pipe_sampler_state": gallium.Sampler,
107 "pipe_scissor_state": gallium.Scissor,
108 #"pipe_shader_state": gallium.Shader,
109 #"pipe_vertex_buffer": gallium.VertexBuffer,
110 "pipe_vertex_element": gallium.VertexElement,
111 "pipe_viewport_state": gallium.Viewport,
112 #"pipe_texture": gallium.Texture,
116 member_array_factories = {
117 "pipe_rasterizer_state": {"sprite_coord_mode": gallium.ByteArray},
118 "pipe_poly_stipple": {"stipple": gallium.UnsignedArray},
119 "pipe_viewport_state": {"scale": gallium.FloatArray, "translate": gallium.FloatArray},
120 #"pipe_clip_state": {"ucp": gallium.FloatArray},
121 "pipe_depth_stencil_alpha_state": {"stencil": gallium.StencilArray},
122 "pipe_blend_color": {"color": gallium.FloatArray},
123 "pipe_sampler_state": {"border_color": gallium.FloatArray},
127 class Translator(model.Visitor):
128 """Translate model arguments into regular Python objects"""
130 def __init__(self, interpreter):
131 self.interpreter = interpreter
134 def visit(self, node):
139 def visit_literal(self, node):
140 self.result = node.value
142 def visit_named_constant(self, node):
143 # lookup the named constant in the gallium module
144 self.result = getattr(gallium, node.name)
146 def visit_array(self, node):
148 for element in node.elements:
149 array.append(self.visit(element))
152 def visit_struct(self, node):
153 struct_factory = struct_factories.get(node.name, Struct)
154 struct = struct_factory()
155 for member_name, member_node in node.members:
156 member_value = self.visit(member_node)
158 array_factory = member_array_factories[node.name][member_name]
162 assert isinstance(member_value, list)
163 array = array_factory(len(member_value))
164 for i in range(len(member_value)):
165 array[i] = member_value[i]
167 #print node.name, member_name, member_value
168 assert isinstance(struct, Struct) or hasattr(struct, member_name)
169 setattr(struct, member_name, member_value)
172 def visit_pointer(self, node):
173 self.result = self.interpreter.lookup_object(node.address)
178 def __init__(self, interpreter, real):
179 self.interpreter = interpreter
183 class Global(Object):
185 def __init__(self, interpreter, real):
186 self.interpreter = interpreter
189 def pipe_winsys_create(self):
190 return Winsys(self.interpreter, gallium.Device())
192 def pipe_screen_create(self, winsys=None):
194 real = gallium.Device()
197 return Screen(self.interpreter, real)
199 def pipe_context_create(self, screen):
200 context = screen.real.context_create()
201 return Context(self.interpreter, context)
204 class Winsys(Object):
206 def __init__(self, interpreter, real):
207 self.interpreter = interpreter
213 def user_buffer_create(self, data, size):
214 # We don't really care to distinguish between user and regular buffers
215 buffer = self.real.buffer_create(size,
217 gallium.PIPE_BUFFER_USAGE_CPU_READ |
218 gallium.PIPE_BUFFER_USAGE_CPU_WRITE )
219 assert size == len(data)
223 def buffer_create(self, alignment, usage, size):
224 return self.real.buffer_create(size, alignment, usage)
226 def buffer_destroy(self, buffer):
229 def buffer_write(self, buffer, data, size):
230 assert size == len(data)
233 def fence_finish(self, fence, flags):
236 def fence_reference(self, dst, src):
239 def flush_frontbuffer(self, surface):
242 def surface_alloc(self):
245 def surface_release(self, surface):
251 def __init__(self, surface, x, y, w, h):
252 self.surface = surface
259 class Screen(Object):
267 def get_vendor(self):
270 def get_param(self, param):
273 def get_paramf(self, param):
276 def is_format_supported(self, format, target, tex_usage, geom_flags):
277 return self.real.is_format_supported(format, target, tex_usage, geom_flags)
279 def texture_create(self, templat):
280 return self.real.texture_create(
281 format = templat.format,
282 width = templat.width[0],
283 height = templat.height[0],
284 depth = templat.depth[0],
285 last_level = templat.last_level,
286 target = templat.target,
287 tex_usage = templat.tex_usage,
290 def texture_destroy(self, texture):
291 self.interpreter.unregister_object(texture)
293 def texture_release(self, surface):
296 def get_tex_surface(self, texture, face, level, zslice, usage):
299 return texture.get_surface(face, level, zslice)
301 def tex_surface_destroy(self, surface):
302 self.interpreter.unregister_object(surface)
304 def tex_surface_release(self, surface):
307 def surface_write(self, surface, data, stride, size):
310 assert surface.nblocksy * stride == size
311 surface.put_tile_raw(0, 0, surface.width, surface.height, data, stride)
313 def get_tex_transfer(self, texture, face, level, zslice, usage, x, y, w, h):
316 transfer = Transfer(texture.get_surface(face, level, zslice), x, y, w, h)
317 if transfer and usage != gallium.PIPE_TRANSFER_WRITE:
318 if self.interpreter.options.all:
319 self.interpreter.present(transfer.surface, 'transf_read', x, y, w, h)
322 def tex_transfer_destroy(self, transfer):
323 self.interpreter.unregister_object(transfer)
325 def transfer_write(self, transfer, stride, data, size):
328 transfer.surface.put_tile_raw(transfer.x, transfer.y, transfer.w, transfer.h, data, stride)
329 if self.interpreter.options.all:
330 self.interpreter.present(transfer.surface, 'transf_write', transfer.x, transfer.y, transfer.w, transfer.h)
332 def user_buffer_create(self, data, size):
333 # We don't really care to distinguish between user and regular buffers
334 buffer = self.real.buffer_create(size,
336 gallium.PIPE_BUFFER_USAGE_CPU_READ |
337 gallium.PIPE_BUFFER_USAGE_CPU_WRITE )
338 assert size == len(data)
342 def buffer_create(self, alignment, usage, size):
343 return self.real.buffer_create(size, alignment, usage)
345 def buffer_destroy(self, buffer):
348 def buffer_write(self, buffer, data, size, offset=0):
349 assert size == len(data)
352 def fence_finish(self, fence, flags):
355 def fence_reference(self, dst, src):
358 def flush_frontbuffer(self, surface):
362 class Context(Object):
364 def __init__(self, interpreter, real):
365 Object.__init__(self, interpreter, real)
375 def create_blend_state(self, state):
378 def bind_blend_state(self, state):
379 if state is not None:
380 self.real.set_blend(state)
382 def delete_blend_state(self, state):
385 def create_sampler_state(self, state):
388 def delete_sampler_state(self, state):
391 def bind_sampler_states(self, num_states, states):
392 for i in range(num_states):
393 self.real.set_sampler(i, states[i])
395 def create_rasterizer_state(self, state):
398 def bind_rasterizer_state(self, state):
399 if state is not None:
400 self.real.set_rasterizer(state)
402 def delete_rasterizer_state(self, state):
405 def create_depth_stencil_alpha_state(self, state):
408 def bind_depth_stencil_alpha_state(self, state):
409 if state is not None:
410 self.real.set_depth_stencil_alpha(state)
412 def delete_depth_stencil_alpha_state(self, state):
415 def create_fs_state(self, state):
416 tokens = str(state.tokens)
417 shader = gallium.Shader(tokens)
420 create_vs_state = create_fs_state
422 def bind_fs_state(self, state):
423 self.real.set_fragment_shader(state)
425 def bind_vs_state(self, state):
426 self.real.set_vertex_shader(state)
428 def delete_fs_state(self, state):
431 delete_vs_state = delete_fs_state
433 def set_blend_color(self, state):
434 self.real.set_blend_color(state)
436 def set_clip_state(self, state):
437 _state = gallium.Clip()
441 ucp = gallium.FloatArray(gallium.PIPE_MAX_CLIP_PLANES*4)
442 for i in range(len(state.ucp)):
443 for j in range(len(state.ucp[i])):
444 ucp[i*4 + j] = state.ucp[i][j]
446 self.real.set_clip(_state)
448 def dump_constant_buffer(self, buffer):
449 if not self.interpreter.verbosity(2):
455 for offset in range(0, len(data), struct.calcsize(format)):
456 x, y, z, w = unpack_from(format, data, offset)
457 sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))
460 def set_constant_buffer(self, shader, index, buffer):
461 if buffer is not None:
462 self.real.set_constant_buffer(shader, index, buffer.buffer)
464 self.dump_constant_buffer(buffer.buffer)
466 def set_framebuffer_state(self, state):
467 _state = gallium.Framebuffer()
468 _state.width = state.width
469 _state.height = state.height
470 _state.nr_cbufs = state.nr_cbufs
471 for i in range(len(state.cbufs)):
472 _state.set_cbuf(i, state.cbufs[i])
473 _state.set_zsbuf(state.zsbuf)
474 self.real.set_framebuffer(_state)
476 self.cbufs = state.cbufs
477 self.zsbuf = state.zsbuf
479 def set_polygon_stipple(self, state):
480 self.real.set_polygon_stipple(state)
482 def set_scissor_state(self, state):
483 self.real.set_scissor(state)
485 def set_viewport_state(self, state):
486 self.real.set_viewport(state)
488 def set_sampler_textures(self, num_textures, textures):
489 for i in range(num_textures):
490 self.real.set_sampler_texture(i, textures[i])
492 def set_vertex_buffers(self, num_buffers, buffers):
493 self.vbufs = buffers[0:num_buffers]
494 for i in range(num_buffers):
496 self.real.set_vertex_buffer(
498 stride = vbuf.stride,
499 max_index = vbuf.max_index,
500 buffer_offset = vbuf.buffer_offset,
501 buffer = vbuf.buffer,
504 def set_vertex_elements(self, num_elements, elements):
505 self.velems = elements[0:num_elements]
506 for i in range(num_elements):
507 self.real.set_vertex_element(i, elements[i])
508 self.real.set_vertex_elements(num_elements)
510 def set_edgeflags(self, bitfield):
514 def dump_vertices(self, start, count):
515 if not self.interpreter.verbosity(2):
518 for index in range(start, start + count):
519 if index >= start + 16:
520 sys.stdout.write('\t...\n')
522 sys.stdout.write('\t{\n')
523 for velem in self.velems:
524 vbuf = self.vbufs[velem.vertex_buffer_index]
526 offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index
528 gallium.PIPE_FORMAT_R32_FLOAT: 'f',
529 gallium.PIPE_FORMAT_R32G32_FLOAT: '2f',
530 gallium.PIPE_FORMAT_R32G32B32_FLOAT: '3f',
531 gallium.PIPE_FORMAT_R32G32B32A32_FLOAT: '4f',
532 gallium.PIPE_FORMAT_B8G8R8A8_UNORM: '4B',
535 data = vbuf.buffer.read()
536 values = unpack_from(format, data, offset)
537 sys.stdout.write('\t\t{' + ', '.join(map(str, values)) + '},\n')
538 assert len(values) == velem.nr_components
539 sys.stdout.write('\t},\n')
541 def dump_indices(self, ibuf, isize, start, count):
542 if not self.interpreter.verbosity(2):
551 assert struct.calcsize(format) == isize
554 maxindex, minindex = 0, 0xffffffff
556 sys.stdout.write('\t{\n')
557 for i in range(start, start + count):
559 sys.stdout.write('\t...\n')
562 index, = unpack_from(format, data, offset)
563 sys.stdout.write('\t\t%u,\n' % index)
564 minindex = min(minindex, index)
565 maxindex = max(maxindex, index)
566 sys.stdout.write('\t},\n')
568 return minindex, maxindex
570 def draw_arrays(self, mode, start, count):
571 self.dump_vertices(start, count)
573 self.real.draw_arrays(mode, start, count)
576 def draw_elements(self, indexBuffer, indexSize, mode, start, count):
577 if self.interpreter.verbosity(2):
578 minindex, maxindex = self.dump_indices(indexBuffer, indexSize, start, count)
579 self.dump_vertices(minindex, maxindex - minindex)
581 self.real.draw_elements(indexBuffer, indexSize, mode, start, count)
584 def draw_range_elements(self, indexBuffer, indexSize, minIndex, maxIndex, mode, start, count):
585 if self.interpreter.verbosity(2):
586 minindex, maxindex = self.dump_indices(indexBuffer, indexSize, start, count)
587 minindex = min(minindex, minIndex)
588 maxindex = min(maxindex, maxIndex)
589 self.dump_vertices(minindex, maxindex - minindex)
591 self.real.draw_range_elements(indexBuffer, indexSize, minIndex, maxIndex, mode, start, count)
594 def surface_copy(self, dest, destx, desty, src, srcx, srcy, width, height):
595 if dest is not None and src is not None:
596 self.real.surface_copy(dest, destx, desty, src, srcx, srcy, width, height)
598 def is_texture_referenced(self, texture, face, level):
599 #return self.real.is_texture_referenced(format, texture, face, level)
602 def is_buffer_referenced(self, buf):
603 #return self.real.is_buffer_referenced(format, buf)
606 def _set_dirty(self):
607 if self.interpreter.options.step:
612 def flush(self, flags):
613 self.real.flush(flags)
615 if flags & gallium.PIPE_FLUSH_FRAME:
620 def clear(self, buffers, rgba, depth, stencil):
621 _rgba = gallium.FloatArray(4)
624 self.real.clear(buffers, _rgba, depth, stencil)
629 if self.cbufs and self.cbufs[0]:
630 self.interpreter.present(self.cbufs[0], "cbuf")
632 if self.interpreter.options.all:
633 self.interpreter.present(self.zsbuf, "zsbuf")
636 class Interpreter(parser.TraceDumper):
639 ('pipe_screen', 'is_format_supported'),
640 ('pipe_screen', 'get_param'),
641 ('pipe_screen', 'get_paramf'),
644 def __init__(self, stream, options):
645 parser.TraceDumper.__init__(self, stream)
646 self.options = options
649 self.globl = Global(self, None)
652 def register_object(self, address, object):
653 self.objects[address] = object
655 def unregister_object(self, object):
659 def lookup_object(self, address):
660 return self.objects[address]
662 def interpret(self, trace):
663 for call in trace.calls:
664 self.interpret_call(call)
666 def handle_call(self, call):
667 if self.options.stop and call.no >= self.options.stop:
670 if (call.klass, call.method) in self.ignore_calls:
673 self.call_no = call.no
675 if self.verbosity(1):
676 parser.TraceDumper.handle_call(self, call)
678 args = [(str(name), self.interpret_arg(arg)) for name, arg in call.args]
686 method = getattr(obj, call.method)
687 ret = method(**dict(args))
689 if call.ret and isinstance(call.ret, model.Pointer):
691 sys.stderr.write('warning: NULL returned\n')
692 self.register_object(call.ret.address, ret)
696 def interpret_arg(self, node):
697 translator = Translator(self)
698 return translator.visit(node)
700 def verbosity(self, level):
701 return self.options.verbosity >= level
703 def present(self, surface, description, x=None, y=None, w=None, h=None):
704 if self.call_no < self.options.start:
707 if self.options.images:
708 filename = '%04u_%s.png' % (self.call_no, description)
709 save_image(filename, surface, x, y, w, h)
711 title = '%u. %s' % (self.call_no, description)
712 show_image(surface, title, x, y, w, h)
715 class Main(parser.Main):
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")
728 def process_arg(self, stream, options):
729 parser = Interpreter(stream, options)
733 if __name__ == '__main__':