python/retrace: Cope with null constant buffers.
[mesa.git] / src / gallium / state_trackers / python / retrace / interpreter.py
index 63ae54736b9f2c3e16801e5614f7b38f2604cada..348f2e436831e89186895fc4a140e06ebf36de63 100755 (executable)
@@ -32,38 +32,44 @@ import struct
 
 import gallium
 import model
-import parser
+import parse as parser
 
 
-def make_image(surface):
-    pixels = gallium.FloatArray(surface.height*surface.width*4)
-    surface.get_tile_rgba(0, 0, surface.width, surface.height, pixels)
+try:
+    from struct import unpack_from
+except ImportError:
+    def unpack_from(fmt, buf, offset=0):
+        size = struct.calcsize(fmt)
+        return struct.unpack(fmt, buf[offset:offset + size])
+
+
+def make_image(surface, x=None, y=None, w=None, h=None):
+    if x is None:
+        x = 0
+    if y is None:
+        y = 0
+    if w is None:
+        w = surface.width - x
+    if h is None:
+        h = surface.height - y
+    data = surface.get_tile_rgba8(0, 0, surface.width, surface.height)
 
     import Image
-    outimage = Image.new(
-        mode='RGB',
-        size=(surface.width, surface.height),
-        color=(0,0,0))
-    outpixels = outimage.load()
-    for y in range(0, surface.height):
-        for x in range(0, surface.width):
-            offset = (y*surface.width + x)*4
-            r, g, b, a = [int(pixels[offset + ch]*255) for ch in range(4)]
-            outpixels[x, y] = r, g, b
+    outimage = Image.fromstring('RGBA', (surface.width, surface.height), data, "raw", 'RGBA', 0, 1)
     return outimage
 
-def save_image(filename, surface):
-    outimage = make_image(surface)
+def save_image(filename, surface, x=None, y=None, w=None, h=None):
+    outimage = make_image(surface, x, y, w, h)
     outimage.save(filename, "PNG")
 
-def show_image(surface):
-    outimage = make_image(surface)
+def show_image(surface, title, x=None, y=None, w=None, h=None):
+    outimage = make_image(surface, x, y, w, h)
     
     import Tkinter as tk
     from PIL import Image, ImageTk
     root = tk.Tk()
     
-    root.title('background image')
+    root.title(title)
     
     image1 = ImageTk.PhotoImage(outimage)
     w = image1.width()
@@ -77,8 +83,6 @@ def show_image(surface):
     root.mainloop()
 
 
-
-
 class Struct:
     """C-like struct"""
 
@@ -185,8 +189,12 @@ class Global(Object):
     def pipe_winsys_create(self):
         return Winsys(self.interpreter, gallium.Device())
 
-    def pipe_screen_create(self, winsys):
-        return Screen(self.interpreter, winsys.real)
+    def pipe_screen_create(self, winsys=None):
+        if winsys is None:
+            real = gallium.Device()
+        else:
+            real = winsys.real
+        return Screen(self.interpreter, real)
     
     def pipe_context_create(self, screen):
         context = screen.real.context_create()
@@ -238,6 +246,16 @@ class Winsys(Object):
         pass
 
 
+class Transfer:
+
+    def __init__(self, surface, x, y, w, h):
+        self.surface = surface
+        self.x = x
+        self.y = y
+        self.w = w
+        self.h = h
+
+
 class Screen(Object):
     
     def destroy(self):
@@ -258,15 +276,15 @@ class Screen(Object):
     def is_format_supported(self, format, target, tex_usage, geom_flags):
         return self.real.is_format_supported(format, target, tex_usage, geom_flags)
     
-    def texture_create(self, template):
+    def texture_create(self, templat):
         return self.real.texture_create(
-            format = template.format,
-            width = template.width[0],
-            height = template.height[0],
-            depth = template.depth[0],
-            last_level = template.last_level,
-            target = template.target,
-            tex_usage = template.tex_usage,
+            format = templat.format,
+            width = templat.width[0],
+            height = templat.height[0],
+            depth = templat.depth[0],
+            last_level = templat.last_level,
+            target = templat.target,
+            tex_usage = templat.tex_usage,
         )
 
     def texture_destroy(self, texture):
@@ -276,7 +294,9 @@ class Screen(Object):
         pass
 
     def get_tex_surface(self, texture, face, level, zslice, usage):
-        return texture.get_surface(face, level, zslice, usage)
+        if texture is None:
+            return None
+        return texture.get_surface(face, level, zslice)
     
     def tex_surface_destroy(self, surface):
         self.interpreter.unregister_object(surface)
@@ -285,9 +305,59 @@ class Screen(Object):
         pass
 
     def surface_write(self, surface, data, stride, size):
+        if surface is None:
+            return
         assert surface.nblocksy * stride == size 
         surface.put_tile_raw(0, 0, surface.width, surface.height, data, stride)
 
+    def get_tex_transfer(self, texture, face, level, zslice, usage, x, y, w, h):
+        if texture is None:
+            return None
+        transfer = Transfer(texture.get_surface(face, level, zslice), x, y, w, h)
+        if transfer and usage & gallium.PIPE_TRANSFER_READ:
+            if self.interpreter.options.all:
+                self.interpreter.present(transfer.surface, 'transf_read', x, y, w, h)
+        return transfer
+    
+    def tex_transfer_destroy(self, transfer):
+        self.interpreter.unregister_object(transfer)
+
+    def transfer_write(self, transfer, stride, data, size):
+        if transfer is None:
+            return
+        transfer.surface.put_tile_raw(transfer.x, transfer.y, transfer.w, transfer.h, data, stride)
+        if self.interpreter.options.all:
+            self.interpreter.present(transfer.surface, 'transf_write', transfer.x, transfer.y, transfer.w, transfer.h)
+
+    def user_buffer_create(self, data, size):
+        # We don't really care to distinguish between user and regular buffers
+        buffer = self.real.buffer_create(size, 
+                                         4, 
+                                         gallium.PIPE_BUFFER_USAGE_CPU_READ |
+                                         gallium.PIPE_BUFFER_USAGE_CPU_WRITE )
+        assert size == len(data)
+        buffer.write(data)
+        return buffer
+    
+    def buffer_create(self, alignment, usage, size):
+        return self.real.buffer_create(size, alignment, usage)
+    
+    def buffer_destroy(self, buffer):
+        pass
+    
+    def buffer_write(self, buffer, data, size, offset=0):
+        assert size == len(data)
+        buffer.write(data)
+        
+    def fence_finish(self, fence, flags):
+        pass
+    
+    def fence_reference(self, dst, src):
+        pass
+    
+    def flush_frontbuffer(self, surface):
+        pass
+
 
 class Context(Object):
     
@@ -297,6 +367,7 @@ class Context(Object):
         self.zsbuf = None
         self.vbufs = []
         self.velems = []
+        self.dirty = False
 
     def destroy(self):
         pass
@@ -317,8 +388,8 @@ class Context(Object):
     def delete_sampler_state(self, state):
         pass
 
-    def bind_sampler_states(self, n, states):
-        for i in range(n):
+    def bind_sampler_states(self, num_states, states):
+        for i in range(num_states):
             self.real.set_sampler(i, states[i])
         
     def create_rasterizer_state(self, state):
@@ -374,24 +445,30 @@ class Context(Object):
             _state.ucp = ucp
         self.real.set_clip(_state)
 
-    def set_constant_buffer(self, shader, index, state):
-        if state is not None:
-            self.real.set_constant_buffer(shader, index, state.buffer)
+    def dump_constant_buffer(self, buffer):
+        if not self.interpreter.verbosity(2):
+            return
+
+        data = buffer.read()
+        format = '4f'
+        index = 0
+        for offset in range(0, len(data), struct.calcsize(format)):
+            x, y, z, w = unpack_from(format, data, offset)
+            sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))
+            index += 1
+        sys.stdout.flush()
 
-            if 1:
-                data = state.buffer.read()
-                format = '4f'
-                index = 0
-                for offset in range(0, len(data), struct.calcsize(format)):
-                    x, y, z, w = struct.unpack_from(format, data, offset)
-                    sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))
-                    index += 1
+    def set_constant_buffer(self, shader, index, buffer):
+        if buffer is not None and buffer.buffer is not None:
+            self.real.set_constant_buffer(shader, index, buffer.buffer)
+
+            self.dump_constant_buffer(buffer.buffer)
 
     def set_framebuffer_state(self, state):
         _state = gallium.Framebuffer()
         _state.width = state.width
         _state.height = state.height
-        _state.num_cbufs = state.num_cbufs
+        _state.nr_cbufs = state.nr_cbufs
         for i in range(len(state.cbufs)):
             _state.set_cbuf(i, state.cbufs[i])
         _state.set_zsbuf(state.zsbuf)    
@@ -409,72 +486,164 @@ class Context(Object):
     def set_viewport_state(self, state):
         self.real.set_viewport(state)
 
-    def set_sampler_textures(self, n, textures):
-        for i in range(n):
+    def set_sampler_textures(self, num_textures, textures):
+        for i in range(num_textures):
             self.real.set_sampler_texture(i, textures[i])
 
-    def set_vertex_buffers(self, n, vbufs):
-        self.vbufs = vbufs[0:n]
-        for i in range(n):
-            vbuf = vbufs[i]
+    def set_vertex_buffers(self, num_buffers, buffers):
+        self.vbufs = buffers[0:num_buffers]
+        for i in range(num_buffers):
+            vbuf = buffers[i]
             self.real.set_vertex_buffer(
                 i,
-                pitch = vbuf.pitch,
+                stride = vbuf.stride,
                 max_index = vbuf.max_index,
                 buffer_offset = vbuf.buffer_offset,
                 buffer = vbuf.buffer,
             )
 
-    def set_vertex_elements(self, n, elements):
-        self.velems = elements[0:n]
-        for i in range(n):
+    def set_vertex_elements(self, num_elements, elements):
+        self.velems = elements[0:num_elements]
+        for i in range(num_elements):
             self.real.set_vertex_element(i, elements[i])
-        self.real.set_vertex_elements(n)
+        self.real.set_vertex_elements(num_elements)
 
     def set_edgeflags(self, bitfield):
         # FIXME
         pass
     
-    def draw_arrays(self, mode, start, count):
+    def dump_vertices(self, start, count):
+        if not self.interpreter.verbosity(2):
+            return
+
         for index in range(start, start + count):
+            if index >= start + 16:
+                sys.stdout.write('\t...\n')
+                break
             sys.stdout.write('\t{\n')
             for velem in self.velems:
                 vbuf = self.vbufs[velem.vertex_buffer_index]
 
-                offset = vbuf.buffer_offset + velem.src_offset + vbuf.pitch*index
+                offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index
                 format = {
+                    gallium.PIPE_FORMAT_R32_FLOAT: 'f',
+                    gallium.PIPE_FORMAT_R32G32_FLOAT: '2f',
                     gallium.PIPE_FORMAT_R32G32B32_FLOAT: '3f',
+                    gallium.PIPE_FORMAT_R32G32B32A32_FLOAT: '4f',
                     gallium.PIPE_FORMAT_B8G8R8A8_UNORM: '4B',
                 }[velem.src_format]
 
                 data = vbuf.buffer.read()
-                values = struct.unpack_from(format, data, offset)
+                values = unpack_from(format, data, offset)
                 sys.stdout.write('\t\t{' + ', '.join(map(str, values)) + '},\n')
                 assert len(values) == velem.nr_components
             sys.stdout.write('\t},\n')
+        sys.stdout.flush()
+
+    def dump_indices(self, ibuf, isize, start, count):
+        if not self.interpreter.verbosity(2):
+            return
+
+        format = {
+            1: 'B',
+            2: 'H',
+            4: 'I',
+        }[isize]
+
+        assert struct.calcsize(format) == isize
+
+        data = ibuf.read()
+        maxindex, minindex = 0, 0xffffffff
+
+        sys.stdout.write('\t{\n')
+        for i in range(start, start + count):
+            if i >= start + 16:
+                sys.stdout.write('\t...\n')
+                break
+            offset = i*isize
+            index, = unpack_from(format, data, offset)
+            sys.stdout.write('\t\t%u,\n' % index)
+            minindex = min(minindex, index)
+            maxindex = max(maxindex, index)
+        sys.stdout.write('\t},\n')
+        sys.stdout.flush()
+
+        return minindex, maxindex
+
+    def draw_arrays(self, mode, start, count):
+        self.dump_vertices(start, count)
             
         self.real.draw_arrays(mode, start, count)
+        self._set_dirty()
     
     def draw_elements(self, indexBuffer, indexSize, mode, start, count):
+        if self.interpreter.verbosity(2):
+            minindex, maxindex = self.dump_indices(indexBuffer, indexSize, start, count)
+            self.dump_vertices(minindex, maxindex - minindex)
+
         self.real.draw_elements(indexBuffer, indexSize, mode, start, count)
+        self._set_dirty()
         
     def draw_range_elements(self, indexBuffer, indexSize, minIndex, maxIndex, mode, start, count):
+        if self.interpreter.verbosity(2):
+            minindex, maxindex = self.dump_indices(indexBuffer, indexSize, start, count)
+            minindex = min(minindex, minIndex)
+            maxindex = min(maxindex, maxIndex)
+            self.dump_vertices(minindex, maxindex - minindex)
+
         self.real.draw_range_elements(indexBuffer, indexSize, minIndex, maxIndex, mode, start, count)
+        self._set_dirty()
         
+    def surface_copy(self, dest, destx, desty, src, srcx, srcy, width, height):
+        if dest is not None and src is not None:
+            if self.interpreter.options.all:
+                self.interpreter.present(src, 'surface_copy_src', srcx, srcy, width, height)
+            self.real.surface_copy(dest, destx, desty, src, srcx, srcy, width, height)
+            if dest in self.cbufs:
+                self._set_dirty()
+                flags = gallium.PIPE_FLUSH_FRAME
+            else:
+                flags = 0
+            self.flush(flags)
+            if self.interpreter.options.all:
+                self.interpreter.present(dest, 'surface_copy_dest', destx, desty, width, height)
+
+    def is_texture_referenced(self, texture, face, level):
+        #return self.real.is_texture_referenced(format, texture, face, level)
+        pass
+    
+    def is_buffer_referenced(self, buf):
+        #return self.real.is_buffer_referenced(format, buf)
+        pass
+    
+    def _set_dirty(self):
+        if self.interpreter.options.step:
+            self._present()
+        else:
+            self.dirty = True
+
     def flush(self, flags):
         self.real.flush(flags)
-        if flags & gallium.PIPE_FLUSH_FRAME:
-            self._update()
+        if self.dirty:
+            if flags & gallium.PIPE_FLUSH_FRAME:
+                self._present()
+            self.dirty = False
         return None
 
-    def clear(self, surface, value):
-        self.real.surface_clear(surface, value)
+    def clear(self, buffers, rgba, depth, stencil):
+        _rgba = gallium.FloatArray(4)
+        for i in range(4):
+            _rgba[i] = rgba[i]
+        self.real.clear(buffers, _rgba, depth, stencil)
         
-    def _update(self):
+    def _present(self):
         self.real.flush()
     
         if self.cbufs and self.cbufs[0]:
-            show_image(self.cbufs[0])
+            self.interpreter.present(self.cbufs[0], "cbuf")
+        if self.zsbuf:
+            if self.interpreter.options.all:
+                self.interpreter.present(self.zsbuf, "zsbuf")
     
 
 class Interpreter(parser.TraceDumper):
@@ -485,11 +654,13 @@ class Interpreter(parser.TraceDumper):
             ('pipe_screen', 'get_paramf'),
     ))
 
-    def __init__(self, stream):
+    def __init__(self, stream, options):
         parser.TraceDumper.__init__(self, stream)
+        self.options = options
         self.objects = {}
         self.result = None
         self.globl = Global(self, None)
+        self.call_no = None
 
     def register_object(self, address, object):
         self.objects[address] = object
@@ -506,30 +677,72 @@ class Interpreter(parser.TraceDumper):
             self.interpret_call(call)
 
     def handle_call(self, call):
+        if self.options.stop and call.no > self.options.stop:
+            sys.exit(0)
 
         if (call.klass, call.method) in self.ignore_calls:
             return
 
-        parser.TraceDumper.handle_call(self, call)
+        self.call_no = call.no
+
+        if self.verbosity(1):
+            parser.TraceDumper.handle_call(self, call)
+            sys.stdout.flush()
         
-        args = [self.interpret_arg(arg) for name, arg in call.args] 
+        args = [(str(name), self.interpret_arg(arg)) for name, arg in call.args] 
         
         if call.klass:
-            obj = args[0]
+            name, obj = args[0]
             args = args[1:]
         else:
             obj = self.globl
             
         method = getattr(obj, call.method)
-        ret = method(*args)
+        ret = method(**dict(args))
         
         if call.ret and isinstance(call.ret, model.Pointer):
+            if ret is None:
+                sys.stderr.write('warning: NULL returned\n')
             self.register_object(call.ret.address, ret)
 
+        self.call_no = None
+
     def interpret_arg(self, node):
         translator = Translator(self)
         return translator.visit(node)
+
+    def verbosity(self, level):
+        return self.options.verbosity >= level
+
+    def present(self, surface, description, x=None, y=None, w=None, h=None):
+        if self.call_no < self.options.start:
+            return
+
+        if self.options.images:
+            filename = '%04u_%s.png' % (self.call_no, description)
+            save_image(filename, surface, x, y, w, h)
+        else:
+            title = '%u. %s' % (self.call_no, description)
+            show_image(surface, title, x, y, w, h)
     
 
+class Main(parser.Main):
+
+    def get_optparser(self):
+        optparser = parser.Main.get_optparser(self)
+        optparser.add_option("-q", "--quiet", action="store_const", const=0, dest="verbosity", help="no messages")
+        optparser.add_option("-v", "--verbose", action="count", dest="verbosity", default=1, help="increase verbosity level")
+        optparser.add_option("-i", "--images", action="store_true", dest="images", default=False, help="save images instead of showing them")
+        optparser.add_option("-a", "--all", action="store_true", dest="all", default=False, help="show depth, stencil, and transfers")
+        optparser.add_option("-s", "--step", action="store_true", dest="step", default=False, help="step trhough every draw")
+        optparser.add_option("-f", "--from", action="store", type="int", dest="start", default=0, help="from call no")
+        optparser.add_option("-t", "--to", action="store", type="int", dest="stop", default=0, help="until call no")
+        return optparser
+
+    def process_arg(self, stream, options):
+        parser = Interpreter(stream, options)
+        parser.parse()
+
+
 if __name__ == '__main__':
-    parser.main(Interpreter)
+    Main().main()