Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 1 | #!/usr/bin/env python |
2 | |||
3 | # (C) Copyright IBM Corporation 2004, 2005 |
||
4 | # All Rights Reserved. |
||
5 | # |
||
6 | # Permission is hereby granted, free of charge, to any person obtaining a |
||
7 | # copy of this software and associated documentation files (the "Software"), |
||
8 | # to deal in the Software without restriction, including without limitation |
||
9 | # on the rights to use, copy, modify, merge, publish, distribute, sub |
||
10 | # license, and/or sell copies of the Software, and to permit persons to whom |
||
11 | # the Software is furnished to do so, subject to the following conditions: |
||
12 | # |
||
13 | # The above copyright notice and this permission notice (including the next |
||
14 | # paragraph) shall be included in all copies or substantial portions of the |
||
15 | # Software. |
||
16 | # |
||
17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
||
20 | # IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||
22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||
23 | # IN THE SOFTWARE. |
||
24 | # |
||
25 | # Authors: |
||
26 | # Ian Romanick |
||
27 | |||
28 | import gl_XML |
||
29 | import license |
||
30 | import sys, getopt, string |
||
31 | |||
32 | |||
33 | class glx_item_factory(gl_XML.gl_item_factory): |
||
34 | """Factory to create GLX protocol oriented objects derived from gl_item.""" |
||
35 | |||
36 | def create_item(self, name, element, context): |
||
37 | if name == "function": |
||
38 | return glx_function(element, context) |
||
39 | elif name == "enum": |
||
40 | return glx_enum(element, context) |
||
41 | elif name == "api": |
||
42 | return glx_api(self) |
||
43 | else: |
||
44 | return gl_XML.gl_item_factory.create_item(self, name, element, context) |
||
45 | |||
46 | |||
47 | class glx_enum(gl_XML.gl_enum): |
||
48 | def __init__(self, element, context): |
||
49 | gl_XML.gl_enum.__init__(self, element, context) |
||
50 | |||
51 | self.functions = {} |
||
52 | |||
53 | child = element.children |
||
54 | while child: |
||
55 | if child.type == "element" and child.name == "size": |
||
56 | n = child.nsProp( "name", None ) |
||
57 | c = child.nsProp( "count", None ) |
||
58 | m = child.nsProp( "mode", None ) |
||
59 | |||
60 | if not c: |
||
61 | c = self.default_count |
||
62 | else: |
||
63 | c = int(c) |
||
64 | |||
65 | if m == "get": |
||
66 | mode = 0 |
||
67 | else: |
||
68 | mode = 1 |
||
69 | |||
70 | if not self.functions.has_key(n): |
||
71 | self.functions[ n ] = [c, mode] |
||
72 | |||
73 | child = child.next |
||
74 | |||
75 | return |
||
76 | |||
77 | |||
78 | class glx_function(gl_XML.gl_function): |
||
79 | def __init__(self, element, context): |
||
80 | self.glx_rop = 0 |
||
81 | self.glx_sop = 0 |
||
82 | self.glx_vendorpriv = 0 |
||
83 | |||
84 | self.glx_vendorpriv_names = [] |
||
85 | |||
86 | # If this is set to true, it means that GLdouble parameters should be |
||
87 | # written to the GLX protocol packet in the order they appear in the |
||
88 | # prototype. This is different from the "classic" ordering. In the |
||
89 | # classic ordering GLdoubles are written to the protocol packet first, |
||
90 | # followed by non-doubles. NV_vertex_program was the first extension |
||
91 | # to break with this tradition. |
||
92 | |||
93 | self.glx_doubles_in_order = 0 |
||
94 | |||
95 | self.vectorequiv = None |
||
96 | self.output = None |
||
97 | self.can_be_large = 0 |
||
98 | self.reply_always_array = 0 |
||
99 | self.dimensions_in_reply = 0 |
||
100 | self.img_reset = None |
||
101 | |||
102 | self.server_handcode = 0 |
||
103 | self.client_handcode = 0 |
||
104 | self.ignore = 0 |
||
105 | |||
106 | self.count_parameter_list = [] |
||
107 | self.counter_list = [] |
||
108 | self.parameters_by_name = {} |
||
109 | self.offsets_calculated = 0 |
||
110 | |||
111 | gl_XML.gl_function.__init__(self, element, context) |
||
112 | return |
||
113 | |||
114 | |||
115 | def process_element(self, element): |
||
116 | gl_XML.gl_function.process_element(self, element) |
||
117 | |||
118 | # If the function already has a vector equivalent set, don't |
||
119 | # set it again. This can happen if an alias to a function |
||
120 | # appears after the function that it aliases. |
||
121 | |||
122 | if not self.vectorequiv: |
||
123 | self.vectorequiv = element.nsProp("vectorequiv", None) |
||
124 | |||
125 | |||
126 | name = element.nsProp("name", None) |
||
127 | if name == self.name: |
||
128 | for param in self.parameters: |
||
129 | self.parameters_by_name[ param.name ] = param |
||
130 | |||
131 | if len(param.count_parameter_list): |
||
132 | self.count_parameter_list.extend( param.count_parameter_list ) |
||
133 | |||
134 | if param.counter and param.counter not in self.counter_list: |
||
135 | self.counter_list.append(param.counter) |
||
136 | |||
137 | |||
138 | child = element.children |
||
139 | while child: |
||
140 | if child.type == "element" and child.name == "glx": |
||
141 | rop = child.nsProp( 'rop', None ) |
||
142 | sop = child.nsProp( 'sop', None ) |
||
143 | vop = child.nsProp( 'vendorpriv', None ) |
||
144 | |||
145 | if rop: |
||
146 | self.glx_rop = int(rop) |
||
147 | |||
148 | if sop: |
||
149 | self.glx_sop = int(sop) |
||
150 | |||
151 | if vop: |
||
152 | self.glx_vendorpriv = int(vop) |
||
153 | self.glx_vendorpriv_names.append(name) |
||
154 | |||
155 | self.img_reset = child.nsProp( 'img_reset', None ) |
||
156 | |||
157 | # The 'handcode' attribute can be one of 'true', |
||
158 | # 'false', 'client', or 'server'. |
||
159 | |||
160 | handcode = child.nsProp( 'handcode', None ) |
||
161 | if handcode == "false": |
||
162 | self.server_handcode = 0 |
||
163 | self.client_handcode = 0 |
||
164 | elif handcode == "true": |
||
165 | self.server_handcode = 1 |
||
166 | self.client_handcode = 1 |
||
167 | elif handcode == "client": |
||
168 | self.server_handcode = 0 |
||
169 | self.client_handcode = 1 |
||
170 | elif handcode == "server": |
||
171 | self.server_handcode = 1 |
||
172 | self.client_handcode = 0 |
||
173 | else: |
||
174 | raise RuntimeError('Invalid handcode mode "%s" in function "%s".' % (handcode, self.name)) |
||
175 | |||
176 | self.ignore = gl_XML.is_attr_true( child, 'ignore' ) |
||
177 | self.can_be_large = gl_XML.is_attr_true( child, 'large' ) |
||
178 | self.glx_doubles_in_order = gl_XML.is_attr_true( child, 'doubles_in_order' ) |
||
179 | self.reply_always_array = gl_XML.is_attr_true( child, 'always_array' ) |
||
180 | self.dimensions_in_reply = gl_XML.is_attr_true( child, 'dimensions_in_reply' ) |
||
181 | |||
182 | child = child.next |
||
183 | |||
184 | |||
185 | # Do some validation of the GLX protocol information. As |
||
186 | # new tests are discovered, they should be added here. |
||
187 | |||
188 | for param in self.parameters: |
||
189 | if param.is_output and self.glx_rop != 0: |
||
190 | raise RuntimeError("Render / RenderLarge commands cannot have outputs (%s)." % (self.name)) |
||
191 | |||
192 | return |
||
193 | |||
194 | |||
195 | def has_variable_size_request(self): |
||
196 | """Determine if the GLX request packet is variable sized. |
||
197 | |||
198 | The GLX request packet is variable sized in several common |
||
199 | situations. |
||
200 | |||
201 | 1. The function has a non-output parameter that is counted |
||
202 | by another parameter (e.g., the 'textures' parameter of |
||
203 | glDeleteTextures). |
||
204 | |||
205 | 2. The function has a non-output parameter whose count is |
||
206 | determined by another parameter that is an enum (e.g., the |
||
207 | 'params' parameter of glLightfv). |
||
208 | |||
209 | 3. The function has a non-output parameter that is an |
||
210 | image. |
||
211 | |||
212 | 4. The function must be hand-coded on the server. |
||
213 | """ |
||
214 | |||
215 | if self.glx_rop == 0: |
||
216 | return 0 |
||
217 | |||
218 | if self.server_handcode or self.images: |
||
219 | return 1 |
||
220 | |||
221 | for param in self.parameters: |
||
222 | if not param.is_output: |
||
223 | if param.counter or len(param.count_parameter_list): |
||
224 | return 1 |
||
225 | |||
226 | return 0 |
||
227 | |||
228 | |||
229 | def variable_length_parameter(self): |
||
230 | for param in self.parameters: |
||
231 | if not param.is_output: |
||
232 | if param.counter or len(param.count_parameter_list): |
||
233 | return param |
||
234 | |||
235 | return None |
||
236 | |||
237 | |||
238 | def calculate_offsets(self): |
||
239 | if not self.offsets_calculated: |
||
240 | # Calculate the offset of the first function parameter |
||
241 | # in the GLX command packet. This byte offset is |
||
242 | # measured from the end of the Render / RenderLarge |
||
243 | # header. The offset for all non-pixel commends is |
||
244 | # zero. The offset for pixel commands depends on the |
||
245 | # number of dimensions of the pixel data. |
||
246 | |||
247 | if len(self.images) and not self.images[0].is_output: |
||
248 | [dim, junk, junk, junk, junk] = self.images[0].get_dimensions() |
||
249 | |||
250 | # The base size is the size of the pixel pack info |
||
251 | # header used by images with the specified number |
||
252 | # of dimensions. |
||
253 | |||
254 | if dim <= 2: |
||
255 | offset = 20 |
||
256 | elif dim <= 4: |
||
257 | offset = 36 |
||
258 | else: |
||
259 | raise RuntimeError('Invalid number of dimensions %u for parameter "%s" in function "%s".' % (dim, self.image.name, self.name)) |
||
260 | else: |
||
261 | offset = 0 |
||
262 | |||
263 | for param in self.parameterIterateGlxSend(): |
||
264 | if param.img_null_flag: |
||
265 | offset += 4 |
||
266 | |||
267 | if param.name != self.img_reset: |
||
268 | param.offset = offset |
||
269 | if not param.is_variable_length() and not param.is_client_only: |
||
270 | offset += param.size() |
||
271 | |||
272 | if self.pad_after( param ): |
||
273 | offset += 4 |
||
274 | |||
275 | |||
276 | self.offsets_calculated = 1 |
||
277 | return |
||
278 | |||
279 | |||
280 | def offset_of(self, param_name): |
||
281 | self.calculate_offsets() |
||
282 | return self.parameters_by_name[ param_name ].offset |
||
283 | |||
284 | |||
285 | def parameterIterateGlxSend(self, include_variable_parameters = 1): |
||
286 | """Create an iterator for parameters in GLX request order.""" |
||
287 | |||
288 | # The parameter lists are usually quite short, so it's easier |
||
289 | # (i.e., less code) to just generate a new list with the |
||
290 | # required elements than it is to create a new iterator class. |
||
291 | |||
292 | temp = [ [], [], [] ] |
||
293 | for param in self.parameters: |
||
294 | if param.is_output: continue |
||
295 | |||
296 | if param.is_variable_length(): |
||
297 | temp[2].append( param ) |
||
298 | elif not self.glx_doubles_in_order and param.is_64_bit(): |
||
299 | temp[0].append( param ) |
||
300 | else: |
||
301 | temp[1].append( param ) |
||
302 | |||
303 | parameters = temp[0] |
||
304 | parameters.extend( temp[1] ) |
||
305 | if include_variable_parameters: |
||
306 | parameters.extend( temp[2] ) |
||
307 | return parameters.__iter__() |
||
308 | |||
309 | |||
310 | def parameterIterateCounters(self): |
||
311 | temp = [] |
||
312 | for name in self.counter_list: |
||
313 | temp.append( self.parameters_by_name[ name ] ) |
||
314 | |||
315 | return temp.__iter__() |
||
316 | |||
317 | |||
318 | def parameterIterateOutputs(self): |
||
319 | temp = [] |
||
320 | for p in self.parameters: |
||
321 | if p.is_output: |
||
322 | temp.append( p ) |
||
323 | |||
324 | return temp |
||
325 | |||
326 | |||
327 | def command_fixed_length(self): |
||
328 | """Return the length, in bytes as an integer, of the |
||
329 | fixed-size portion of the command.""" |
||
330 | |||
331 | if len(self.parameters) == 0: |
||
332 | return 0 |
||
333 | |||
334 | self.calculate_offsets() |
||
335 | |||
336 | size = 0 |
||
337 | for param in self.parameterIterateGlxSend(0): |
||
338 | if param.name != self.img_reset and not param.is_client_only: |
||
339 | if size == 0: |
||
340 | size = param.offset + param.size() |
||
341 | else: |
||
342 | size += param.size() |
||
343 | |||
344 | if self.pad_after( param ): |
||
345 | size += 4 |
||
346 | |||
347 | for param in self.images: |
||
348 | if param.img_null_flag or param.is_output: |
||
349 | size += 4 |
||
350 | |||
351 | return size |
||
352 | |||
353 | |||
354 | def command_variable_length(self): |
||
355 | """Return the length, as a string, of the variable-sized |
||
356 | portion of the command.""" |
||
357 | |||
358 | size_string = "" |
||
359 | for p in self.parameterIterateGlxSend(): |
||
360 | if (not p.is_output) and (p.is_variable_length() or p.is_image()): |
||
361 | # FIXME Replace the 1 in the size_string call |
||
362 | # FIXME w/0 to eliminate some un-needed parnes |
||
363 | # FIXME This would already be done, but it |
||
364 | # FIXME adds some extra diffs to the generated |
||
365 | # FIXME code. |
||
366 | |||
367 | size_string = size_string + " + __GLX_PAD(%s)" % (p.size_string(1)) |
||
368 | |||
369 | return size_string |
||
370 | |||
371 | |||
372 | def command_length(self): |
||
373 | size = self.command_fixed_length() |
||
374 | |||
375 | if self.glx_rop != 0: |
||
376 | size += 4 |
||
377 | |||
378 | size = ((size + 3) & ~3) |
||
379 | return "%u%s" % (size, self.command_variable_length()) |
||
380 | |||
381 | |||
382 | def opcode_real_value(self): |
||
383 | """Get the true numeric value of the GLX opcode |
||
384 | |||
385 | Behaves similarly to opcode_value, except for |
||
386 | X_GLXVendorPrivate and X_GLXVendorPrivateWithReply commands. |
||
387 | In these cases the value for the GLX opcode field (i.e., |
||
388 | 16 for X_GLXVendorPrivate or 17 for |
||
389 | X_GLXVendorPrivateWithReply) is returned. For other 'single' |
||
390 | commands, the opcode for the command (e.g., 101 for |
||
391 | X_GLsop_NewList) is returned.""" |
||
392 | |||
393 | if self.glx_vendorpriv != 0: |
||
394 | if self.needs_reply(): |
||
395 | return 17 |
||
396 | else: |
||
397 | return 16 |
||
398 | else: |
||
399 | return self.opcode_value() |
||
400 | |||
401 | |||
402 | def opcode_value(self): |
||
403 | """Get the unique protocol opcode for the glXFunction""" |
||
404 | |||
405 | if (self.glx_rop == 0) and self.vectorequiv: |
||
406 | equiv = self.context.functions_by_name[ self.vectorequiv ] |
||
407 | self.glx_rop = equiv.glx_rop |
||
408 | |||
409 | |||
410 | if self.glx_rop != 0: |
||
411 | return self.glx_rop |
||
412 | elif self.glx_sop != 0: |
||
413 | return self.glx_sop |
||
414 | elif self.glx_vendorpriv != 0: |
||
415 | return self.glx_vendorpriv |
||
416 | else: |
||
417 | return -1 |
||
418 | |||
419 | |||
420 | def opcode_rop_basename(self): |
||
421 | """Return either the name to be used for GLX protocol enum. |
||
422 | |||
423 | Returns either the name of the function or the name of the |
||
424 | name of the equivalent vector (e.g., glVertex3fv for |
||
425 | glVertex3f) function.""" |
||
426 | |||
427 | if self.vectorequiv == None: |
||
428 | return self.name |
||
429 | else: |
||
430 | return self.vectorequiv |
||
431 | |||
432 | |||
433 | def opcode_name(self): |
||
434 | """Get the unique protocol enum name for the glXFunction""" |
||
435 | |||
436 | if (self.glx_rop == 0) and self.vectorequiv: |
||
437 | equiv = self.context.functions_by_name[ self.vectorequiv ] |
||
438 | self.glx_rop = equiv.glx_rop |
||
439 | self.glx_doubles_in_order = equiv.glx_doubles_in_order |
||
440 | |||
441 | |||
442 | if self.glx_rop != 0: |
||
443 | return "X_GLrop_%s" % (self.opcode_rop_basename()) |
||
444 | elif self.glx_sop != 0: |
||
445 | return "X_GLsop_%s" % (self.name) |
||
446 | elif self.glx_vendorpriv != 0: |
||
447 | return "X_GLvop_%s" % (self.name) |
||
448 | else: |
||
449 | raise RuntimeError('Function "%s" has no opcode.' % (self.name)) |
||
450 | |||
451 | |||
452 | def opcode_vendor_name(self, name): |
||
453 | if name in self.glx_vendorpriv_names: |
||
454 | return "X_GLvop_%s" % (name) |
||
455 | else: |
||
456 | raise RuntimeError('Function "%s" has no VendorPrivate opcode.' % (name)) |
||
457 | |||
458 | |||
459 | def opcode_real_name(self): |
||
460 | """Get the true protocol enum name for the GLX opcode |
||
461 | |||
462 | Behaves similarly to opcode_name, except for |
||
463 | X_GLXVendorPrivate and X_GLXVendorPrivateWithReply commands. |
||
464 | In these cases the string 'X_GLXVendorPrivate' or |
||
465 | 'X_GLXVendorPrivateWithReply' is returned. For other |
||
466 | single or render commands 'X_GLsop' or 'X_GLrop' plus the |
||
467 | name of the function returned.""" |
||
468 | |||
469 | if self.glx_vendorpriv != 0: |
||
470 | if self.needs_reply(): |
||
471 | return "X_GLXVendorPrivateWithReply" |
||
472 | else: |
||
473 | return "X_GLXVendorPrivate" |
||
474 | else: |
||
475 | return self.opcode_name() |
||
476 | |||
477 | |||
478 | def needs_reply(self): |
||
479 | try: |
||
480 | x = self._needs_reply |
||
481 | except Exception, e: |
||
482 | x = 0 |
||
483 | if self.return_type != 'void': |
||
484 | x = 1 |
||
485 | |||
486 | for param in self.parameters: |
||
487 | if param.is_output: |
||
488 | x = 1 |
||
489 | break |
||
490 | |||
491 | self._needs_reply = x |
||
492 | |||
493 | return x |
||
494 | |||
495 | |||
496 | def pad_after(self, p): |
||
497 | """Returns the name of the field inserted after the |
||
498 | specified field to pad out the command header.""" |
||
499 | |||
500 | for image in self.images: |
||
501 | if image.img_pad_dimensions: |
||
502 | if not image.height: |
||
503 | if p.name == image.width: |
||
504 | return "height" |
||
505 | elif p.name == image.img_xoff: |
||
506 | return "yoffset" |
||
507 | elif not image.extent: |
||
508 | if p.name == image.depth: |
||
509 | # Should this be "size4d"? |
||
510 | return "extent" |
||
511 | elif p.name == image.img_zoff: |
||
512 | return "woffset" |
||
513 | |||
514 | return None |
||
515 | |||
516 | |||
517 | def has_different_protocol(self, name): |
||
518 | """Returns true if the named version of the function uses different protocol from the other versions. |
||
519 | |||
520 | Some functions, such as glDeleteTextures and |
||
521 | glDeleteTexturesEXT are functionally identical, but have |
||
522 | different protocol. This function returns true if the |
||
523 | named function is an alias name and that named version uses |
||
524 | different protocol from the function that is aliased. |
||
525 | """ |
||
526 | |||
527 | return (name in self.glx_vendorpriv_names) and self.glx_sop |
||
528 | |||
529 | |||
530 | def static_glx_name(self, name): |
||
531 | if self.has_different_protocol(name): |
||
532 | for n in self.glx_vendorpriv_names: |
||
533 | if n in self.static_entry_points: |
||
534 | return n |
||
535 | |||
536 | return self.static_name(name) |
||
537 | |||
538 | |||
539 | def client_supported_for_indirect(self): |
||
540 | """Returns true if the function is supported on the client |
||
541 | side for indirect rendering.""" |
||
542 | |||
543 | return not self.ignore and (self.offset != -1) and (self.glx_rop or self.glx_sop or self.glx_vendorpriv or self.vectorequiv or self.client_handcode) |
||
544 | |||
545 | |||
546 | class glx_function_iterator(object): |
||
547 | """Class to iterate over a list of glXFunctions""" |
||
548 | |||
549 | def __init__(self, context): |
||
550 | self.iterator = context.functionIterateByOffset() |
||
551 | return |
||
552 | |||
553 | |||
554 | def __iter__(self): |
||
555 | return self |
||
556 | |||
557 | |||
558 | def next(self): |
||
559 | f = self.iterator.next() |
||
560 | |||
561 | if f.client_supported_for_indirect(): |
||
562 | return f |
||
563 | else: |
||
564 | return self.next() |
||
565 | |||
566 | |||
567 | class glx_api(gl_XML.gl_api): |
||
568 | def functionIterateGlx(self): |
||
569 | return glx_function_iterator(self)=>=> |
||
570 |