Path: blob/21.2-virgl/src/gallium/tools/trace/dump_state.py
4561 views
#!/usr/bin/env python31##########################################################################2#3# Copyright 2008-2013, VMware, Inc.4# All Rights Reserved.5#6# Permission is hereby granted, free of charge, to any person obtaining a7# copy of this software and associated documentation files (the8# "Software"), to deal in the Software without restriction, including9# without limitation the rights to use, copy, modify, merge, publish,10# distribute, sub license, and/or sell copies of the Software, and to11# permit persons to whom the Software is furnished to do so, subject to12# the following conditions:13#14# The above copyright notice and this permission notice (including the15# next paragraph) shall be included in all copies or substantial portions16# of the Software.17#18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS19# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF20# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.21# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR22# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,23# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE24# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.25#26##########################################################################272829import sys30import struct31import json32import binascii33import re34import copy35import argparse3637import model38import format39import parse as parser404142try:43from struct import unpack_from44except ImportError:45def unpack_from(fmt, buf, offset=0):46size = struct.calcsize(fmt)47return struct.unpack(fmt, buf[offset:offset + size])4849#50# Some constants51#52PIPE_BUFFER = 053PIPE_SHADER_VERTEX = 054PIPE_SHADER_FRAGMENT = 155PIPE_SHADER_GEOMETRY = 256PIPE_SHADER_COMPUTE = 357PIPE_SHADER_TYPES = 4585960def serialize(obj):61'''JSON serializer function for non-standard Python objects.'''62if isinstance(obj, bytearray) or isinstance(obj, bytes):63# TODO: Decide on a single way of dumping blobs64if False:65# Don't dump full blobs, but merely a description of their size and66# CRC32 hash.67crc32 = binascii.crc32(obj)68if crc32 < 0:69crc32 += 0x10000000070return 'blob(size=%u,crc32=0x%08x)' % (len(obj), crc32)71if True:72# Dump blobs as an array of 16byte hexadecimals73res = []74for i in range(0, len(obj), 16):75res.append(obj[i : i + 16].hex())76return res77# Dump blobs as a single hexadecimal string78return obj.hex()7980# If the object has a __json__ method, use it.81try:82method = obj.__json__83except AttributeError:84raise TypeError(obj)85else:86return method()878889class Struct:90"""C-like struct.9192Python doesn't have C structs, but do its dynamic nature, any object is93pretty close.94"""9596def __json__(self):97'''Convert the structure to a standard Python dict, so it can be98serialized.'''99100obj = {}101for name, value in self.__dict__.items():102if not name.startswith('_'):103obj[name] = value104return obj105106def __repr__(self):107return repr(self.__json__())108109110class Translator(model.Visitor):111"""Translate model arguments into regular Python objects"""112113def __init__(self, interpreter):114self.interpreter = interpreter115self.result = None116117def visit(self, node):118self.result = None119node.visit(self)120return self.result121122def visit_literal(self, node):123self.result = node.value124125def visit_blob(self, node):126self.result = node127128def visit_named_constant(self, node):129self.result = node.name130131def visit_array(self, node):132array = []133for element in node.elements:134array.append(self.visit(element))135self.result = array136137def visit_struct(self, node):138struct = Struct()139for member_name, member_node in node.members:140member_name = member_name.replace('.', '_')141member_value = self.visit(member_node)142setattr(struct, member_name, member_value)143self.result = struct144145def visit_pointer(self, node):146self.result = self.interpreter.lookup_object(node.address)147148149class Dispatcher:150'''Base class for classes whose methods can dispatch Gallium calls.'''151152def __init__(self, interpreter):153self.interpreter = interpreter154155156class Global(Dispatcher):157'''Global name space.158159For calls that are not associated with objects, i.e, functions and not160methods.161'''162163def pipe_screen_create(self):164return Screen(self.interpreter)165166def pipe_context_create(self, screen):167return screen.context_create()168169170class Transfer:171'''pipe_transfer'''172173def __init__(self, resource, usage, subresource, box):174self.resource = resource175self.usage = usage176self.subresource = subresource177self.box = box178179180class Screen(Dispatcher):181'''pipe_screen'''182183def __init__(self, interpreter):184Dispatcher.__init__(self, interpreter)185186def destroy(self):187pass188189def context_create(self, priv=None, flags=0):190return Context(self.interpreter)191192def is_format_supported(self, format, target, sample_count, bind, geom_flags):193pass194195def resource_create(self, templat):196resource = templat197# Normalize state to avoid spurious differences198if resource.nr_samples == 0:199resource.nr_samples = 1200if resource.target == PIPE_BUFFER:201# We will keep track of buffer contents202resource.data = bytearray(resource.width)203# Ignore format204del resource.format205return resource206207def resource_destroy(self, resource):208self.interpreter.unregister_object(resource)209210def fence_finish(self, fence, timeout=None, ctx=None):211pass212213def fence_signalled(self, fence):214pass215216def fence_reference(self, dst, src):217pass218219def flush_frontbuffer(self, resource):220pass221222223class Context(Dispatcher):224'''pipe_context'''225226# Internal methods variable should be prefixed with '_'227228def __init__(self, interpreter):229Dispatcher.__init__(self, interpreter)230231# Setup initial state232self._state = Struct()233self._state.scissors = []234self._state.viewports = []235self._state.vertex_buffers = []236self._state.vertex_elements = []237self._state.vs = Struct()238self._state.gs = Struct()239self._state.fs = Struct()240self._state.vs.shader = None241self._state.gs.shader = None242self._state.fs.shader = None243self._state.vs.sampler = []244self._state.gs.sampler = []245self._state.fs.sampler = []246self._state.vs.sampler_views = []247self._state.gs.sampler_views = []248self._state.fs.sampler_views = []249self._state.vs.constant_buffer = []250self._state.gs.constant_buffer = []251self._state.fs.constant_buffer = []252self._state.render_condition_condition = 0253self._state.render_condition_mode = 0254255self._draw_no = 0256257def destroy(self):258pass259260def create_blend_state(self, state):261# Normalize state to avoid spurious differences262if not state.logicop_enable:263del state.logicop_func264if not state.rt[0].blend_enable:265del state.rt[0].rgb_src_factor266del state.rt[0].rgb_dst_factor267del state.rt[0].rgb_func268del state.rt[0].alpha_src_factor269del state.rt[0].alpha_dst_factor270del state.rt[0].alpha_func271return state272273def bind_blend_state(self, state):274# Normalize state275self._state.blend = state276277def delete_blend_state(self, state):278pass279280def create_sampler_state(self, state):281return state282283def delete_sampler_state(self, state):284pass285286def bind_sampler_states(self, shader, start, num_states, states):287# FIXME: Handle non-zero start288assert start == 0289self._get_stage_state(shader).sampler = states290291def bind_vertex_sampler_states(self, num_states, states):292# XXX: deprecated method293self._state.vs.sampler = states294295def bind_geometry_sampler_states(self, num_states, states):296# XXX: deprecated method297self._state.gs.sampler = states298299def bind_fragment_sampler_states(self, num_states, states):300# XXX: deprecated method301self._state.fs.sampler = states302303def create_rasterizer_state(self, state):304return state305306def bind_rasterizer_state(self, state):307self._state.rasterizer = state308309def delete_rasterizer_state(self, state):310pass311312def create_depth_stencil_alpha_state(self, state):313# Normalize state to avoid spurious differences314if not state.alpha.enabled:315del state.alpha.func316del state.alpha.ref_value317for i in range(2):318if not state.stencil[i].enabled:319del state.stencil[i].func320return state321322def bind_depth_stencil_alpha_state(self, state):323self._state.depth_stencil_alpha = state324325def delete_depth_stencil_alpha_state(self, state):326pass327328_tokenLabelRE = re.compile('^\s*\d+: ', re.MULTILINE)329330def _create_shader_state(self, state):331# Strip the labels from the tokens332if state.tokens is not None:333state.tokens = self._tokenLabelRE.sub('', state.tokens)334return state335336create_vs_state = _create_shader_state337create_gs_state = _create_shader_state338create_fs_state = _create_shader_state339340def bind_vs_state(self, state):341self._state.vs.shader = state342343def bind_gs_state(self, state):344self._state.gs.shader = state345346def bind_fs_state(self, state):347self._state.fs.shader = state348349def _delete_shader_state(self, state):350return state351352delete_vs_state = _delete_shader_state353delete_gs_state = _delete_shader_state354delete_fs_state = _delete_shader_state355356def set_blend_color(self, state):357self._state.blend_color = state358359def set_stencil_ref(self, state):360self._state.stencil_ref = state361362def set_clip_state(self, state):363self._state.clip = state364365def _dump_constant_buffer(self, buffer):366if not self.interpreter.verbosity(2):367return368369data = self.real.buffer_read(buffer)370format = '4f'371index = 0372for offset in range(0, len(data), struct.calcsize(format)):373x, y, z, w = unpack_from(format, data, offset)374sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))375index += 1376sys.stdout.flush()377378def _get_stage_state(self, shader):379if shader == PIPE_SHADER_VERTEX:380return self._state.vs381if shader == PIPE_SHADER_GEOMETRY:382return self._state.gs383if shader == PIPE_SHADER_FRAGMENT:384return self._state.fs385assert False386387def set_constant_buffer(self, shader, index, constant_buffer):388self._update(self._get_stage_state(shader).constant_buffer, index, 1, [constant_buffer])389390def set_framebuffer_state(self, state):391self._state.fb = state392393def set_polygon_stipple(self, state):394self._state.polygon_stipple = state395396def _update(self, array, start_slot, num_slots, states):397if not isinstance(states, list):398# XXX: trace is not serializing multiple scissors/viewports properly yet399num_slots = 1400states = [states]401while len(array) < start_slot + num_slots:402array.append(None)403for i in range(num_slots):404array[start_slot + i] = states[i]405406def set_scissor_states(self, start_slot, num_scissors, states):407self._update(self._state.scissors, start_slot, num_scissors, states)408409def set_viewport_states(self, start_slot, num_viewports, states):410self._update(self._state.viewports, start_slot, num_viewports, states)411412def create_sampler_view(self, resource, templ):413templ.resource = resource414return templ415416def sampler_view_destroy(self, view):417pass418419def set_sampler_views(self, shader, start, num, views):420# FIXME: Handle non-zero start421assert start == 0422self._get_stage_state(shader).sampler_views = views423424def set_fragment_sampler_views(self, num, views):425# XXX: deprecated426self._state.fs.sampler_views = views427428def set_geometry_sampler_views(self, num, views):429# XXX: deprecated430self._state.gs.sampler_views = views431432def set_vertex_sampler_views(self, num, views):433# XXX: deprecated434self._state.vs.sampler_views = views435436def set_vertex_buffers(self, start_slot, num_buffers, buffers):437self._update(self._state.vertex_buffers, start_slot, num_buffers, buffers)438439def create_vertex_elements_state(self, num_elements, elements):440return elements[0:num_elements]441442def bind_vertex_elements_state(self, state):443self._state.vertex_elements = state444445def delete_vertex_elements_state(self, state):446pass447448def set_index_buffer(self, ib):449self._state.index_buffer = ib450451# Don't dump more than this number of indices/vertices452MAX_ELEMENTS = 16453454def _merge_indices(self, info):455'''Merge the vertices into our state.'''456457index_size = self._state.index_buffer.index_size458459format = {4601: 'B',4612: 'H',4624: 'I',463}[index_size]464465assert struct.calcsize(format) == index_size466467if self._state.index_buffer.buffer is None:468# Could happen with index in user memory469return 0, 0470471data = self._state.index_buffer.buffer.data472max_index, min_index = 0, 0xffffffff473474count = min(info.count, self.MAX_ELEMENTS)475indices = []476for i in range(info.start, info.start + count):477offset = self._state.index_buffer.offset + i*index_size478if offset + index_size > len(data):479index = 0480else:481index, = unpack_from(format, data, offset)482indices.append(index)483min_index = min(min_index, index)484max_index = max(max_index, index)485486self._state.indices = indices487488return min_index + info.index_bias, max_index + info.index_bias489490def _merge_vertices(self, start, count):491'''Merge the vertices into our state.'''492493count = min(count, self.MAX_ELEMENTS)494vertices = []495for index in range(start, start + count):496if index >= start + 16:497sys.stdout.write('\t...\n')498break499vertex = []500for velem in self._state.vertex_elements:501vbuf = self._state.vertex_buffers[velem.vertex_buffer_index]502resource = vbuf.buffer_resource503if resource is None:504continue505506data = resource.data507508offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index509format = {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]527528data = resource.data529attribute = unpack_from(format, data, offset)530vertex.append(attribute)531532vertices.append(vertex)533534self._state.vertices = vertices535536def render_condition(self, query, condition = 0, mode = 0):537self._state.render_condition_query = query538self._state.render_condition_condition = condition539self._state.render_condition_mode = mode540541def set_stream_output_targets(self, num_targets, tgs, offsets):542self._state.so_targets = tgs543self._state.offsets = offsets544545def draw_vbo(self, info):546self._draw_no += 1547548if self.interpreter.call_no < self.interpreter.options.call and \549self._draw_no < self.interpreter.options.draw:550return551552# Merge the all draw state553554self._state.draw = info555556if info.index_size != 0:557min_index, max_index = self._merge_indices(info)558else:559min_index = info.start560max_index = info.start + info.count - 1561self._merge_vertices(min_index, max_index - min_index + 1)562563self._dump_state()564565_dclRE = re.compile('^DCL\s+(IN|OUT|SAMP|SVIEW)\[([0-9]+)\].*$', re.MULTILINE)566567def _normalize_stage_state(self, stage):568569registers = {}570571if stage.shader is not None and stage.shader.tokens is not None:572for mo in self._dclRE.finditer(stage.shader.tokens):573file_ = mo.group(1)574index = mo.group(2)575register = registers.setdefault(file_, set())576register.add(int(index))577578if 'SAMP' in registers and 'SVIEW' not in registers:579registers['SVIEW'] = registers['SAMP']580581mapping = [582#("CONST", "constant_buffer"),583("SAMP", "sampler"),584("SVIEW", "sampler_views"),585]586587for fileName, attrName in mapping:588register = registers.setdefault(fileName, set())589attr = getattr(stage, attrName)590for index in range(len(attr)):591if index not in register:592attr[index] = None593while attr and attr[-1] is None:594attr.pop()595596def _dump_state(self):597'''Dump our state to JSON and terminate.'''598599state = copy.deepcopy(self._state)600601self._normalize_stage_state(state.vs)602self._normalize_stage_state(state.gs)603self._normalize_stage_state(state.fs)604605json.dump(606obj = state,607fp = sys.stdout,608default = serialize,609sort_keys = True,610indent = 4,611separators = (',', ': ')612)613614sys.exit(0)615616def resource_copy_region(self, dst, dst_level, dstx, dsty, dstz, src, src_level, src_box):617if dst.target == PIPE_BUFFER or src.target == PIPE_BUFFER:618assert dst.target == PIPE_BUFFER and src.target == PIPE_BUFFER619assert dst_level == 0620assert dsty == 0621assert dstz == 0622assert src_level == 0623assert src_box.y == 0624assert src_box.z == 0625assert src_box.height == 1626assert src_box.depth == 1627dst.data[dstx : dstx + src_box.width] = src.data[src_box.x : src_box.x + src_box.width]628pass629630def is_resource_referenced(self, texture, face, level):631pass632633def get_transfer(self, texture, sr, usage, box):634if texture is None:635return None636transfer = Transfer(texture, sr, usage, box)637return transfer638639def tex_transfer_destroy(self, transfer):640self.interpreter.unregister_object(transfer)641642def buffer_subdata(self, resource, usage, data, box=None, offset=None, size=None, level=None, stride=None, layer_stride=None):643if box is not None:644# XXX trace_context_transfer_unmap generates brokens buffer_subdata645assert offset is None646assert size is None647assert level == 0648offset = box.x649size = box.width650box = None651652if resource is not None and resource.target == PIPE_BUFFER:653data = data.getValue()654assert len(data) >= size655assert offset + size <= len(resource.data)656resource.data[offset : offset + size] = data[:size]657658def texture_subdata(self, resource, level, usage, box, data, stride, layer_stride):659pass660661def transfer_inline_write(self, resource, level, usage, box, stride, layer_stride, data):662if resource is not None and resource.target == PIPE_BUFFER:663data = data.getValue()664assert len(data) >= box.width665assert box.x + box.width <= len(resource.data)666resource.data[box.x : box.x + box.width] = data[:box.width]667668def flush(self, flags):669# Return a fake fence670return self.interpreter.call_no671672def clear(self, buffers, color, depth, stencil, scissor_state=None):673pass674675def clear_render_target(self, dst, rgba, dstx, dsty, width, height):676pass677678def clear_depth_stencil(self, dst, clear_flags, depth, stencil, dstx, dsty, width, height):679pass680681def create_surface(self, resource, surf_tmpl):682assert resource is not None683surf_tmpl.resource = resource684return surf_tmpl685686def surface_destroy(self, surface):687self.interpreter.unregister_object(surface)688689def create_query(self, query_type, index):690return query_type691692def destroy_query(self, query):693pass694695def begin_query(self, query):696pass697698def end_query(self, query):699pass700701def create_stream_output_target(self, res, buffer_offset, buffer_size):702so_target = Struct()703so_target.resource = res704so_target.offset = buffer_offset705so_target.size = buffer_size706return so_target707708709class Interpreter(parser.SimpleTraceDumper):710'''Specialization of a trace parser that interprets the calls as it goes711along.'''712713ignoredCalls = 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_screen', 'get_disk_shader_cache'),721('pipe_context', 'clear_render_target'), # XXX workaround trace bugs722('pipe_context', 'flush_resource'),723))724725def __init__(self, stream, options, formatter):726parser.SimpleTraceDumper.__init__(self, stream, options, formatter)727self.objects = {}728self.result = None729self.globl = Global(self)730self.call_no = None731732def register_object(self, address, object):733self.objects[address] = object734735def unregister_object(self, object):736# TODO737pass738739def lookup_object(self, address):740try:741return self.objects[address]742except KeyError:743# Could happen, e.g., with user memory pointers744return address745746def interpret(self, trace):747for call in trace.calls:748self.interpret_call(call)749750def handle_call(self, call):751if (call.klass, call.method) in self.ignoredCalls:752return753754self.call_no = call.no755756if self.verbosity(1):757# Write the call to stderr (as stdout would corrupt the JSON output)758sys.stderr.flush()759sys.stdout.flush()760parser.TraceDumper.handle_call(self, call)761sys.stderr.flush()762sys.stdout.flush()763764args = [(str(name), self.interpret_arg(arg)) for name, arg in call.args]765766if call.klass:767name, obj = args[0]768args = args[1:]769else:770obj = self.globl771772method = getattr(obj, call.method)773ret = method(**dict(args))774775# Keep track of created pointer objects.776if call.ret and isinstance(call.ret, model.Pointer):777if ret is None:778sys.stderr.write('warning: NULL returned\n')779self.register_object(call.ret.address, ret)780781self.call_no = None782783def interpret_arg(self, node):784translator = Translator(self)785return translator.visit(node)786787def verbosity(self, level):788return self.options.verbosity >= level789790791class Main(parser.Main):792793def get_optparser(self):794optparser = argparse.ArgumentParser(795description="Parse and dump Gallium trace(s) as JSON")796797optparser.add_argument("filename", action="extend", nargs="+",798type=str, metavar="filename", help="Gallium trace filename (plain or .gz, .bz2)")799800optparser.add_argument("-v", "--verbose", action="count", default=0, dest="verbosity", help="increase verbosity level")801optparser.add_argument("-q", "--quiet", action="store_const", const=0, dest="verbosity", help="no messages")802optparser.add_argument("-c", "--call", action="store", type=int, dest="call", default=0xffffffff, help="dump on this call")803optparser.add_argument("-d", "--draw", action="store", type=int, dest="draw", default=0xffffffff, help="dump on this draw")804return optparser805806def process_arg(self, stream, options):807formatter = format.Formatter(sys.stderr)808parser = Interpreter(stream, options, formatter)809parser.parse()810811812if __name__ == '__main__':813Main().main()814815816