python: Allow to dump all images to disk.
[mesa.git] / src / gallium / state_trackers / python / retrace / interpreter.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
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 TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
23 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #
27 ##########################################################################
28
29
30 import sys
31 import struct
32
33 import gallium
34 import model
35 import parser
36
37
38 try:
39 from struct import unpack_from
40 except ImportError:
41 def unpack_from(fmt, buf, offset=0):
42 size = struct.calcsize(fmt)
43 return struct.unpack(fmt, buf[offset:offset + size])
44
45
46 def make_image(surface):
47 data = surface.get_tile_rgba8(0, 0, surface.width, surface.height)
48
49 import Image
50 outimage = Image.fromstring('RGBA', (surface.width, surface.height), data, "raw", 'RGBA', 0, 1)
51 return outimage
52
53 def save_image(filename, surface):
54 outimage = make_image(surface)
55 outimage.save(filename, "PNG")
56
57 def show_image(surface):
58 outimage = make_image(surface)
59
60 import Tkinter as tk
61 from PIL import Image, ImageTk
62 root = tk.Tk()
63
64 root.title('background image')
65
66 image1 = ImageTk.PhotoImage(outimage)
67 w = image1.width()
68 h = image1.height()
69 x = 100
70 y = 100
71 root.geometry("%dx%d+%d+%d" % (w, h, x, y))
72 panel1 = tk.Label(root, image=image1)
73 panel1.pack(side='top', fill='both', expand='yes')
74 panel1.image = image1
75 root.mainloop()
76
77
78 # Verbosity level: 0, 1, 2
79 verbose = 1
80 # Dump images to disk instead of showing: True, False
81 images = False
82
83
84 image_no = 0
85
86
87 class Struct:
88 """C-like struct"""
89
90 # A basic Python class can pass as a C-like structure
91 pass
92
93
94 struct_factories = {
95 "pipe_blend_color": gallium.BlendColor,
96 "pipe_blend_state": gallium.Blend,
97 #"pipe_clip_state": gallium.Clip,
98 #"pipe_constant_buffer": gallium.ConstantBuffer,
99 "pipe_depth_state": gallium.Depth,
100 "pipe_stencil_state": gallium.Stencil,
101 "pipe_alpha_state": gallium.Alpha,
102 "pipe_depth_stencil_alpha_state": gallium.DepthStencilAlpha,
103 "pipe_format_block": gallium.FormatBlock,
104 #"pipe_framebuffer_state": gallium.Framebuffer,
105 "pipe_poly_stipple": gallium.PolyStipple,
106 "pipe_rasterizer_state": gallium.Rasterizer,
107 "pipe_sampler_state": gallium.Sampler,
108 "pipe_scissor_state": gallium.Scissor,
109 #"pipe_shader_state": gallium.Shader,
110 #"pipe_vertex_buffer": gallium.VertexBuffer,
111 "pipe_vertex_element": gallium.VertexElement,
112 "pipe_viewport_state": gallium.Viewport,
113 #"pipe_texture": gallium.Texture,
114 }
115
116
117 member_array_factories = {
118 "pipe_rasterizer_state": {"sprite_coord_mode": gallium.ByteArray},
119 "pipe_poly_stipple": {"stipple": gallium.UnsignedArray},
120 "pipe_viewport_state": {"scale": gallium.FloatArray, "translate": gallium.FloatArray},
121 #"pipe_clip_state": {"ucp": gallium.FloatArray},
122 "pipe_depth_stencil_alpha_state": {"stencil": gallium.StencilArray},
123 "pipe_blend_color": {"color": gallium.FloatArray},
124 "pipe_sampler_state": {"border_color": gallium.FloatArray},
125 }
126
127
128 class Translator(model.Visitor):
129 """Translate model arguments into regular Python objects"""
130
131 def __init__(self, interpreter):
132 self.interpreter = interpreter
133 self.result = None
134
135 def visit(self, node):
136 self.result = None
137 node.visit(self)
138 return self.result
139
140 def visit_literal(self, node):
141 self.result = node.value
142
143 def visit_named_constant(self, node):
144 # lookup the named constant in the gallium module
145 self.result = getattr(gallium, node.name)
146
147 def visit_array(self, node):
148 array = []
149 for element in node.elements:
150 array.append(self.visit(element))
151 self.result = array
152
153 def visit_struct(self, node):
154 struct_factory = struct_factories.get(node.name, Struct)
155 struct = struct_factory()
156 for member_name, member_node in node.members:
157 member_value = self.visit(member_node)
158 try:
159 array_factory = member_array_factories[node.name][member_name]
160 except KeyError:
161 pass
162 else:
163 assert isinstance(member_value, list)
164 array = array_factory(len(member_value))
165 for i in range(len(member_value)):
166 array[i] = member_value[i]
167 member_value = array
168 #print node.name, member_name, member_value
169 assert isinstance(struct, Struct) or hasattr(struct, member_name)
170 setattr(struct, member_name, member_value)
171 self.result = struct
172
173 def visit_pointer(self, node):
174 self.result = self.interpreter.lookup_object(node.address)
175
176
177 class Object:
178
179 def __init__(self, interpreter, real):
180 self.interpreter = interpreter
181 self.real = real
182
183
184 class Global(Object):
185
186 def __init__(self, interpreter, real):
187 self.interpreter = interpreter
188 self.real = real
189
190 def pipe_winsys_create(self):
191 return Winsys(self.interpreter, gallium.Device())
192
193 def pipe_screen_create(self, winsys):
194 return Screen(self.interpreter, winsys.real)
195
196 def pipe_context_create(self, screen):
197 context = screen.real.context_create()
198 return Context(self.interpreter, context)
199
200
201 class Winsys(Object):
202
203 def __init__(self, interpreter, real):
204 self.interpreter = interpreter
205 self.real = real
206
207 def get_name(self):
208 pass
209
210 def user_buffer_create(self, data, size):
211 # We don't really care to distinguish between user and regular buffers
212 buffer = self.real.buffer_create(size,
213 4,
214 gallium.PIPE_BUFFER_USAGE_CPU_READ |
215 gallium.PIPE_BUFFER_USAGE_CPU_WRITE )
216 assert size == len(data)
217 buffer.write(data)
218 return buffer
219
220 def buffer_create(self, alignment, usage, size):
221 return self.real.buffer_create(size, alignment, usage)
222
223 def buffer_destroy(self, buffer):
224 pass
225
226 def buffer_write(self, buffer, data, size):
227 assert size == len(data)
228 buffer.write(data)
229
230 def fence_finish(self, fence, flags):
231 pass
232
233 def fence_reference(self, dst, src):
234 pass
235
236 def flush_frontbuffer(self, surface):
237 pass
238
239 def surface_alloc(self):
240 return None
241
242 def surface_release(self, surface):
243 pass
244
245
246 class Screen(Object):
247
248 def destroy(self):
249 pass
250
251 def get_name(self):
252 pass
253
254 def get_vendor(self):
255 pass
256
257 def get_param(self, param):
258 pass
259
260 def get_paramf(self, param):
261 pass
262
263 def is_format_supported(self, format, target, tex_usage, geom_flags):
264 return self.real.is_format_supported(format, target, tex_usage, geom_flags)
265
266 def texture_create(self, template):
267 return self.real.texture_create(
268 format = template.format,
269 width = template.width[0],
270 height = template.height[0],
271 depth = template.depth[0],
272 last_level = template.last_level,
273 target = template.target,
274 tex_usage = template.tex_usage,
275 )
276
277 def texture_destroy(self, texture):
278 self.interpreter.unregister_object(texture)
279
280 def texture_release(self, surface):
281 pass
282
283 def get_tex_surface(self, texture, face, level, zslice, usage):
284 return texture.get_surface(face, level, zslice, usage)
285
286 def tex_surface_destroy(self, surface):
287 self.interpreter.unregister_object(surface)
288
289 def tex_surface_release(self, surface):
290 pass
291
292 def surface_write(self, surface, data, stride, size):
293 assert surface.nblocksy * stride == size
294 surface.put_tile_raw(0, 0, surface.width, surface.height, data, stride)
295
296
297 class Context(Object):
298
299 def __init__(self, interpreter, real):
300 Object.__init__(self, interpreter, real)
301 self.cbufs = []
302 self.zsbuf = None
303 self.vbufs = []
304 self.velems = []
305 self.dirty = False
306
307 def destroy(self):
308 pass
309
310 def create_blend_state(self, state):
311 return state
312
313 def bind_blend_state(self, state):
314 if state is not None:
315 self.real.set_blend(state)
316
317 def delete_blend_state(self, state):
318 pass
319
320 def create_sampler_state(self, state):
321 return state
322
323 def delete_sampler_state(self, state):
324 pass
325
326 def bind_sampler_states(self, n, states):
327 for i in range(n):
328 self.real.set_sampler(i, states[i])
329
330 def create_rasterizer_state(self, state):
331 return state
332
333 def bind_rasterizer_state(self, state):
334 if state is not None:
335 self.real.set_rasterizer(state)
336
337 def delete_rasterizer_state(self, state):
338 pass
339
340 def create_depth_stencil_alpha_state(self, state):
341 return state
342
343 def bind_depth_stencil_alpha_state(self, state):
344 if state is not None:
345 self.real.set_depth_stencil_alpha(state)
346
347 def delete_depth_stencil_alpha_state(self, state):
348 pass
349
350 def create_fs_state(self, state):
351 tokens = str(state.tokens)
352 shader = gallium.Shader(tokens)
353 return shader
354
355 create_vs_state = create_fs_state
356
357 def bind_fs_state(self, state):
358 self.real.set_fragment_shader(state)
359
360 def bind_vs_state(self, state):
361 self.real.set_vertex_shader(state)
362
363 def delete_fs_state(self, state):
364 pass
365
366 delete_vs_state = delete_fs_state
367
368 def set_blend_color(self, state):
369 self.real.set_blend_color(state)
370
371 def set_clip_state(self, state):
372 _state = gallium.Clip()
373 _state.nr = state.nr
374 if state.nr:
375 # FIXME
376 ucp = gallium.FloatArray(gallium.PIPE_MAX_CLIP_PLANES*4)
377 for i in range(len(state.ucp)):
378 for j in range(len(state.ucp[i])):
379 ucp[i*4 + j] = state.ucp[i][j]
380 _state.ucp = ucp
381 self.real.set_clip(_state)
382
383 def dump_constant_buffer(self, buffer):
384 if verbose < 2:
385 return
386
387 data = buffer.read()
388 format = '4f'
389 index = 0
390 for offset in range(0, len(data), struct.calcsize(format)):
391 x, y, z, w = unpack_from(format, data, offset)
392 sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))
393 index += 1
394
395 def set_constant_buffer(self, shader, index, state):
396 if state is not None:
397 self.real.set_constant_buffer(shader, index, state.buffer)
398
399 self.dump_constant_buffer(state.buffer)
400
401 def set_framebuffer_state(self, state):
402 _state = gallium.Framebuffer()
403 _state.width = state.width
404 _state.height = state.height
405 _state.nr_cbufs = state.nr_cbufs
406 for i in range(len(state.cbufs)):
407 _state.set_cbuf(i, state.cbufs[i])
408 _state.set_zsbuf(state.zsbuf)
409 self.real.set_framebuffer(_state)
410
411 self.cbufs = state.cbufs
412 self.zsbuf = state.zsbuf
413
414 def set_polygon_stipple(self, state):
415 self.real.set_polygon_stipple(state)
416
417 def set_scissor_state(self, state):
418 self.real.set_scissor(state)
419
420 def set_viewport_state(self, state):
421 self.real.set_viewport(state)
422
423 def set_sampler_textures(self, n, textures):
424 for i in range(n):
425 self.real.set_sampler_texture(i, textures[i])
426
427 def set_vertex_buffers(self, n, vbufs):
428 self.vbufs = vbufs[0:n]
429 for i in range(n):
430 vbuf = vbufs[i]
431 self.real.set_vertex_buffer(
432 i,
433 stride = vbuf.stride,
434 max_index = vbuf.max_index,
435 buffer_offset = vbuf.buffer_offset,
436 buffer = vbuf.buffer,
437 )
438
439 def set_vertex_elements(self, n, elements):
440 self.velems = elements[0:n]
441 for i in range(n):
442 self.real.set_vertex_element(i, elements[i])
443 self.real.set_vertex_elements(n)
444
445 def set_edgeflags(self, bitfield):
446 # FIXME
447 pass
448
449 def dump_vertices(self, start, count):
450 if verbose < 2:
451 return
452
453 for index in range(start, start + count):
454 if index >= start + 16:
455 sys.stdout.write('\t...\n')
456 break
457 sys.stdout.write('\t{\n')
458 for velem in self.velems:
459 vbuf = self.vbufs[velem.vertex_buffer_index]
460
461 offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index
462 format = {
463 gallium.PIPE_FORMAT_R32_FLOAT: 'f',
464 gallium.PIPE_FORMAT_R32G32_FLOAT: '2f',
465 gallium.PIPE_FORMAT_R32G32B32_FLOAT: '3f',
466 gallium.PIPE_FORMAT_R32G32B32A32_FLOAT: '4f',
467 gallium.PIPE_FORMAT_B8G8R8A8_UNORM: '4B',
468 }[velem.src_format]
469
470 data = vbuf.buffer.read()
471 values = unpack_from(format, data, offset)
472 sys.stdout.write('\t\t{' + ', '.join(map(str, values)) + '},\n')
473 assert len(values) == velem.nr_components
474 sys.stdout.write('\t},\n')
475
476 def dump_indices(self, ibuf, isize, start, count):
477 if verbose < 2:
478 return
479
480 format = {
481 1: 'B',
482 2: 'H',
483 4: 'I',
484 }[isize]
485
486 assert struct.calcsize(format) == isize
487
488 data = ibuf.read()
489 maxindex, minindex = 0, 0xffffffff
490
491 sys.stdout.write('\t{\n')
492 for i in range(start, start + count):
493 if i >= start + 16:
494 sys.stdout.write('\t...\n')
495 break
496 offset = i*isize
497 index, = unpack_from(format, data, offset)
498 sys.stdout.write('\t\t%u,\n' % index)
499 minindex = min(minindex, index)
500 maxindex = max(maxindex, index)
501 sys.stdout.write('\t},\n')
502
503 return minindex, maxindex
504
505 def draw_arrays(self, mode, start, count):
506 self.dump_vertices(start, count)
507
508 self.real.draw_arrays(mode, start, count)
509
510 self.dirty = True
511
512 def draw_elements(self, indexBuffer, indexSize, mode, start, count):
513 if verbose >= 2:
514 minindex, maxindex = self.dump_indices(indexBuffer, indexSize, start, count)
515 self.dump_vertices(minindex, maxindex - minindex)
516
517 self.real.draw_elements(indexBuffer, indexSize, mode, start, count)
518
519 self.dirty = True
520
521 def draw_range_elements(self, indexBuffer, indexSize, minIndex, maxIndex, mode, start, count):
522 if verbose >= 2:
523 minindex, maxindex = self.dump_indices(indexBuffer, indexSize, start, count)
524 minindex = min(minindex, minIndex)
525 maxindex = min(maxindex, maxIndex)
526 self.dump_vertices(minindex, maxindex - minindex)
527
528 self.real.draw_range_elements(indexBuffer, indexSize, minIndex, maxIndex, mode, start, count)
529
530 self.dirty = True
531
532 def flush(self, flags):
533 self.real.flush(flags)
534 if self.dirty:
535 if flags & gallium.PIPE_FLUSH_FRAME:
536 self._update()
537 self.dirty = False
538 return None
539
540 def clear(self, surface, value):
541 self.real.surface_clear(surface, value)
542
543 def _update(self):
544 self.real.flush()
545
546 if self.cbufs and self.cbufs[0]:
547 if images:
548 global image_no
549 image_no += 1
550 filename = 'cbuf_%04u.png' % image_no
551 save_image(filename, self.cbufs[0])
552 else:
553 show_image(self.cbufs[0])
554
555
556 class Interpreter(parser.TraceDumper):
557
558 ignore_calls = set((
559 ('pipe_screen', 'is_format_supported'),
560 ('pipe_screen', 'get_param'),
561 ('pipe_screen', 'get_paramf'),
562 ))
563
564 def __init__(self, stream):
565 parser.TraceDumper.__init__(self, stream)
566 self.objects = {}
567 self.result = None
568 self.globl = Global(self, None)
569
570 def register_object(self, address, object):
571 self.objects[address] = object
572
573 def unregister_object(self, object):
574 # FIXME:
575 pass
576
577 def lookup_object(self, address):
578 return self.objects[address]
579
580 def interpret(self, trace):
581 for call in trace.calls:
582 self.interpret_call(call)
583
584 def handle_call(self, call):
585
586 if (call.klass, call.method) in self.ignore_calls:
587 return
588
589 if verbose >= 1:
590 parser.TraceDumper.handle_call(self, call)
591
592 args = [self.interpret_arg(arg) for name, arg in call.args]
593
594 if call.klass:
595 obj = args[0]
596 args = args[1:]
597 else:
598 obj = self.globl
599
600 method = getattr(obj, call.method)
601 ret = method(*args)
602
603 if call.ret and isinstance(call.ret, model.Pointer):
604 self.register_object(call.ret.address, ret)
605
606 def interpret_arg(self, node):
607 translator = Translator(self)
608 return translator.visit(node)
609
610
611 if __name__ == '__main__':
612 parser.main(Interpreter)