• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python2
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
30import sys
31import struct
32import json
33import binascii
34import re
35import copy
36
37import model
38import parse as parser
39
40
41try:
42    from struct import unpack_from
43except ImportError:
44    def unpack_from(fmt, buf, offset=0):
45        size = struct.calcsize(fmt)
46        return struct.unpack(fmt, buf[offset:offset + size])
47
48#
49# Some constants
50#
51PIPE_BUFFER = 0
52PIPE_SHADER_VERTEX   = 0
53PIPE_SHADER_FRAGMENT = 1
54PIPE_SHADER_GEOMETRY = 2
55PIPE_SHADER_COMPUTE  = 3
56PIPE_SHADER_TYPES    = 4
57
58
59def serialize(obj):
60    '''JSON serializer function for non-standard Python objects.'''
61
62    if isinstance(obj, bytearray):
63        # TODO: Decide on a single way of dumping blobs
64        if False:
65            # Don't dump full blobs, but merely a description of their size and
66            # CRC32 hash.
67            crc32 = binascii.crc32(obj)
68            if crc32 < 0:
69                crc32 += 0x100000000
70            return 'blob(size=%u,crc32=0x%08x)' % (len(obj), crc32)
71        if True:
72            # Dump blobs as an array of 16byte hexadecimals
73            res = []
74            for i in range(0, len(obj), 16):
75                res.append(binascii.b2a_hex(obj[i: i+16]))
76            return res
77        # Dump blobs as a single hexadecimal string
78        return binascii.b2a_hex(obj)
79
80    # If the object has a __json__ method, use it.
81    try:
82        method = obj.__json__
83    except AttributeError:
84        raise TypeError(obj)
85    else:
86        return method()
87
88
89class Struct:
90    """C-like struct.
91
92    Python doesn't have C structs, but do its dynamic nature, any object is
93    pretty close.
94    """
95
96    def __json__(self):
97        '''Convert the structure to a standard Python dict, so it can be
98        serialized.'''
99
100        obj = {}
101        for name, value in self.__dict__.items():
102            if not name.startswith('_'):
103                obj[name] = value
104        return obj
105
106    def __repr__(self):
107        return repr(self.__json__())
108
109
110class Translator(model.Visitor):
111    """Translate model arguments into regular Python objects"""
112
113    def __init__(self, interpreter):
114        self.interpreter = interpreter
115        self.result = None
116
117    def visit(self, node):
118        self.result = None
119        node.visit(self)
120        return self.result
121
122    def visit_literal(self, node):
123        self.result = node.value
124
125    def visit_blob(self, node):
126        self.result = node
127
128    def visit_named_constant(self, node):
129        self.result = node.name
130
131    def visit_array(self, node):
132        array = []
133        for element in node.elements:
134            array.append(self.visit(element))
135        self.result = array
136
137    def visit_struct(self, node):
138        struct = Struct()
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)
143        self.result = struct
144
145    def visit_pointer(self, node):
146        self.result = self.interpreter.lookup_object(node.address)
147
148
149class Dispatcher:
150    '''Base class for classes whose methods can dispatch Gallium calls.'''
151
152    def __init__(self, interpreter):
153        self.interpreter = interpreter
154
155
156class Global(Dispatcher):
157    '''Global name space.
158
159    For calls that are not associated with objects, i.e, functions and not
160    methods.
161    '''
162
163    def pipe_screen_create(self):
164        return Screen(self.interpreter)
165
166    def pipe_context_create(self, screen):
167        return screen.context_create()
168
169
170class Transfer:
171    '''pipe_transfer'''
172
173    def __init__(self, resource, usage, subresource, box):
174        self.resource = resource
175        self.usage = usage
176        self.subresource = subresource
177        self.box = box
178
179
180class Screen(Dispatcher):
181    '''pipe_screen'''
182
183    def __init__(self, interpreter):
184        Dispatcher.__init__(self, interpreter)
185
186    def destroy(self):
187        pass
188
189    def context_create(self, priv=None, flags=0):
190        return Context(self.interpreter)
191
192    def is_format_supported(self, format, target, sample_count, bind, geom_flags):
193        pass
194
195    def resource_create(self, templat):
196        resource = 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)
203            # Ignore format
204            del resource.format
205        return resource
206
207    def resource_destroy(self, resource):
208        self.interpreter.unregister_object(resource)
209
210    def fence_finish(self, fence, timeout=None):
211        pass
212
213    def fence_signalled(self, fence):
214        pass
215
216    def fence_reference(self, dst, src):
217        pass
218
219    def flush_frontbuffer(self, resource):
220        pass
221
222
223class Context(Dispatcher):
224    '''pipe_context'''
225
226    # Internal methods variable should be prefixed with '_'
227
228    def __init__(self, interpreter):
229        Dispatcher.__init__(self, interpreter)
230
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
254
255        self._draw_no = 0
256
257    def destroy(self):
258        pass
259
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
271        return state
272
273    def bind_blend_state(self, state):
274        # Normalize state
275        self._state.blend = state
276
277    def delete_blend_state(self, state):
278        pass
279
280    def create_sampler_state(self, state):
281        return state
282
283    def delete_sampler_state(self, state):
284        pass
285
286    def bind_sampler_states(self, shader, start, num_states, states):
287        # FIXME: Handle non-zero start
288        assert start == 0
289        self._get_stage_state(shader).sampler = states
290
291    def bind_vertex_sampler_states(self, num_states, states):
292        # XXX: deprecated method
293        self._state.vs.sampler = states
294
295    def bind_geometry_sampler_states(self, num_states, states):
296        # XXX: deprecated method
297        self._state.gs.sampler = states
298
299    def bind_fragment_sampler_states(self, num_states, states):
300        # XXX: deprecated method
301        self._state.fs.sampler = states
302
303    def create_rasterizer_state(self, state):
304        return state
305
306    def bind_rasterizer_state(self, state):
307        self._state.rasterizer = state
308
309    def delete_rasterizer_state(self, state):
310        pass
311
312    def create_depth_stencil_alpha_state(self, state):
313        # Normalize state to avoid spurious differences
314        if not state.alpha.enabled:
315            del state.alpha.func
316            del state.alpha.ref_value
317        for i in range(2):
318            if not state.stencil[i].enabled:
319                del state.stencil[i].func
320        return state
321
322    def bind_depth_stencil_alpha_state(self, state):
323        self._state.depth_stencil_alpha = state
324
325    def delete_depth_stencil_alpha_state(self, state):
326        pass
327
328    _tokenLabelRE = re.compile('^\s*\d+: ', re.MULTILINE)
329
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)
334        return state
335
336    create_vs_state = _create_shader_state
337    create_gs_state = _create_shader_state
338    create_fs_state = _create_shader_state
339
340    def bind_vs_state(self, state):
341        self._state.vs.shader = state
342
343    def bind_gs_state(self, state):
344        self._state.gs.shader = state
345
346    def bind_fs_state(self, state):
347        self._state.fs.shader = state
348
349    def _delete_shader_state(self, state):
350        return state
351
352    delete_vs_state = _delete_shader_state
353    delete_gs_state = _delete_shader_state
354    delete_fs_state = _delete_shader_state
355
356    def set_blend_color(self, state):
357        self._state.blend_color = state
358
359    def set_stencil_ref(self, state):
360        self._state.stencil_ref = state
361
362    def set_clip_state(self, state):
363        self._state.clip = state
364
365    def _dump_constant_buffer(self, buffer):
366        if not self.interpreter.verbosity(2):
367            return
368
369        data = self.real.buffer_read(buffer)
370        format = '4f'
371        index = 0
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))
375            index += 1
376        sys.stdout.flush()
377
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
385        assert False
386
387    def set_constant_buffer(self, shader, index, constant_buffer):
388        self._update(self._get_stage_state(shader).constant_buffer, index, 1, [constant_buffer])
389
390    def set_framebuffer_state(self, state):
391        self._state.fb = state
392
393    def set_polygon_stipple(self, state):
394        self._state.polygon_stipple = state
395
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
399            num_slots = 1
400            states = [states]
401        while len(array) < start_slot + num_slots:
402            array.append(None)
403        for i in range(num_slots):
404            array[start_slot + i] = states[i]
405
406    def set_scissor_states(self, start_slot, num_scissors, states):
407        self._update(self._state.scissors, start_slot, num_scissors, states)
408
409    def set_viewport_states(self, start_slot, num_viewports, states):
410        self._update(self._state.viewports, start_slot, num_viewports, states)
411
412    def create_sampler_view(self, resource, templ):
413        templ.resource = resource
414        return templ
415
416    def sampler_view_destroy(self, view):
417        pass
418
419    def set_sampler_views(self, shader, start, num, views):
420        # FIXME: Handle non-zero start
421        assert start == 0
422        self._get_stage_state(shader).sampler_views = views
423
424    def set_fragment_sampler_views(self, num, views):
425        # XXX: deprecated
426        self._state.fs.sampler_views = views
427
428    def set_geometry_sampler_views(self, num, views):
429        # XXX: deprecated
430        self._state.gs.sampler_views = views
431
432    def set_vertex_sampler_views(self, num, views):
433        # XXX: deprecated
434        self._state.vs.sampler_views = views
435
436    def set_vertex_buffers(self, start_slot, num_buffers, buffers):
437        self._update(self._state.vertex_buffers, start_slot, num_buffers, buffers)
438
439    def create_vertex_elements_state(self, num_elements, elements):
440        return elements[0:num_elements]
441
442    def bind_vertex_elements_state(self, state):
443        self._state.vertex_elements = state
444
445    def delete_vertex_elements_state(self, state):
446        pass
447
448    def set_index_buffer(self, ib):
449        self._state.index_buffer = ib
450
451    # Don't dump more than this number of indices/vertices
452    MAX_ELEMENTS = 16
453
454    def _merge_indices(self, info):
455        '''Merge the vertices into our state.'''
456
457        index_size = self._state.index_buffer.index_size
458
459        format = {
460            1: 'B',
461            2: 'H',
462            4: 'I',
463        }[index_size]
464
465        assert struct.calcsize(format) == index_size
466
467        if self._state.index_buffer.buffer is None:
468            # Could happen with index in user memory
469            return 0, 0
470
471        data = self._state.index_buffer.buffer.data
472        max_index, min_index = 0, 0xffffffff
473
474        count = min(info.count, self.MAX_ELEMENTS)
475        indices = []
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):
479                index = 0
480            else:
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)
485
486        self._state.indices = indices
487
488        return min_index + info.index_bias, max_index + info.index_bias
489
490    def _merge_vertices(self, start, count):
491        '''Merge the vertices into our state.'''
492
493        count = min(count, self.MAX_ELEMENTS)
494        vertices = []
495        for index in xrange(start, start + count):
496            if index >= start + 16:
497                sys.stdout.write('\t...\n')
498                break
499            vertex = []
500            for velem in self._state.vertex_elements:
501                vbuf = self._state.vertex_buffers[velem.vertex_buffer_index]
502                resource = vbuf.buffer_resource
503                if resource is None:
504                    continue
505
506                data = resource.data
507
508                offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index
509                format = {
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',
526                }[velem.src_format]
527
528                data = resource.data
529                attribute = unpack_from(format, data, offset)
530                vertex.append(attribute)
531
532            vertices.append(vertex)
533
534        self._state.vertices = vertices
535
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
540
541    def set_stream_output_targets(self, num_targets, tgs, offsets):
542        self._state.so_targets = tgs
543        self._state.offsets = offsets
544
545    def draw_vbo(self, info):
546        self._draw_no += 1
547
548        if self.interpreter.call_no < self.interpreter.options.call and \
549            self._draw_no < self.interpreter.options.draw:
550                return
551
552        # Merge the all draw state
553
554        self._state.draw = info
555
556        if info.index_size != 0:
557            min_index, max_index = self._merge_indices(info)
558        else:
559            min_index = info.start
560            max_index = info.start + info.count - 1
561        self._merge_vertices(min_index, max_index - min_index + 1)
562
563        self._dump_state()
564
565    _dclRE = re.compile('^DCL\s+(IN|OUT|SAMP|SVIEW)\[([0-9]+)\].*$', re.MULTILINE)
566
567    def _normalize_stage_state(self, stage):
568
569        registers = {}
570
571        if stage.shader is not None and stage.shader.tokens is not None:
572            for mo in self._dclRE.finditer(stage.shader.tokens):
573                file_ = mo.group(1)
574                index = mo.group(2)
575                register = registers.setdefault(file_, set())
576                register.add(int(index))
577
578        if 'SAMP' in registers and 'SVIEW' not in registers:
579            registers['SVIEW'] = registers['SAMP']
580
581        mapping = [
582            #("CONST", "constant_buffer"),
583            ("SAMP", "sampler"),
584            ("SVIEW", "sampler_views"),
585        ]
586
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:
592                    attr[index] = None
593            while attr and attr[-1] is None:
594                attr.pop()
595
596    def _dump_state(self):
597        '''Dump our state to JSON and terminate.'''
598
599        state = copy.deepcopy(self._state)
600
601        self._normalize_stage_state(state.vs)
602        self._normalize_stage_state(state.gs)
603        self._normalize_stage_state(state.fs)
604
605        json.dump(
606            obj = state,
607            fp = sys.stdout,
608            default = serialize,
609            sort_keys = True,
610            indent = 4,
611            separators = (',', ': ')
612        )
613
614        sys.exit(0)
615
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
620            assert dsty == 0
621            assert dstz == 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]
628        pass
629
630    def is_resource_referenced(self, texture, face, level):
631        pass
632
633    def get_transfer(self, texture, sr, usage, box):
634        if texture is None:
635            return None
636        transfer = Transfer(texture, sr, usage, box)
637        return transfer
638
639    def tex_transfer_destroy(self, transfer):
640        self.interpreter.unregister_object(transfer)
641
642    def buffer_subdata(self, resource, usage, data, box=None, offset=None, size=None, level=None, stride=None, layer_stride=None):
643        if box is not None:
644            # XXX trace_context_transfer_unmap generates brokens buffer_subdata
645            assert offset is None
646            assert size is None
647            assert level == 0
648            offset = box.x
649            size = box.width
650            box = None
651
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]
657
658    def texture_subdata(self, resource, level, usage, box, data, stride, layer_stride):
659        pass
660
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]
667
668    def flush(self, flags):
669        # Return a fake fence
670        return self.interpreter.call_no
671
672    def clear(self, buffers, color, depth, stencil):
673        pass
674
675    def clear_render_target(self, dst, rgba, dstx, dsty, width, height):
676        pass
677
678    def clear_depth_stencil(self, dst, clear_flags, depth, stencil, dstx, dsty, width, height):
679        pass
680
681    def create_surface(self, resource, surf_tmpl):
682        assert resource is not None
683        surf_tmpl.resource = resource
684        return surf_tmpl
685
686    def surface_destroy(self, surface):
687        self.interpreter.unregister_object(surface)
688
689    def create_query(self, query_type, index):
690        return query_type
691
692    def destroy_query(self, query):
693        pass
694
695    def begin_query(self, query):
696        pass
697
698    def end_query(self, query):
699        pass
700
701    def create_stream_output_target(self, res, buffer_offset, buffer_size):
702        so_target = Struct()
703        so_target.resource = res
704        so_target.offset = buffer_offset
705        so_target.size = buffer_size
706        return so_target
707
708
709class Interpreter(parser.TraceDumper):
710    '''Specialization of a trace parser that interprets the calls as it goes
711    along.'''
712
713    ignoredCalls = set((
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
721    ))
722
723    def __init__(self, stream, options):
724        parser.TraceDumper.__init__(self, stream, sys.stderr)
725        self.options = options
726        self.objects = {}
727        self.result = None
728        self.globl = Global(self)
729        self.call_no = None
730
731    def register_object(self, address, object):
732        self.objects[address] = object
733
734    def unregister_object(self, object):
735        # TODO
736        pass
737
738    def lookup_object(self, address):
739        try:
740            return self.objects[address]
741        except KeyError:
742            # Could happen, e.g., with user memory pointers
743            return address
744
745    def interpret(self, trace):
746        for call in trace.calls:
747            self.interpret_call(call)
748
749    def handle_call(self, call):
750        if (call.klass, call.method) in self.ignoredCalls:
751            return
752
753        self.call_no = call.no
754
755        if self.verbosity(1):
756            # Write the call to stderr (as stdout would corrupt the JSON output)
757            sys.stderr.flush()
758            sys.stdout.flush()
759            parser.TraceDumper.handle_call(self, call)
760            sys.stderr.flush()
761            sys.stdout.flush()
762
763        args = [(str(name), self.interpret_arg(arg)) for name, arg in call.args]
764
765        if call.klass:
766            name, obj = args[0]
767            args = args[1:]
768        else:
769            obj = self.globl
770
771        method = getattr(obj, call.method)
772        ret = method(**dict(args))
773
774        # Keep track of created pointer objects.
775        if call.ret and isinstance(call.ret, model.Pointer):
776            if ret is None:
777                sys.stderr.write('warning: NULL returned\n')
778            self.register_object(call.ret.address, ret)
779
780        self.call_no = None
781
782    def interpret_arg(self, node):
783        translator = Translator(self)
784        return translator.visit(node)
785
786    def verbosity(self, level):
787        return self.options.verbosity >= level
788
789
790class Main(parser.Main):
791
792    def get_optparser(self):
793        '''Custom options.'''
794
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")
800        return optparser
801
802    def process_arg(self, stream, options):
803        parser = Interpreter(stream, options)
804        parser.parse()
805
806
807if __name__ == '__main__':
808    Main().main()
809