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