2 ##########################################################################
4 # Copyright 2008-2013, VMware, Inc.
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 VMWARE 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 ##########################################################################
38 import parse
as parser
42 from struct
import unpack_from
44 def unpack_from(fmt
, buf
, offset
=0):
45 size
= struct
.calcsize(fmt
)
46 return struct
.unpack(fmt
, buf
[offset
:offset
+ size
])
52 PIPE_SHADER_VERTEX
= 0
53 PIPE_SHADER_FRAGMENT
= 1
54 PIPE_SHADER_GEOMETRY
= 2
55 PIPE_SHADER_COMPUTE
= 3
60 '''JSON serializer function for non-standard Python objects.'''
62 if isinstance(obj
, bytearray
):
63 # TODO: Decide on a single way of dumping blobs
65 # Don't dump full blobs, but merely a description of their size and
67 crc32
= binascii
.crc32(obj
)
70 return 'blob(size=%u,crc32=0x%08x)' % (len(obj
), crc32
)
72 # Dump blobs as an array of 16byte hexadecimals
74 for i
in range(0, len(obj
), 16):
75 res
.append(binascii
.b2a_hex(obj
[i
: i
+16]))
77 # Dump blobs as a single hexadecimal string
78 return binascii
.b2a_hex(obj
)
80 # If the object has a __json__ method, use it.
83 except AttributeError:
92 Python doesn't have C structs, but do its dynamic nature, any object is
97 '''Convert the structure to a standard Python dict, so it can be
101 for name
, value
in self
.__dict
__.items():
102 if not name
.startswith('_'):
107 return repr(self
.__json
__())
110 class Translator(model
.Visitor
):
111 """Translate model arguments into regular Python objects"""
113 def __init__(self
, interpreter
):
114 self
.interpreter
= interpreter
117 def visit(self
, node
):
122 def visit_literal(self
, node
):
123 self
.result
= node
.value
125 def visit_blob(self
, node
):
128 def visit_named_constant(self
, node
):
129 self
.result
= node
.name
131 def visit_array(self
, node
):
133 for element
in node
.elements
:
134 array
.append(self
.visit(element
))
137 def visit_struct(self
, node
):
139 for member_name
, member_node
in node
.members
:
140 member_name
= member_name
.replace('.', '_')
141 member_value
= self
.visit(member_node
)
142 setattr(struct
, member_name
, member_value
)
145 def visit_pointer(self
, node
):
146 self
.result
= self
.interpreter
.lookup_object(node
.address
)
150 '''Base class for classes whose methods can dispatch Gallium calls.'''
152 def __init__(self
, interpreter
):
153 self
.interpreter
= interpreter
156 class Global(Dispatcher
):
157 '''Global name space.
159 For calls that are not associated with objects, i.e, functions and not
163 def pipe_screen_create(self
):
164 return Screen(self
.interpreter
)
166 def pipe_context_create(self
, screen
):
167 return screen
.context_create()
173 def __init__(self
, resource
, usage
, subresource
, box
):
174 self
.resource
= resource
176 self
.subresource
= subresource
180 class Screen(Dispatcher
):
183 def __init__(self
, interpreter
):
184 Dispatcher
.__init
__(self
, interpreter
)
189 def context_create(self
, priv
=None, flags
=0):
190 return Context(self
.interpreter
)
192 def is_format_supported(self
, format
, target
, sample_count
, bind
, geom_flags
):
195 def resource_create(self
, templat
):
197 # Normalize state to avoid spurious differences
198 if resource
.nr_samples
== 0:
199 resource
.nr_samples
= 1
200 if resource
.target
== PIPE_BUFFER
:
201 # We will keep track of buffer contents
202 resource
.data
= bytearray(resource
.width
)
207 def resource_destroy(self
, resource
):
208 self
.interpreter
.unregister_object(resource
)
210 def fence_finish(self
, fence
, timeout
=None):
213 def fence_signalled(self
, fence
):
216 def fence_reference(self
, dst
, src
):
219 def flush_frontbuffer(self
, resource
):
223 class Context(Dispatcher
):
226 # Internal methods variable should be prefixed with '_'
228 def __init__(self
, interpreter
):
229 Dispatcher
.__init
__(self
, interpreter
)
231 # Setup initial state
232 self
._state
= Struct()
233 self
._state
.scissors
= []
234 self
._state
.viewports
= []
235 self
._state
.vertex_buffers
= []
236 self
._state
.vertex_elements
= []
237 self
._state
.vs
= Struct()
238 self
._state
.gs
= Struct()
239 self
._state
.fs
= Struct()
240 self
._state
.vs
.shader
= None
241 self
._state
.gs
.shader
= None
242 self
._state
.fs
.shader
= None
243 self
._state
.vs
.sampler
= []
244 self
._state
.gs
.sampler
= []
245 self
._state
.fs
.sampler
= []
246 self
._state
.vs
.sampler_views
= []
247 self
._state
.gs
.sampler_views
= []
248 self
._state
.fs
.sampler_views
= []
249 self
._state
.vs
.constant_buffer
= []
250 self
._state
.gs
.constant_buffer
= []
251 self
._state
.fs
.constant_buffer
= []
252 self
._state
.render_condition_condition
= 0
253 self
._state
.render_condition_mode
= 0
260 def create_blend_state(self
, state
):
261 # Normalize state to avoid spurious differences
262 if not state
.logicop_enable
:
263 del state
.logicop_func
264 if not state
.rt
[0].blend_enable
:
265 del state
.rt
[0].rgb_src_factor
266 del state
.rt
[0].rgb_dst_factor
267 del state
.rt
[0].rgb_func
268 del state
.rt
[0].alpha_src_factor
269 del state
.rt
[0].alpha_dst_factor
270 del state
.rt
[0].alpha_func
273 def bind_blend_state(self
, state
):
275 self
._state
.blend
= state
277 def delete_blend_state(self
, state
):
280 def create_sampler_state(self
, state
):
283 def delete_sampler_state(self
, state
):
286 def bind_sampler_states(self
, shader
, start
, num_states
, states
):
287 # FIXME: Handle non-zero start
289 self
._get
_stage
_state
(shader
).sampler
= states
291 def bind_vertex_sampler_states(self
, num_states
, states
):
292 # XXX: deprecated method
293 self
._state
.vs
.sampler
= states
295 def bind_geometry_sampler_states(self
, num_states
, states
):
296 # XXX: deprecated method
297 self
._state
.gs
.sampler
= states
299 def bind_fragment_sampler_states(self
, num_states
, states
):
300 # XXX: deprecated method
301 self
._state
.fs
.sampler
= states
303 def create_rasterizer_state(self
, state
):
306 def bind_rasterizer_state(self
, state
):
307 self
._state
.rasterizer
= state
309 def delete_rasterizer_state(self
, state
):
312 def create_depth_stencil_alpha_state(self
, state
):
313 # Normalize state to avoid spurious differences
314 if not state
.alpha
.enabled
:
316 del state
.alpha
.ref_value
318 if not state
.stencil
[i
].enabled
:
319 del state
.stencil
[i
].func
322 def bind_depth_stencil_alpha_state(self
, state
):
323 self
._state
.depth_stencil_alpha
= state
325 def delete_depth_stencil_alpha_state(self
, state
):
328 _tokenLabelRE
= re
.compile('^\s*\d+: ', re
.MULTILINE
)
330 def _create_shader_state(self
, state
):
331 # Strip the labels from the tokens
332 if state
.tokens
is not None:
333 state
.tokens
= self
._tokenLabelRE
.sub('', state
.tokens
)
336 create_vs_state
= _create_shader_state
337 create_gs_state
= _create_shader_state
338 create_fs_state
= _create_shader_state
340 def bind_vs_state(self
, state
):
341 self
._state
.vs
.shader
= state
343 def bind_gs_state(self
, state
):
344 self
._state
.gs
.shader
= state
346 def bind_fs_state(self
, state
):
347 self
._state
.fs
.shader
= state
349 def _delete_shader_state(self
, state
):
352 delete_vs_state
= _delete_shader_state
353 delete_gs_state
= _delete_shader_state
354 delete_fs_state
= _delete_shader_state
356 def set_blend_color(self
, state
):
357 self
._state
.blend_color
= state
359 def set_stencil_ref(self
, state
):
360 self
._state
.stencil_ref
= state
362 def set_clip_state(self
, state
):
363 self
._state
.clip
= state
365 def _dump_constant_buffer(self
, buffer):
366 if not self
.interpreter
.verbosity(2):
369 data
= self
.real
.buffer_read(buffer)
372 for offset
in range(0, len(data
), struct
.calcsize(format
)):
373 x
, y
, z
, w
= unpack_from(format
, data
, offset
)
374 sys
.stdout
.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index
, x
, y
, z
, w
))
378 def _get_stage_state(self
, shader
):
379 if shader
== PIPE_SHADER_VERTEX
:
380 return self
._state
.vs
381 if shader
== PIPE_SHADER_GEOMETRY
:
382 return self
._state
.gs
383 if shader
== PIPE_SHADER_FRAGMENT
:
384 return self
._state
.fs
387 def set_constant_buffer(self
, shader
, index
, constant_buffer
):
388 self
._update
(self
._get
_stage
_state
(shader
).constant_buffer
, index
, 1, [constant_buffer
])
390 def set_framebuffer_state(self
, state
):
391 self
._state
.fb
= state
393 def set_polygon_stipple(self
, state
):
394 self
._state
.polygon_stipple
= state
396 def _update(self
, array
, start_slot
, num_slots
, states
):
397 if not isinstance(states
, list):
398 # XXX: trace is not serializing multiple scissors/viewports properly yet
401 while len(array
) < start_slot
+ num_slots
:
403 for i
in range(num_slots
):
404 array
[start_slot
+ i
] = states
[i
]
406 def set_scissor_states(self
, start_slot
, num_scissors
, states
):
407 self
._update
(self
._state
.scissors
, start_slot
, num_scissors
, states
)
409 def set_viewport_states(self
, start_slot
, num_viewports
, states
):
410 self
._update
(self
._state
.viewports
, start_slot
, num_viewports
, states
)
412 def create_sampler_view(self
, resource
, templ
):
413 templ
.resource
= resource
416 def sampler_view_destroy(self
, view
):
419 def set_sampler_views(self
, shader
, start
, num
, views
):
420 # FIXME: Handle non-zero start
422 self
._get
_stage
_state
(shader
).sampler_views
= views
424 def set_fragment_sampler_views(self
, num
, views
):
426 self
._state
.fs
.sampler_views
= views
428 def set_geometry_sampler_views(self
, num
, views
):
430 self
._state
.gs
.sampler_views
= views
432 def set_vertex_sampler_views(self
, num
, views
):
434 self
._state
.vs
.sampler_views
= views
436 def set_vertex_buffers(self
, start_slot
, num_buffers
, buffers
):
437 self
._update
(self
._state
.vertex_buffers
, start_slot
, num_buffers
, buffers
)
439 def create_vertex_elements_state(self
, num_elements
, elements
):
440 return elements
[0:num_elements
]
442 def bind_vertex_elements_state(self
, state
):
443 self
._state
.vertex_elements
= state
445 def delete_vertex_elements_state(self
, state
):
448 def set_index_buffer(self
, ib
):
449 self
._state
.index_buffer
= ib
451 # Don't dump more than this number of indices/vertices
454 def _merge_indices(self
, info
):
455 '''Merge the vertices into our state.'''
457 index_size
= self
._state
.index_buffer
.index_size
465 assert struct
.calcsize(format
) == index_size
467 if self
._state
.index_buffer
.buffer is None:
468 # Could happen with index in user memory
471 data
= self
._state
.index_buffer
.buffer.data
472 max_index
, min_index
= 0, 0xffffffff
474 count
= min(info
.count
, self
.MAX_ELEMENTS
)
476 for i
in xrange(info
.start
, info
.start
+ count
):
477 offset
= self
._state
.index_buffer
.offset
+ i
*index_size
478 if offset
+ index_size
> len(data
):
481 index
, = unpack_from(format
, data
, offset
)
482 indices
.append(index
)
483 min_index
= min(min_index
, index
)
484 max_index
= max(max_index
, index
)
486 self
._state
.indices
= indices
488 return min_index
+ info
.index_bias
, max_index
+ info
.index_bias
490 def _merge_vertices(self
, start
, count
):
491 '''Merge the vertices into our state.'''
493 count
= min(count
, self
.MAX_ELEMENTS
)
495 for index
in xrange(start
, start
+ count
):
496 if index
>= start
+ 16:
497 sys
.stdout
.write('\t...\n')
500 for velem
in self
._state
.vertex_elements
:
501 vbuf
= self
._state
.vertex_buffers
[velem
.vertex_buffer_index
]
502 resource
= vbuf
.buffer_resource
508 offset
= vbuf
.buffer_offset
+ velem
.src_offset
+ vbuf
.stride
*index
510 'PIPE_FORMAT_R32_FLOAT': 'f',
511 'PIPE_FORMAT_R32G32_FLOAT': '2f',
512 'PIPE_FORMAT_R32G32B32_FLOAT': '3f',
513 'PIPE_FORMAT_R32G32B32A32_FLOAT': '4f',
514 'PIPE_FORMAT_R32_UINT': 'I',
515 'PIPE_FORMAT_R32G32_UINT': '2I',
516 'PIPE_FORMAT_R32G32B32_UINT': '3I',
517 'PIPE_FORMAT_R32G32B32A32_UINT': '4I',
518 'PIPE_FORMAT_R8_UINT': 'B',
519 'PIPE_FORMAT_R8G8_UINT': '2B',
520 'PIPE_FORMAT_R8G8B8_UINT': '3B',
521 'PIPE_FORMAT_R8G8B8A8_UINT': '4B',
522 'PIPE_FORMAT_A8R8G8B8_UNORM': '4B',
523 'PIPE_FORMAT_R8G8B8A8_UNORM': '4B',
524 'PIPE_FORMAT_B8G8R8A8_UNORM': '4B',
525 'PIPE_FORMAT_R16G16B16_SNORM': '3h',
529 attribute
= unpack_from(format
, data
, offset
)
530 vertex
.append(attribute
)
532 vertices
.append(vertex
)
534 self
._state
.vertices
= vertices
536 def render_condition(self
, query
, condition
= 0, mode
= 0):
537 self
._state
.render_condition_query
= query
538 self
._state
.render_condition_condition
= condition
539 self
._state
.render_condition_mode
= mode
541 def set_stream_output_targets(self
, num_targets
, tgs
, offsets
):
542 self
._state
.so_targets
= tgs
543 self
._state
.offsets
= offsets
545 def draw_vbo(self
, info
):
548 if self
.interpreter
.call_no
< self
.interpreter
.options
.call
and \
549 self
._draw
_no
< self
.interpreter
.options
.draw
:
552 # Merge the all draw state
554 self
._state
.draw
= info
556 if info
.index_size
!= 0:
557 min_index
, max_index
= self
._merge
_indices
(info
)
559 min_index
= info
.start
560 max_index
= info
.start
+ info
.count
- 1
561 self
._merge
_vertices
(min_index
, max_index
- min_index
+ 1)
565 _dclRE
= re
.compile('^DCL\s+(IN|OUT|SAMP|SVIEW)\[([0-9]+)\].*$', re
.MULTILINE
)
567 def _normalize_stage_state(self
, stage
):
571 if stage
.shader
is not None and stage
.shader
.tokens
is not None:
572 for mo
in self
._dclRE
.finditer(stage
.shader
.tokens
):
575 register
= registers
.setdefault(file_
, set())
576 register
.add(int(index
))
578 if 'SAMP' in registers
and 'SVIEW' not in registers
:
579 registers
['SVIEW'] = registers
['SAMP']
582 #("CONST", "constant_buffer"),
584 ("SVIEW", "sampler_views"),
587 for fileName
, attrName
in mapping
:
588 register
= registers
.setdefault(fileName
, set())
589 attr
= getattr(stage
, attrName
)
590 for index
in range(len(attr
)):
591 if index
not in register
:
593 while attr
and attr
[-1] is None:
596 def _dump_state(self
):
597 '''Dump our state to JSON and terminate.'''
599 state
= copy
.deepcopy(self
._state
)
601 self
._normalize
_stage
_state
(state
.vs
)
602 self
._normalize
_stage
_state
(state
.gs
)
603 self
._normalize
_stage
_state
(state
.fs
)
611 separators
= (',', ': ')
616 def resource_copy_region(self
, dst
, dst_level
, dstx
, dsty
, dstz
, src
, src_level
, src_box
):
617 if dst
.target
== PIPE_BUFFER
or src
.target
== PIPE_BUFFER
:
618 assert dst
.target
== PIPE_BUFFER
and src
.target
== PIPE_BUFFER
619 assert dst_level
== 0
622 assert src_level
== 0
623 assert src_box
.y
== 0
624 assert src_box
.z
== 0
625 assert src_box
.height
== 1
626 assert src_box
.depth
== 1
627 dst
.data
[dstx
: dstx
+ src_box
.width
] = src
.data
[src_box
.x
: src_box
.x
+ src_box
.width
]
630 def is_resource_referenced(self
, texture
, face
, level
):
633 def get_transfer(self
, texture
, sr
, usage
, box
):
636 transfer
= Transfer(texture
, sr
, usage
, box
)
639 def tex_transfer_destroy(self
, transfer
):
640 self
.interpreter
.unregister_object(transfer
)
642 def buffer_subdata(self
, resource
, usage
, data
, box
=None, offset
=None, size
=None, level
=None, stride
=None, layer_stride
=None):
644 # XXX trace_context_transfer_unmap generates brokens buffer_subdata
645 assert offset
is None
652 if resource
is not None and resource
.target
== PIPE_BUFFER
:
653 data
= data
.getValue()
654 assert len(data
) >= size
655 assert offset
+ size
<= len(resource
.data
)
656 resource
.data
[offset
: offset
+ size
] = data
[:size
]
658 def texture_subdata(self
, resource
, level
, usage
, box
, data
, stride
, layer_stride
):
661 def transfer_inline_write(self
, resource
, level
, usage
, box
, stride
, layer_stride
, data
):
662 if resource
is not None and resource
.target
== PIPE_BUFFER
:
663 data
= data
.getValue()
664 assert len(data
) >= box
.width
665 assert box
.x
+ box
.width
<= len(resource
.data
)
666 resource
.data
[box
.x
: box
.x
+ box
.width
] = data
[:box
.width
]
668 def flush(self
, flags
):
669 # Return a fake fence
670 return self
.interpreter
.call_no
672 def clear(self
, buffers
, color
, depth
, stencil
):
675 def clear_render_target(self
, dst
, rgba
, dstx
, dsty
, width
, height
):
678 def clear_depth_stencil(self
, dst
, clear_flags
, depth
, stencil
, dstx
, dsty
, width
, height
):
681 def create_surface(self
, resource
, surf_tmpl
):
682 assert resource
is not None
683 surf_tmpl
.resource
= resource
686 def surface_destroy(self
, surface
):
687 self
.interpreter
.unregister_object(surface
)
689 def create_query(self
, query_type
, index
):
692 def destroy_query(self
, query
):
695 def begin_query(self
, query
):
698 def end_query(self
, query
):
701 def create_stream_output_target(self
, res
, buffer_offset
, buffer_size
):
703 so_target
.resource
= res
704 so_target
.offset
= buffer_offset
705 so_target
.size
= buffer_size
709 class Interpreter(parser
.TraceDumper
):
710 '''Specialization of a trace parser that interprets the calls as it goes
714 ('pipe_screen', 'is_format_supported'),
715 ('pipe_screen', 'get_name'),
716 ('pipe_screen', 'get_vendor'),
717 ('pipe_screen', 'get_param'),
718 ('pipe_screen', 'get_paramf'),
719 ('pipe_screen', 'get_shader_param'),
720 ('pipe_context', 'clear_render_target'), # XXX workaround trace bugs
723 def __init__(self
, stream
, options
):
724 parser
.TraceDumper
.__init
__(self
, stream
, sys
.stderr
)
725 self
.options
= options
728 self
.globl
= Global(self
)
731 def register_object(self
, address
, object):
732 self
.objects
[address
] = object
734 def unregister_object(self
, object):
738 def lookup_object(self
, address
):
740 return self
.objects
[address
]
742 # Could happen, e.g., with user memory pointers
745 def interpret(self
, trace
):
746 for call
in trace
.calls
:
747 self
.interpret_call(call
)
749 def handle_call(self
, call
):
750 if (call
.klass
, call
.method
) in self
.ignoredCalls
:
753 self
.call_no
= call
.no
755 if self
.verbosity(1):
756 # Write the call to stderr (as stdout would corrupt the JSON output)
759 parser
.TraceDumper
.handle_call(self
, call
)
763 args
= [(str(name
), self
.interpret_arg(arg
)) for name
, arg
in call
.args
]
771 method
= getattr(obj
, call
.method
)
772 ret
= method(**dict(args
))
774 # Keep track of created pointer objects.
775 if call
.ret
and isinstance(call
.ret
, model
.Pointer
):
777 sys
.stderr
.write('warning: NULL returned\n')
778 self
.register_object(call
.ret
.address
, ret
)
782 def interpret_arg(self
, node
):
783 translator
= Translator(self
)
784 return translator
.visit(node
)
786 def verbosity(self
, level
):
787 return self
.options
.verbosity
>= level
790 class Main(parser
.Main
):
792 def get_optparser(self
):
793 '''Custom options.'''
795 optparser
= parser
.Main
.get_optparser(self
)
796 optparser
.add_option("-q", "--quiet", action
="store_const", const
=0, dest
="verbosity", help="no messages")
797 optparser
.add_option("-v", "--verbose", action
="count", dest
="verbosity", default
=0, help="increase verbosity level")
798 optparser
.add_option("-c", "--call", action
="store", type="int", dest
="call", default
=0xffffffff, help="dump on this call")
799 optparser
.add_option("-d", "--draw", action
="store", type="int", dest
="draw", default
=0xffffffff, help="dump on this draw")
802 def process_arg(self
, stream
, options
):
803 parser
= Interpreter(stream
, options
)
807 if __name__
== '__main__':