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 <idr@us.ibm.com>
27
28import gl_XML
29import license
30import sys, getopt, string
31
32
33class 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
47class 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
78class 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
546class glx_function_iterator:
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
567class glx_api(gl_XML.gl_api):
568	def functionIterateGlx(self):
569		return glx_function_iterator(self)
570
571