Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. #!/usr/bin/env python
  2. ##########################################################################
  3. #
  4. # Copyright 2008-2013, VMware, Inc.
  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 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.
  26. #
  27. ##########################################################################
  28.  
  29.  
  30. import sys
  31. import struct
  32. import json
  33. import binascii
  34. import re
  35.  
  36. import model
  37. import parse as parser
  38.  
  39.  
  40. try:
  41.     from struct import unpack_from
  42. except ImportError:
  43.     def unpack_from(fmt, buf, offset=0):
  44.         size = struct.calcsize(fmt)
  45.         return struct.unpack(fmt, buf[offset:offset + size])
  46.  
  47. #
  48. # Some constants
  49. #
  50. PIPE_BUFFER = 0
  51.  
  52.  
  53. def serialize(obj):
  54.     '''JSON serializer function for non-standard Python objects.'''
  55.  
  56.     if isinstance(obj, bytearray):
  57.         # TODO: Decide on a single way of dumping blobs
  58.         if False:
  59.             # Don't dump full blobs, but merely a description of their size and
  60.             # CRC32 hash.
  61.             crc32 = binascii.crc32(obj)
  62.             if crc32 < 0:
  63.                 crc32 += 0x100000000
  64.             return 'blob(size=%u,crc32=0x%08x)' % (len(obj), crc32)
  65.         if True:
  66.             # Dump blobs as an array of 16byte hexadecimals
  67.             res = []
  68.             for i in range(0, len(obj), 16):
  69.                 res.append(binascii.b2a_hex(obj[i: i+16]))
  70.             return res
  71.         # Dump blobs as a single hexadecimal string
  72.         return binascii.b2a_hex(obj)
  73.  
  74.     # If the object has a __json__ method, use it.
  75.     try:
  76.         method = obj.__json__
  77.     except AttributeError:
  78.         raise TypeError(obj)
  79.     else:
  80.         return method()
  81.  
  82.  
  83. class Struct:
  84.     """C-like struct.
  85.    
  86.    Python doesn't have C structs, but do its dynamic nature, any object is
  87.    pretty close.
  88.    """
  89.  
  90.     def __json__(self):
  91.         '''Convert the structure to a standard Python dict, so it can be
  92.        serialized.'''
  93.  
  94.         obj = {}
  95.         for name, value in self.__dict__.items():
  96.             if not name.startswith('_'):
  97.                 obj[name] = value
  98.         return obj
  99.  
  100.  
  101. class Translator(model.Visitor):
  102.     """Translate model arguments into regular Python objects"""
  103.  
  104.     def __init__(self, interpreter):
  105.         self.interpreter = interpreter
  106.         self.result = None
  107.  
  108.     def visit(self, node):
  109.         self.result = None
  110.         node.visit(self)
  111.         return self.result
  112.        
  113.     def visit_literal(self, node):
  114.         self.result = node.value
  115.    
  116.     def visit_blob(self, node):
  117.         self.result = node
  118.    
  119.     def visit_named_constant(self, node):
  120.         self.result = node.name
  121.    
  122.     def visit_array(self, node):
  123.         array = []
  124.         for element in node.elements:
  125.             array.append(self.visit(element))
  126.         self.result = array
  127.    
  128.     def visit_struct(self, node):
  129.         struct = Struct()
  130.         for member_name, member_node in node.members:
  131.             member_value = self.visit(member_node)
  132.             setattr(struct, member_name, member_value)
  133.         self.result = struct
  134.    
  135.     def visit_pointer(self, node):
  136.         self.result = self.interpreter.lookup_object(node.address)
  137.  
  138.  
  139. class Dispatcher:
  140.     '''Base class for classes whose methods can dispatch Gallium calls.'''
  141.    
  142.     def __init__(self, interpreter):
  143.         self.interpreter = interpreter
  144.        
  145.  
  146. class Global(Dispatcher):
  147.     '''Global name space.
  148.  
  149.    For calls that are not associated with objects, i.e, functions and not
  150.    methods.
  151.    '''
  152.  
  153.     def pipe_screen_create(self):
  154.         return Screen(self.interpreter)
  155.    
  156.     def pipe_context_create(self, screen):
  157.         return screen.context_create()
  158.  
  159.    
  160. class Transfer:
  161.     '''pipe_transfer'''
  162.  
  163.     def __init__(self, resource, usage, subresource, box):
  164.         self.resource = resource
  165.         self.usage = usage
  166.         self.subresource = subresource
  167.         self.box = box
  168.  
  169.  
  170. class Screen(Dispatcher):
  171.     '''pipe_screen'''
  172.    
  173.     def __init__(self, interpreter):
  174.         Dispatcher.__init__(self, interpreter)
  175.  
  176.     def destroy(self):
  177.         pass
  178.  
  179.     def context_create(self):
  180.         return Context(self.interpreter)
  181.    
  182.     def is_format_supported(self, format, target, sample_count, bind, geom_flags):
  183.         pass
  184.    
  185.     def resource_create(self, templat):
  186.         resource = templat
  187.         # Normalize state to avoid spurious differences
  188.         if resource.nr_samples == 0:
  189.             resource.nr_samples = 1
  190.         if resource.target == PIPE_BUFFER:
  191.             # We will keep track of buffer contents
  192.             resource.data = bytearray(resource.width)
  193.         return resource
  194.  
  195.     def resource_destroy(self, resource):
  196.         self.interpreter.unregister_object(resource)
  197.  
  198.     def fence_finish(self, fence, flags):
  199.         pass
  200.    
  201.     def fence_signalled(self, fence):
  202.         pass
  203.    
  204.     def fence_reference(self, dst, src):
  205.         pass
  206.    
  207.     def flush_frontbuffer(self, resource):
  208.         pass
  209.  
  210.  
  211. class Context(Dispatcher):
  212.     '''pipe_context'''
  213.  
  214.     # Internal methods variable should be prefixed with '_'
  215.    
  216.     def __init__(self, interpreter):
  217.         Dispatcher.__init__(self, interpreter)
  218.  
  219.         # Setup initial state
  220.         self._state = Struct()
  221.         self._state.scissors = []
  222.         self._state.viewports = []
  223.         self._state.vertex_buffers = []
  224.         self._state.constant_buffer = [[] for i in range(3)]
  225.  
  226.         self._draw_no = 0
  227.  
  228.     def destroy(self):
  229.         pass
  230.    
  231.     def create_blend_state(self, state):
  232.         # Normalize state to avoid spurious differences
  233.         if not state.logicop_enable:
  234.             del state.logicop_func
  235.         if not state.rt[0].blend_enable:
  236.             del state.rt[0].rgb_src_factor
  237.             del state.rt[0].rgb_dst_factor
  238.             del state.rt[0].rgb_func
  239.             del state.rt[0].alpha_src_factor
  240.             del state.rt[0].alpha_dst_factor
  241.             del state.rt[0].alpha_func
  242.         return state
  243.  
  244.     def bind_blend_state(self, state):
  245.         # Normalize state
  246.         self._state.blend = state
  247.  
  248.     def delete_blend_state(self, state):
  249.         pass
  250.    
  251.     def create_sampler_state(self, state):
  252.         return state
  253.  
  254.     def delete_sampler_state(self, state):
  255.         pass
  256.  
  257.     def bind_vertex_sampler_states(self, num_states, states):
  258.         self._state.vertex_sampler = states
  259.        
  260.     def bind_geometry_sampler_states(self, num_states, states):
  261.         self._state.geometry_sampler = states
  262.        
  263.     def bind_fragment_sampler_states(self, num_states, states):
  264.         self._state.fragment_sampler = states
  265.        
  266.     def create_rasterizer_state(self, state):
  267.         return state
  268.  
  269.     def bind_rasterizer_state(self, state):
  270.         self._state.rasterizer = state
  271.        
  272.     def delete_rasterizer_state(self, state):
  273.         pass
  274.    
  275.     def create_depth_stencil_alpha_state(self, state):
  276.         # Normalize state to avoid spurious differences
  277.         if not state.alpha.enabled:
  278.             del state.alpha.func
  279.             del state.alpha.ref_value
  280.         for i in range(2):
  281.             if not state.stencil[i].enabled:
  282.                 del state.stencil[i].func
  283.         return state
  284.  
  285.     def bind_depth_stencil_alpha_state(self, state):
  286.         self._state.depth_stencil_alpha = state
  287.            
  288.     def delete_depth_stencil_alpha_state(self, state):
  289.         pass
  290.  
  291.     _tokenLabelRE = re.compile('^\s*\d+: ', re.MULTILINE)
  292.  
  293.     def _create_shader_state(self, state):
  294.         # Strip the labels from the tokens
  295.         state.tokens = self._tokenLabelRE.sub('', state.tokens)
  296.         return state
  297.  
  298.     create_vs_state = _create_shader_state
  299.     create_gs_state = _create_shader_state
  300.     create_fs_state = _create_shader_state
  301.    
  302.     def bind_vs_state(self, state):
  303.         self._state.vs = state
  304.  
  305.     def bind_gs_state(self, state):
  306.         self._state.gs = state
  307.        
  308.     def bind_fs_state(self, state):
  309.         self._state.fs = state
  310.        
  311.     def _delete_shader_state(self, state):
  312.         return state
  313.  
  314.     delete_vs_state = _delete_shader_state
  315.     delete_gs_state = _delete_shader_state
  316.     delete_fs_state = _delete_shader_state
  317.  
  318.     def set_blend_color(self, state):
  319.         self._state.blend_color = state
  320.  
  321.     def set_stencil_ref(self, state):
  322.         self._state.stencil_ref = state
  323.  
  324.     def set_clip_state(self, state):
  325.         self._state.clip = state
  326.  
  327.     def _dump_constant_buffer(self, buffer):
  328.         if not self.interpreter.verbosity(2):
  329.             return
  330.  
  331.         data = self.real.buffer_read(buffer)
  332.         format = '4f'
  333.         index = 0
  334.         for offset in range(0, len(data), struct.calcsize(format)):
  335.             x, y, z, w = unpack_from(format, data, offset)
  336.             sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))
  337.             index += 1
  338.         sys.stdout.flush()
  339.  
  340.     def set_constant_buffer(self, shader, index, constant_buffer):
  341.         self._update(self._state.constant_buffer[shader], index, 1, [constant_buffer])
  342.  
  343.     def set_framebuffer_state(self, state):
  344.         self._state.fb = state
  345.  
  346.     def set_polygon_stipple(self, state):
  347.         self._state.polygon_stipple = state
  348.  
  349.     def _update(self, array, start_slot, num_slots, states):
  350.         if not isinstance(states, list):
  351.             # XXX: trace is not serializing multiple scissors/viewports properly yet
  352.             num_slots = 1
  353.             states = [states]
  354.         while len(array) < start_slot + num_slots:
  355.             array.append(None)
  356.         for i in range(num_slots):
  357.             array[start_slot + i] = states[i]
  358.  
  359.     def set_scissor_states(self, start_slot, num_scissors, states):
  360.         self._update(self._state.scissors, start_slot, num_scissors, states)
  361.  
  362.     def set_viewport_states(self, start_slot, num_viewports, states):
  363.         self._update(self._state.viewports, start_slot, num_viewports, states)
  364.  
  365.     def create_sampler_view(self, resource, templ):
  366.         templ.resource = resource
  367.         return templ
  368.  
  369.     def sampler_view_destroy(self, view):
  370.         pass
  371.  
  372.     def set_fragment_sampler_views(self, num, views):
  373.         self._state.fragment_sampler_views = views
  374.  
  375.     def set_geometry_sampler_views(self, num, views):
  376.         self._state.geometry_sampler_views = views
  377.  
  378.     def set_vertex_sampler_views(self, num, views):
  379.         self._state.vertex_sampler_views = views
  380.  
  381.     def set_vertex_buffers(self, start_slot, num_buffers, buffers):
  382.         self._update(self._state.vertex_buffers, start_slot, num_buffers, buffers)
  383.            
  384.     def create_vertex_elements_state(self, num_elements, elements):
  385.         return elements[0:num_elements]
  386.  
  387.     def bind_vertex_elements_state(self, state):
  388.         self._state.vertex_elements = state
  389.  
  390.     def delete_vertex_elements_state(self, state):
  391.         pass
  392.  
  393.     def set_index_buffer(self, ib):
  394.         self._state.index_buffer = ib
  395.  
  396.     # Don't dump more than this number of indices/vertices
  397.     MAX_ELEMENTS = 16
  398.  
  399.     def _merge_indices(self, info):
  400.         '''Merge the vertices into our state.'''
  401.  
  402.         index_size = self._state.index_buffer.index_size
  403.        
  404.         format = {
  405.             1: 'B',
  406.             2: 'H',
  407.             4: 'I',
  408.         }[index_size]
  409.  
  410.         assert struct.calcsize(format) == index_size
  411.  
  412.         data = self._state.index_buffer.buffer.data
  413.         max_index, min_index = 0, 0xffffffff
  414.  
  415.         count = min(info.count, self.MAX_ELEMENTS)
  416.         indices = []
  417.         for i in xrange(info.start, info.start + count):
  418.             offset = self._state.index_buffer.offset + i*index_size
  419.             index, = unpack_from(format, data, offset)
  420.             indices.append(index)
  421.             min_index = min(min_index, index)
  422.             max_index = max(max_index, index)
  423.  
  424.         self._state.indices = indices
  425.  
  426.         return min_index + info.index_bias, max_index + info.index_bias
  427.  
  428.     def _merge_vertices(self, start, count):
  429.         '''Merge the vertices into our state.'''
  430.  
  431.         count = min(count, self.MAX_ELEMENTS)
  432.         vertices = []
  433.         for index in xrange(start, start + count):
  434.             if index >= start + 16:
  435.                 sys.stdout.write('\t...\n')
  436.                 break
  437.             vertex = []
  438.             for velem in self._state.vertex_elements:
  439.                 vbuf = self._state.vertex_buffers[velem.vertex_buffer_index]
  440.                 if vbuf.buffer is None:
  441.                     continue
  442.  
  443.                 data = vbuf.buffer.data
  444.  
  445.                 offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index
  446.                 format = {
  447.                     'PIPE_FORMAT_R32_UINT': 'I',
  448.                     'PIPE_FORMAT_R32_FLOAT': 'f',
  449.                     'PIPE_FORMAT_R32G32_FLOAT': '2f',
  450.                     'PIPE_FORMAT_R32G32B32_FLOAT': '3f',
  451.                     'PIPE_FORMAT_R32G32B32A32_FLOAT': '4f',
  452.                     'PIPE_FORMAT_A8R8G8B8_UNORM': '4B',
  453.                     'PIPE_FORMAT_R8G8B8A8_UNORM': '4B',
  454.                     'PIPE_FORMAT_B8G8R8A8_UNORM': '4B',
  455.                     'PIPE_FORMAT_R16G16B16_SNORM': '3h',
  456.                 }[velem.src_format]
  457.  
  458.                 data = vbuf.buffer.data
  459.                 attribute = unpack_from(format, data, offset)
  460.                 vertex.append(attribute)
  461.  
  462.             vertices.append(vertex)
  463.  
  464.         self._state.vertices = vertices
  465.  
  466.     def draw_vbo(self, info):
  467.         self._draw_no += 1
  468.  
  469.         if self.interpreter.call_no < self.interpreter.options.call and \
  470.             self._draw_no < self.interpreter.options.draw:
  471.                 return
  472.  
  473.         # Merge the all draw state
  474.  
  475.         self._state.draw = info
  476.  
  477.         if info.indexed:
  478.             min_index, max_index = self._merge_indices(info)
  479.         else:
  480.             min_index = info.start
  481.             max_index = info.start + info.count - 1
  482.         self._merge_vertices(min_index, max_index - min_index + 1)
  483.  
  484.         self._dump_state()
  485.  
  486.     def _dump_state(self):
  487.         '''Dump our state to JSON and terminate.'''
  488.  
  489.         json.dump(
  490.             obj = self._state,
  491.             fp = sys.stdout,
  492.             default = serialize,
  493.             sort_keys = True,
  494.             indent = 4,
  495.             separators = (',', ': ')
  496.         )
  497.  
  498.         sys.exit(0)
  499.  
  500.     def resource_copy_region(self, dst, dst_level, dstx, dsty, dstz, src, src_level, src_box):
  501.         if dst.target == PIPE_BUFFER or src.target == PIPE_BUFFER:
  502.             # TODO
  503.             assert 0
  504.         pass
  505.  
  506.     def is_resource_referenced(self, texture, face, level):
  507.         pass
  508.    
  509.     def get_transfer(self, texture, sr, usage, box):
  510.         if texture is None:
  511.             return None
  512.         transfer = Transfer(texture, sr, usage, box)
  513.         return transfer
  514.    
  515.     def tex_transfer_destroy(self, transfer):
  516.         self.interpreter.unregister_object(transfer)
  517.  
  518.     def transfer_inline_write(self, resource, level, usage, box, stride, layer_stride, data):
  519.         if resource.target == PIPE_BUFFER:
  520.             data = data.getValue()
  521.             assert len(data) == box.width
  522.             assert box.x + box.width <= len(resource.data)
  523.             resource.data[box.x : box.x + box.width] = data
  524.  
  525.     def flush(self, flags):
  526.         # Return a fake fence
  527.         return self.interpreter.call_no
  528.  
  529.     def clear(self, buffers, color, depth, stencil):
  530.         pass
  531.        
  532.     def clear_render_target(self, dst, rgba, dstx, dsty, width, height):
  533.         pass
  534.  
  535.     def clear_depth_stencil(self, dst, clear_flags, depth, stencil, dstx, dsty, width, height):
  536.         pass
  537.  
  538.     def create_surface(self, resource, surf_tmpl):
  539.         surf_tmpl.resource = resource
  540.         return surf_tmpl
  541.  
  542.     def surface_destroy(self, surface):
  543.         self.interpreter.unregister_object(surface)
  544.  
  545.     def create_query(self, query_type):
  546.         return query_type
  547.    
  548.     def destroy_query(self, query):
  549.         pass
  550.  
  551.     def create_stream_output_target(self, res, buffer_offset, buffer_size):
  552.         so_target = Struct()
  553.         so_target.resource = res
  554.         so_target.offset = buffer_offset
  555.         so_target.size = buffer_size
  556.         return so_target
  557.  
  558.  
  559. class Interpreter(parser.TraceDumper):
  560.     '''Specialization of a trace parser that interprets the calls as it goes
  561.    along.'''
  562.    
  563.     ignoredCalls = set((
  564.             ('pipe_screen', 'is_format_supported'),
  565.             ('pipe_screen', 'get_name'),
  566.             ('pipe_screen', 'get_vendor'),
  567.             ('pipe_screen', 'get_param'),
  568.             ('pipe_screen', 'get_paramf'),
  569.             ('pipe_screen', 'get_shader_param'),
  570.             ('pipe_context', 'clear_render_target'), # XXX workaround trace bugs
  571.     ))
  572.  
  573.     def __init__(self, stream, options):
  574.         parser.TraceDumper.__init__(self, stream, sys.stderr)
  575.         self.options = options
  576.         self.objects = {}
  577.         self.result = None
  578.         self.globl = Global(self)
  579.         self.call_no = None
  580.  
  581.     def register_object(self, address, object):
  582.         self.objects[address] = object
  583.        
  584.     def unregister_object(self, object):
  585.         # TODO
  586.         pass
  587.  
  588.     def lookup_object(self, address):
  589.         return self.objects[address]
  590.    
  591.     def interpret(self, trace):
  592.         for call in trace.calls:
  593.             self.interpret_call(call)
  594.  
  595.     def handle_call(self, call):
  596.         if (call.klass, call.method) in self.ignoredCalls:
  597.             return
  598.  
  599.         self.call_no = call.no
  600.  
  601.         if self.verbosity(1):
  602.             # Write the call to stderr (as stdout would corrupt the JSON output)
  603.             sys.stderr.flush()
  604.             sys.stdout.flush()
  605.             parser.TraceDumper.handle_call(self, call)
  606.             sys.stderr.flush()
  607.             sys.stdout.flush()
  608.        
  609.         args = [(str(name), self.interpret_arg(arg)) for name, arg in call.args]
  610.        
  611.         if call.klass:
  612.             name, obj = args[0]
  613.             args = args[1:]
  614.         else:
  615.             obj = self.globl
  616.            
  617.         method = getattr(obj, call.method)
  618.         ret = method(**dict(args))
  619.        
  620.         # Keep track of created pointer objects.
  621.         if call.ret and isinstance(call.ret, model.Pointer):
  622.             if ret is None:
  623.                 sys.stderr.write('warning: NULL returned\n')
  624.             self.register_object(call.ret.address, ret)
  625.  
  626.         self.call_no = None
  627.  
  628.     def interpret_arg(self, node):
  629.         translator = Translator(self)
  630.         return translator.visit(node)
  631.  
  632.     def verbosity(self, level):
  633.         return self.options.verbosity >= level
  634.    
  635.  
  636. class Main(parser.Main):
  637.  
  638.     def get_optparser(self):
  639.         '''Custom options.'''
  640.  
  641.         optparser = parser.Main.get_optparser(self)
  642.         optparser.add_option("-q", "--quiet", action="store_const", const=0, dest="verbosity", help="no messages")
  643.         optparser.add_option("-v", "--verbose", action="count", dest="verbosity", default=0, help="increase verbosity level")
  644.         optparser.add_option("-c", "--call", action="store", type="int", dest="call", default=0xffffffff, help="dump on this call")
  645.         optparser.add_option("-d", "--draw", action="store", type="int", dest="draw", default=0xffffffff, help="dump on this draw")
  646.         return optparser
  647.  
  648.     def process_arg(self, stream, options):
  649.         parser = Interpreter(stream, options)
  650.         parser.parse()
  651.  
  652.  
  653. if __name__ == '__main__':
  654.     Main().main()
  655.