• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2##########################################################################
3#
4# Copyright 2008-2021, 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
30import sys
31import struct
32import json
33import binascii
34import re
35import copy
36import argparse
37
38import model
39import format
40import parse as parser
41
42
43try:
44    from struct import unpack_from
45except ImportError:
46    def unpack_from(fmt, buf, offset=0):
47        size = struct.calcsize(fmt)
48        return struct.unpack(fmt, buf[offset:offset + size])
49
50#
51# Some constants
52#
53PIPE_BUFFER = 'PIPE_BUFFER'
54
55
56def serialize(obj):
57    '''JSON serializer function for non-standard Python objects.'''
58    if isinstance(obj, bytearray) or isinstance(obj, bytes):
59        # TODO: Decide on a single way of dumping blobs
60        if False:
61            # Don't dump full blobs, but merely a description of their size and
62            # CRC32 hash.
63            crc32 = binascii.crc32(obj)
64            if crc32 < 0:
65                crc32 += 0x100000000
66            return 'blob(size=%u,crc32=0x%08x)' % (len(obj), crc32)
67        if True:
68            # Dump blobs as an array of 16byte hexadecimals
69            res = []
70            for i in range(0, len(obj), 16):
71                res.append(obj[i : i + 16].hex())
72            return res
73        # Dump blobs as a single hexadecimal string
74        return obj.hex()
75
76    # If the object has a __json__ method, use it.
77    try:
78        method = obj.__json__
79    except AttributeError:
80        raise TypeError(obj)
81    else:
82        return method()
83
84
85class Struct:
86    """C-like struct.
87
88    Python doesn't have C structs, but do its dynamic nature, any object is
89    pretty close.
90    """
91
92    def __json__(self):
93        '''Convert the structure to a standard Python dict, so it can be
94        serialized.'''
95
96        obj = {}
97        for name, value in self.__dict__.items():
98            if not name.startswith('_'):
99                obj[name] = value
100        return obj
101
102    def __repr__(self):
103        return repr(self.__json__())
104
105
106class Translator(model.Visitor):
107    """Translate model arguments into regular Python objects"""
108
109    def __init__(self, interpreter):
110        self.interpreter = interpreter
111        self.result = None
112
113    def visit(self, node):
114        self.result = None
115        node.visit(self)
116        return self.result
117
118    def visit_literal(self, node):
119        self.result = node.value
120
121    def visit_blob(self, node):
122        self.result = node
123
124    def visit_named_constant(self, node):
125        self.result = node.name
126
127    def visit_array(self, node):
128        array = []
129        for element in node.elements:
130            array.append(self.visit(element))
131        self.result = array
132
133    def visit_struct(self, node):
134        struct = Struct()
135        for member_name, member_node in node.members:
136            member_name = member_name.replace('.', '_')
137            member_value = self.visit(member_node)
138            setattr(struct, member_name, member_value)
139        self.result = struct
140
141    def visit_pointer(self, node):
142        self.result = self.interpreter.lookup_object(node.address)
143
144
145class Dispatcher:
146    '''Base class for classes whose methods can dispatch Gallium calls.'''
147
148    def __init__(self, interpreter):
149        self.interpreter = interpreter
150
151
152class Global(Dispatcher):
153    '''Global name space.
154
155    For calls that are not associated with objects, i.e, functions and not
156    methods.
157    '''
158
159    def pipe_screen_create(self):
160        return Screen(self.interpreter)
161
162    def pipe_context_create(self, screen):
163        return screen.context_create()
164
165
166class Transfer:
167    '''pipe_transfer'''
168
169    def __init__(self, resource, usage, subresource, box):
170        self.resource = resource
171        self.usage = usage
172        self.subresource = subresource
173        self.box = box
174
175
176class Screen(Dispatcher):
177    '''pipe_screen'''
178
179    def __init__(self, interpreter):
180        Dispatcher.__init__(self, interpreter)
181
182    def destroy(self):
183        pass
184
185    def context_create(self, priv=None, flags=0):
186        return Context(self.interpreter)
187
188    def resource_create(self, templat):
189        resource = templat
190        # Normalize state to avoid spurious differences
191        if resource.nr_samples == 0:
192            resource.nr_samples = 1
193        if resource.target == PIPE_BUFFER:
194            # We will keep track of buffer contents
195            resource.data = bytearray(resource.width)
196            # Ignore format
197            del resource.format
198        return resource
199
200    resource_create_unbacked = resource_create
201
202    def allocate_memory(screen, size):
203        pass
204
205    def free_memory(screen, pmem):
206        pass
207
208    def map_memory(screen, pmem):
209        pass
210
211    def unmap_memory(screen, pmem):
212        pass
213
214    def resource_bind_backing(screen, resource, pmem, offset):
215        pass
216
217    def resource_destroy(self, resource):
218        self.interpreter.unregister_object(resource)
219
220    def fence_finish(self, fence, timeout=None, ctx=None):
221        pass
222
223    def fence_signalled(self, fence):
224        pass
225
226    def fence_reference(self, dst, src):
227        pass
228
229    def flush_frontbuffer(self, resource):
230        pass
231
232
233class Context(Dispatcher):
234    '''pipe_context'''
235
236    # Internal methods variable should be prefixed with '_'
237
238    def __init__(self, interpreter):
239        Dispatcher.__init__(self, interpreter)
240
241        # Setup initial state
242        self._state = Struct()
243        self._state.scissors = []
244        self._state.viewports = []
245        self._state.vertex_buffers = []
246        self._state.vertex_elements = []
247        self._state.vs = Struct()
248        self._state.tcs = Struct()
249        self._state.tes = Struct()
250        self._state.gs = Struct()
251        self._state.fs = Struct()
252        self._state.vs.shader = None
253        self._state.tcs.shader = None
254        self._state.tes.shader = None
255        self._state.gs.shader = None
256        self._state.fs.shader = None
257        self._state.vs.sampler = []
258        self._state.tcs.sampler = []
259        self._state.tes.sampler = []
260        self._state.gs.sampler = []
261        self._state.fs.sampler = []
262        self._state.vs.sampler_views = []
263        self._state.tcs.sampler_views = []
264        self._state.tes.sampler_views = []
265        self._state.gs.sampler_views = []
266        self._state.fs.sampler_views = []
267        self._state.vs.constant_buffer = []
268        self._state.tcs.constant_buffer = []
269        self._state.tes.constant_buffer = []
270        self._state.gs.constant_buffer = []
271        self._state.fs.constant_buffer = []
272        self._state.render_condition_condition = 0
273        self._state.render_condition_mode = 0
274        self._state.sample_mask = 0xffffffff
275
276        self._draw_no = 0
277
278    def destroy(self):
279        pass
280
281    def create_blend_state(self, state):
282        # Normalize state to avoid spurious differences
283        if not state.logicop_enable:
284            del state.logicop_func
285        if not state.rt[0].blend_enable:
286            del state.rt[0].rgb_src_factor
287            del state.rt[0].rgb_dst_factor
288            del state.rt[0].rgb_func
289            del state.rt[0].alpha_src_factor
290            del state.rt[0].alpha_dst_factor
291            del state.rt[0].alpha_func
292        return state
293
294    def bind_blend_state(self, state):
295        # Normalize state
296        self._state.blend = state
297
298    def delete_blend_state(self, state):
299        pass
300
301    def create_sampler_state(self, state):
302        return state
303
304    def delete_sampler_state(self, state):
305        pass
306
307    def bind_sampler_states(self, shader, start, num_states, states):
308        # FIXME: Handle non-zero start
309        assert start == 0
310        self._get_stage_state(shader).sampler = states
311
312    def bind_vertex_sampler_states(self, num_states, states):
313        # XXX: deprecated method
314        self._state.vs.sampler = states
315
316    def bind_geometry_sampler_states(self, num_states, states):
317        # XXX: deprecated method
318        self._state.gs.sampler = states
319
320    def bind_fragment_sampler_states(self, num_states, states):
321        # XXX: deprecated method
322        self._state.fs.sampler = states
323
324    def create_rasterizer_state(self, state):
325        return state
326
327    def bind_rasterizer_state(self, state):
328        self._state.rasterizer = state
329
330    def delete_rasterizer_state(self, state):
331        pass
332
333    def create_depth_stencil_alpha_state(self, state):
334        # Normalize state to avoid spurious differences
335        if not state.alpha_enabled:
336            del state.alpha_func
337            del state.alpha_ref_value
338        for i in range(2):
339            if not state.stencil[i].enabled:
340                del state.stencil[i].func
341        return state
342
343    def bind_depth_stencil_alpha_state(self, state):
344        self._state.depth_stencil_alpha = state
345
346    def delete_depth_stencil_alpha_state(self, state):
347        pass
348
349    _tokenLabelRE = re.compile('^\s*\d+: ', re.MULTILINE)
350
351    def _create_shader_state(self, state):
352        # Strip the labels from the tokens
353        if state.tokens is not None:
354            state.tokens = self._tokenLabelRE.sub('', state.tokens)
355        return state
356
357    create_vs_state = _create_shader_state
358    create_tcs_state = _create_shader_state
359    create_tes_state = _create_shader_state
360    create_gs_state = _create_shader_state
361    create_fs_state = _create_shader_state
362
363    def bind_vs_state(self, state):
364        self._state.vs.shader = state
365
366    def bind_tcs_state(self, state):
367        self._state.tcs.shader = state
368
369    def bind_tes_state(self, state):
370        self._state.tes.shader = state
371
372    def bind_gs_state(self, state):
373        self._state.gs.shader = state
374
375    def bind_fs_state(self, state):
376        self._state.fs.shader = state
377
378    def bind_tcz_state(self, state):
379        self._state.tcs.shader = state
380
381    def _delete_shader_state(self, state):
382        return state
383
384    delete_vs_state = _delete_shader_state
385    delete_tcs_state = _delete_shader_state
386    delete_tes_state = _delete_shader_state
387    delete_gs_state = _delete_shader_state
388    delete_fs_state = _delete_shader_state
389
390    def set_blend_color(self, state):
391        self._state.blend_color = state
392
393    def set_stencil_ref(self, state):
394        self._state.stencil_ref = state
395
396    def set_clip_state(self, state):
397        self._state.clip = state
398
399    def _dump_constant_buffer(self, buffer):
400        if not self.interpreter.verbosity(2):
401            return
402
403        data = self.real.buffer_read(buffer)
404        format = '4f'
405        index = 0
406        for offset in range(0, len(data), struct.calcsize(format)):
407            x, y, z, w = unpack_from(format, data, offset)
408            sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))
409            index += 1
410        sys.stdout.flush()
411
412    def _get_stage_state(self, shader):
413        if shader == 'PIPE_SHADER_VERTEX':
414            return self._state.vs
415        if shader == 'PIPE_SHADER_TESS_CTRL':
416            return self._state.tcs
417        if shader == 'PIPE_SHADER_TESS_EVAL':
418            return self._state.tes
419        if shader == 'PIPE_SHADER_GEOMETRY':
420            return self._state.gs
421        if shader == 'PIPE_SHADER_FRAGMENT':
422            return self._state.fs
423        assert False
424
425    def set_constant_buffer(self, shader, index, take_ownership, constant_buffer):
426        self._update(self._get_stage_state(shader).constant_buffer, index, 1, [constant_buffer])
427
428    def set_framebuffer_state(self, state):
429        self._state.fb = state
430
431    def set_polygon_stipple(self, state):
432        self._state.polygon_stipple = state
433
434    def _update(self, array, start_slot, num_slots, states):
435        if not isinstance(states, list):
436            # XXX: trace is not serializing multiple scissors/viewports properly yet
437            num_slots = 1
438            states = [states]
439        while len(array) < start_slot + num_slots:
440            array.append(None)
441        for i in range(num_slots):
442            array[start_slot + i] = states[i]
443
444    def set_scissor_states(self, start_slot, num_scissors, states):
445        self._update(self._state.scissors, start_slot, num_scissors, states)
446
447    def set_viewport_states(self, start_slot, num_viewports, states):
448        self._update(self._state.viewports, start_slot, num_viewports, states)
449
450    def create_sampler_view(self, resource, templ):
451        templ.resource = resource
452        return templ
453
454    def sampler_view_destroy(self, view):
455        pass
456
457    def set_sampler_views(self, shader, start, num, unbind_num_trailing_slots, take_ownership, views):
458        # FIXME: Handle non-zero start
459        assert start == 0
460        self._get_stage_state(shader).sampler_views = views
461
462    def set_fragment_sampler_views(self, num, views):
463        # XXX: deprecated
464        self._state.fs.sampler_views = views
465
466    def set_geometry_sampler_views(self, num, views):
467        # XXX: deprecated
468        self._state.gs.sampler_views = views
469
470    def set_vertex_sampler_views(self, num, views):
471        # XXX: deprecated
472        self._state.vs.sampler_views = views
473
474    def set_vertex_buffers(self, start_slot, num_buffers, unbind_num_trailing_slots, take_ownership, buffers):
475        self._update(self._state.vertex_buffers, start_slot, num_buffers, buffers)
476
477    def create_vertex_elements_state(self, num_elements, elements):
478        return elements[0:num_elements]
479
480    def bind_vertex_elements_state(self, state):
481        self._state.vertex_elements = state
482
483    def delete_vertex_elements_state(self, state):
484        pass
485
486    def set_index_buffer(self, ib):
487        self._state.index_buffer = ib
488
489    def set_patch_vertices(self, patch_vertices):
490        pass
491
492    # Don't dump more than this number of indices/vertices
493    MAX_ELEMENTS = 16
494
495    def _merge_indices(self, info):
496        '''Merge the vertices into our state.'''
497
498        index_size = self._state.index_buffer.index_size
499
500        format = {
501            1: 'B',
502            2: 'H',
503            4: 'I',
504        }[index_size]
505
506        assert struct.calcsize(format) == index_size
507
508        if self._state.index_buffer.buffer is None:
509            # Could happen with index in user memory
510            return 0, 0
511
512        data = self._state.index_buffer.buffer.data
513        max_index, min_index = 0, 0xffffffff
514
515        count = min(info.count, self.MAX_ELEMENTS)
516        indices = []
517        for i in range(info.start, info.start + count):
518            offset = self._state.index_buffer.offset + i*index_size
519            if offset + index_size > len(data):
520                index = 0
521            else:
522                index, = unpack_from(format, data, offset)
523            indices.append(index)
524            min_index = min(min_index, index)
525            max_index = max(max_index, index)
526
527        self._state.indices = indices
528
529        return min_index + info.index_bias, max_index + info.index_bias
530
531    def _merge_vertices(self, start, count):
532        '''Merge the vertices into our state.'''
533
534        count = min(count, self.MAX_ELEMENTS)
535        vertices = []
536        for index in range(start, start + count):
537            if index >= start + 16:
538                sys.stdout.write('\t...\n')
539                break
540            vertex = []
541            for velem in self._state.vertex_elements:
542                vbuf = self._state.vertex_buffers[velem.vertex_buffer_index]
543                resource = vbuf.buffer_resource
544                if resource is None:
545                    continue
546
547                data = resource.data
548
549                offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index
550                format = {
551                    'PIPE_FORMAT_R32_FLOAT': 'f',
552                    'PIPE_FORMAT_R32G32_FLOAT': '2f',
553                    'PIPE_FORMAT_R32G32B32_FLOAT': '3f',
554                    'PIPE_FORMAT_R32G32B32A32_FLOAT': '4f',
555                    'PIPE_FORMAT_R32_UINT': 'I',
556                    'PIPE_FORMAT_R32G32_UINT': '2I',
557                    'PIPE_FORMAT_R32G32B32_UINT': '3I',
558                    'PIPE_FORMAT_R32G32B32A32_UINT': '4I',
559                    'PIPE_FORMAT_R8_UINT': 'B',
560                    'PIPE_FORMAT_R8G8_UINT': '2B',
561                    'PIPE_FORMAT_R8G8B8_UINT': '3B',
562                    'PIPE_FORMAT_R8G8B8A8_UINT': '4B',
563                    'PIPE_FORMAT_A8R8G8B8_UNORM': '4B',
564                    'PIPE_FORMAT_R8G8B8A8_UNORM': '4B',
565                    'PIPE_FORMAT_B8G8R8A8_UNORM': '4B',
566                    'PIPE_FORMAT_R16G16B16_SNORM': '3h',
567                }[velem.src_format]
568
569                data = resource.data
570                attribute = unpack_from(format, data, offset)
571                vertex.append(attribute)
572
573            vertices.append(vertex)
574
575        self._state.vertices = vertices
576
577    def render_condition(self, query, condition = 0, mode = 0):
578        self._state.render_condition_query = query
579        self._state.render_condition_condition = condition
580        self._state.render_condition_mode = mode
581
582    def set_stream_output_targets(self, num_targets, tgs, offsets):
583        self._state.so_targets = tgs
584        self._state.offsets = offsets
585
586    def set_sample_mask(self, sample_mask):
587        self._state.sample_mask = sample_mask
588
589    def set_min_samples(self, min_samples):
590        pass
591
592    def draw_vbo(self, info, drawid_offset, indirect, draws, num_draws):
593        self._draw_no += 1
594
595        if self.interpreter.call_no < self.interpreter.options.call and \
596            self._draw_no < self.interpreter.options.draw:
597                return
598
599        # Merge the all draw state
600
601        self._state.draw = info
602
603        if info.index_size != 0:
604            min_index, max_index = self._merge_indices(info)
605        else:
606            min_index = draws[0].start
607            max_index = draws[0].start + draws[0].count - 1
608        self._merge_vertices(min_index, max_index - min_index + 1)
609
610        self._dump_state()
611
612    _dclRE = re.compile('^DCL\s+(IN|OUT|SAMP|SVIEW)\[([0-9]+)\].*$', re.MULTILINE)
613
614    def _normalize_stage_state(self, stage):
615        if stage.shader is not None and stage.shader.tokens is not None:
616            registers = {}
617
618            for mo in self._dclRE.finditer(stage.shader.tokens):
619                file_ = mo.group(1)
620                index = mo.group(2)
621                register = registers.setdefault(file_, set())
622                register.add(int(index))
623
624            if 'SAMP' in registers and 'SVIEW' not in registers:
625                registers['SVIEW'] = registers['SAMP']
626
627            mapping = [
628                #("CONST", "constant_buffer"),
629                ("SAMP", "sampler"),
630                ("SVIEW", "sampler_views"),
631            ]
632
633            for fileName, attrName in mapping:
634                register = registers.setdefault(fileName, set())
635                attr = getattr(stage, attrName)
636                for index in range(len(attr)):
637                    if index not in register:
638                        attr[index] = None
639                while attr and attr[-1] is None:
640                    attr.pop()
641
642    def _dump_state(self):
643        '''Dump our state to JSON and terminate.'''
644
645        state = copy.deepcopy(self._state)
646
647        self._normalize_stage_state(state.vs)
648        self._normalize_stage_state(state.tcs)
649        self._normalize_stage_state(state.tes)
650        self._normalize_stage_state(state.gs)
651        self._normalize_stage_state(state.fs)
652
653        json.dump(
654            obj = state,
655            fp = sys.stdout,
656            default = serialize,
657            sort_keys = True,
658            indent = 4,
659            separators = (',', ': ')
660        )
661
662        sys.exit(0)
663
664    def resource_copy_region(self, dst, dst_level, dstx, dsty, dstz, src, src_level, src_box):
665        if dst.target == PIPE_BUFFER or src.target == PIPE_BUFFER:
666            assert dst.target == PIPE_BUFFER and src.target == PIPE_BUFFER
667            assert dst_level == 0
668            assert dsty == 0
669            assert dstz == 0
670            assert src_level == 0
671            assert src_box.y == 0
672            assert src_box.z == 0
673            assert src_box.height == 1
674            assert src_box.depth == 1
675            dst.data[dstx : dstx + src_box.width] = src.data[src_box.x : src_box.x + src_box.width]
676        pass
677
678    def is_resource_referenced(self, texture, face, level):
679        pass
680
681    def get_transfer(self, texture, sr, usage, box):
682        if texture is None:
683            return None
684        transfer = Transfer(texture, sr, usage, box)
685        return transfer
686
687    def tex_transfer_destroy(self, transfer):
688        self.interpreter.unregister_object(transfer)
689
690    def buffer_subdata(self, resource, usage, data, box=None, offset=None, size=None, level=None, stride=None, layer_stride=None):
691        if box is not None:
692            # XXX trace_context_transfer_unmap generates brokens buffer_subdata
693            assert offset is None
694            assert size is None
695            assert level == 0
696            offset = box.x
697            size = box.width
698            box = None
699
700        if resource is not None and resource.target == PIPE_BUFFER:
701            data = data.getValue()
702            assert len(data) >= size
703            assert offset + size <= len(resource.data)
704            resource.data[offset : offset + size] = data[:size]
705
706    def texture_subdata(self, resource, level, usage, box, data, stride, layer_stride):
707        pass
708
709    def transfer_inline_write(self, resource, level, usage, box, stride, layer_stride, data):
710        if resource is not None and resource.target == PIPE_BUFFER:
711            data = data.getValue()
712            assert len(data) >= box.width
713            assert box.x + box.width <= len(resource.data)
714            resource.data[box.x : box.x + box.width] = data[:box.width]
715
716    def flush(self, flags):
717        # Return a fake fence
718        return self.interpreter.call_no
719
720    def clear(self, buffers, color, depth, stencil, scissor_state=None):
721        pass
722
723    def clear_render_target(self, dst, rgba, dstx, dsty, width, height, render_condition_enabled):
724        pass
725
726    def clear_depth_stencil(self, dst, clear_flags, depth, stencil, dstx, dsty, width, height, render_condition_enabled):
727        pass
728
729    def clear_texture(self, res, level, box, **color):
730        pass
731
732    def create_surface(self, resource, surf_tmpl):
733        assert resource is not None
734        surf_tmpl.resource = resource
735        return surf_tmpl
736
737    def surface_destroy(self, surface):
738        self.interpreter.unregister_object(surface)
739
740    def create_query(self, query_type, index):
741        return query_type
742
743    def destroy_query(self, query):
744        pass
745
746    def begin_query(self, query):
747        pass
748
749    def end_query(self, query):
750        pass
751
752    def create_stream_output_target(self, res, buffer_offset, buffer_size):
753        so_target = Struct()
754        so_target.resource = res
755        so_target.offset = buffer_offset
756        so_target.size = buffer_size
757        return so_target
758
759
760class Interpreter(parser.SimpleTraceDumper):
761    '''Specialization of a trace parser that interprets the calls as it goes
762    along.'''
763
764    ignoredCalls = set((
765            ('pipe_screen', 'is_format_supported'),
766            ('pipe_screen', 'get_name'),
767            ('pipe_screen', 'get_vendor'),
768            ('pipe_screen', 'get_device_uuid'),
769            ('pipe_screen', 'get_driver_uuid'),
770            ('pipe_screen', 'get_compiler_options'),
771            ('pipe_screen', 'get_param'),
772            ('pipe_screen', 'get_paramf'),
773            ('pipe_screen', 'get_shader_param'),
774            ('pipe_screen', 'get_compute_param'),
775            ('pipe_screen', 'get_disk_shader_cache'),
776            ('pipe_context', 'clear_render_target'), # XXX workaround trace bugs
777            ('pipe_context', 'flush_resource'),
778            ('pipe_context', 'buffer_map'),
779            ('pipe_context', 'texture_map'),
780            ('pipe_context', 'transfer_unmap'),
781    ))
782
783    def __init__(self, stream, options, formatter, state):
784        parser.SimpleTraceDumper.__init__(self, stream, options, formatter, state)
785        self.objects = {}
786        self.result = None
787        self.globl = Global(self)
788        self.call_no = None
789
790    def register_object(self, address, object):
791        self.objects[address] = object
792
793    def unregister_object(self, object):
794        # TODO
795        pass
796
797    def lookup_object(self, address):
798        try:
799            return self.objects[address]
800        except KeyError:
801            # Could happen, e.g., with user memory pointers
802            return address
803
804    def interpret(self, trace):
805        for call in trace.calls:
806            self.interpret_call(call)
807
808    def handle_call(self, call):
809        if (call.klass, call.method) in self.ignoredCalls:
810            return
811
812        self.call_no = call.no
813
814        if self.verbosity(1):
815            # Write the call to stderr (as stdout would corrupt the JSON output)
816            sys.stderr.flush()
817            sys.stdout.flush()
818            parser.TraceDumper.handle_call(self, call)
819            sys.stderr.flush()
820            sys.stdout.flush()
821
822        args = [(str(name), self.interpret_arg(arg)) for name, arg in call.args]
823
824        if call.klass:
825            name, obj = args[0]
826            args = args[1:]
827        else:
828            obj = self.globl
829
830        method = getattr(obj, call.method)
831        ret = method(**dict(args))
832
833        # Keep track of created pointer objects.
834        if call.ret and isinstance(call.ret, model.Pointer):
835            if ret is None:
836                sys.stderr.write('warning: NULL returned\n')
837            self.register_object(call.ret.address, ret)
838
839        self.call_no = None
840
841    def interpret_arg(self, node):
842        translator = Translator(self)
843        return translator.visit(node)
844
845    def verbosity(self, level):
846        return self.options.verbosity >= level
847
848
849class DumpStateOptions(parser.ParseOptions):
850
851    def __init__(self, args=None):
852
853        # These will get initialized in ModelOptions.__init__()
854        self.verbosity = None
855        self.call = None
856        self.draw = None
857
858        parser.ParseOptions.__init__(self, args)
859
860
861class Main(parser.Main):
862
863    def get_optparser(self):
864        optparser = argparse.ArgumentParser(
865            description="Parse and dump Gallium trace(s) as JSON")
866
867        optparser.add_argument("filename", action="extend", nargs="+",
868            type=str, metavar="filename", help="Gallium trace filename (plain or .gz, .bz2)")
869
870        optparser.add_argument("-v", "--verbose", action="count", default=0, dest="verbosity", help="increase verbosity level")
871        optparser.add_argument("-q", "--quiet", action="store_const", const=0, dest="verbosity", help="no messages")
872        optparser.add_argument("-c", "--call", action="store", type=int, dest="call", default=0xffffffff, help="dump on this call")
873        optparser.add_argument("-d", "--draw", action="store", type=int, dest="draw", default=0xffffffff, help="dump on this draw")
874        return optparser
875
876    def make_options(self, args):
877        return DumpStateOptions(args)
878
879    def process_arg(self, stream, options):
880        formatter = format.Formatter(sys.stderr)
881        parser = Interpreter(stream, options, formatter, model.TraceStateData())
882        parser.parse()
883
884
885if __name__ == '__main__':
886    Main().main()
887