glX_proto_size.py revision 0c6b9303227977723a6d03664f71e5eb56ebf139
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, glX_XML
29import license
30import sys, getopt, copy, string
31
32
33class glx_enum_function:
34	def __init__(self, func_name, enum_dict):
35		self.name = func_name
36		self.mode = 1
37		self.sig = None
38
39		# "enums" is a set of lists.  The element in the set is the
40		# value of the enum.  The list is the list of names for that
41		# value.  For example, [0x8126] = {"POINT_SIZE_MIN",
42		# "POINT_SIZE_MIN_ARB", "POINT_SIZE_MIN_EXT",
43		# "POINT_SIZE_MIN_SGIS"}.
44
45		self.enums = {}
46
47		# "count" is indexed by count values.  Each element of count
48		# is a list of index to "enums" that have that number of
49		# associated data elements.  For example, [4] =
50		# {GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_EMISSION,
51		# GL_AMBIENT_AND_DIFFUSE} (the enum names are used here,
52		# but the actual hexadecimal values would be in the array).
53
54		self.count = {}
55
56
57		# Fill self.count and self.enums using the dictionary of enums
58		# that was passed in.  The generic Get functions (e.g.,
59		# GetBooleanv and friends) are handled specially here.  In
60		# the data the generic Get functions are refered to as "Get".
61
62		if func_name in ["GetIntegerv", "GetBooleanv", "GetFloatv", "GetDoublev"]:
63			match_name = "Get"
64		else:
65			match_name = func_name
66
67		mode_set = 0
68		for enum_name in enum_dict:
69			e = enum_dict[ enum_name ]
70
71			if e.functions.has_key( match_name ):
72				[count, mode] = e.functions[ match_name ]
73
74				if mode_set and mode != self.mode:
75					raise RuntimeError("Not all enums for %s have the same mode." % (func_name))
76
77				self.mode = mode
78
79				if self.enums.has_key( e.value ):
80					if e.name not in self.enums[ e.value ]:
81						self.enums[ e.value ].append( e )
82				else:
83					if not self.count.has_key( count ):
84						self.count[ count ] = []
85
86					self.enums[ e.value ] = [ e ]
87					self.count[ count ].append( e.value )
88
89
90		return
91
92
93	def signature( self ):
94		if self.sig == None:
95			self.sig = ""
96			for i in self.count:
97				if i == None:
98					raise RuntimeError("i is None.  WTF?")
99
100				self.count[i].sort()
101				for e in self.count[i]:
102					self.sig += "%04x,%d," % (e, i)
103
104		return self.sig
105
106
107	def is_set( self ):
108		return self.mode
109
110
111	def PrintUsingTable(self):
112		"""Emit the body of the __gl*_size function using a pair
113		of look-up tables and a mask.  The mask is calculated such
114		that (e & mask) is unique for all the valid values of e for
115		this function.  The result of (e & mask) is used as an index
116		into the first look-up table.  If it matches e, then the
117		same entry of the second table is returned.  Otherwise zero
118		is returned.
119
120		It seems like this should cause better code to be generated.
121		However, on x86 at least, the resulting .o file is about 20%
122		larger then the switch-statment version.  I am leaving this
123		code in because the results may be different on other
124		platforms (e.g., PowerPC or x86-64)."""
125
126		return 0
127		count = 0
128		for a in self.enums:
129			count += 1
130
131		if self.count.has_key(-1):
132			return 0
133
134		# Determine if there is some mask M, such that M = (2^N) - 1,
135		# that will generate unique values for all of the enums.
136
137		mask = 0
138		for i in [1, 2, 3, 4, 5, 6, 7, 8]:
139			mask = (1 << i) - 1
140
141			fail = 0;
142			for a in self.enums:
143				for b in self.enums:
144					if a != b:
145						if (a & mask) == (b & mask):
146							fail = 1;
147
148			if not fail:
149				break;
150			else:
151				mask = 0
152
153		if (mask != 0) and (mask < (2 * count)):
154			masked_enums = {}
155			masked_count = {}
156
157			for i in range(0, mask + 1):
158				masked_enums[i] = "0";
159				masked_count[i] = 0;
160
161			for c in self.count:
162				for e in self.count[c]:
163					i = e & mask
164					enum_obj = self.enums[e][0]
165					masked_enums[i] = '0x%04x /* %s */' % (e, enum_obj.name )
166					masked_count[i] = c
167
168
169			print '    static const GLushort a[%u] = {' % (mask + 1)
170			for e in masked_enums:
171				print '        %s, ' % (masked_enums[e])
172			print '    };'
173
174			print '    static const GLubyte b[%u] = {' % (mask + 1)
175			for c in masked_count:
176				print '        %u, ' % (masked_count[c])
177			print '    };'
178
179			print '    const unsigned idx = (e & 0x%02xU);' % (mask)
180			print ''
181			print '    return (e == a[idx]) ? (GLint) b[idx] : 0;'
182			return 1;
183		else:
184			return 0;
185
186
187	def PrintUsingSwitch(self, name):
188		"""Emit the body of the __gl*_size function using a
189		switch-statement."""
190
191		print '    switch( e ) {'
192
193		for c in self.count:
194			for e in self.count[c]:
195				first = 1
196
197				# There may be multiple enums with the same
198				# value.  This happens has extensions are
199				# promoted from vendor-specific or EXT to
200				# ARB and to the core.  Emit the first one as
201				# a case label, and emit the others as
202				# commented-out case labels.
203
204				list = {}
205				for enum_obj in self.enums[e]:
206					list[ enum_obj.priority() ] = enum_obj.name
207
208				keys = list.keys()
209				keys.sort()
210				for k in keys:
211					j = list[k]
212					if first:
213						print '        case GL_%s:' % (j)
214						first = 0
215					else:
216						print '/*      case GL_%s:*/' % (j)
217
218			if c == -1:
219				print '            return __gl%s_variable_size( e );' % (name)
220			else:
221				print '            return %u;' % (c)
222
223		print '        default: return 0;'
224		print '    }'
225
226
227	def Print(self, name):
228		print '_X_INTERNAL PURE FASTCALL GLint'
229		print '__gl%s_size( GLenum e )' % (name)
230		print '{'
231
232		if not self.PrintUsingTable():
233			self.PrintUsingSwitch(name)
234
235		print '}'
236		print ''
237
238
239class glx_server_enum_function(glx_enum_function):
240	def __init__(self, func, enum_dict):
241		glx_enum_function.__init__(self, func.name, enum_dict)
242
243		self.function = func
244		return
245
246
247	def signature( self ):
248		if self.sig == None:
249			sig = glx_enum_function.signature(self)
250
251			p = self.function.variable_length_parameter()
252			if p:
253				sig += "%u" % (p.size())
254
255			self.sig = sig
256
257		return self.sig;
258
259
260	def Print(self, name, printer):
261		f = self.function
262		printer.common_func_print_just_header( f )
263
264		fixup = []
265
266		foo = {}
267		for param_name in f.count_parameter_list:
268			o = f.offset_of( param_name )
269			foo[o] = param_name
270
271		for param_name in f.counter_list:
272			o = f.offset_of( param_name )
273			foo[o] = param_name
274
275		keys = foo.keys()
276		keys.sort()
277		for o in keys:
278			p = f.parameters_by_name[ foo[o] ]
279
280			printer.common_emit_one_arg(p, "pc", 0)
281			fixup.append( p.name )
282
283
284		print '    GLsizei compsize;'
285		print ''
286
287		printer.common_emit_fixups(fixup)
288
289		print ''
290		print '    compsize = __gl%s_size(%s);' % (f.name, string.join(f.count_parameter_list, ","))
291		p = f.variable_length_parameter()
292		print '    return __GLX_PAD(%s);' % (p.size_string())
293
294		print '}'
295		print ''
296
297
298class PrintGlxSizeStubs_common(gl_XML.gl_print_base):
299	do_get = (1 << 0)
300	do_set = (1 << 1)
301
302	def __init__(self, which_functions):
303		gl_XML.gl_print_base.__init__(self)
304
305		self.name = "glX_proto_size.py (from Mesa)"
306		self.license = license.bsd_license_template % ( "(C) Copyright IBM Corporation 2004", "IBM")
307
308		self.emit_set = ((which_functions & PrintGlxSizeStubs_common.do_set) != 0)
309		self.emit_get = ((which_functions & PrintGlxSizeStubs_common.do_get) != 0)
310		return
311
312
313class PrintGlxSizeStubs_c(PrintGlxSizeStubs_common):
314	def printRealHeader(self):
315		print ''
316		print '#include <X11/Xfuncproto.h>'
317		print '#include <GL/gl.h>'
318		if self.emit_get:
319			print '#include "indirect_size_get.h"'
320			print '#include "glxserver.h"'
321			print '#include "indirect_util.h"'
322
323		print '#include "indirect_size.h"'
324
325		print ''
326		self.printPure()
327		print ''
328		self.printFastcall()
329		print ''
330		print ''
331		print '#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(GLX_USE_APPLEGL)'
332		print '#  undef HAVE_ALIAS'
333		print '#endif'
334		print '#ifdef HAVE_ALIAS'
335		print '#  define ALIAS2(from,to) \\'
336		print '    _X_INTERNAL PURE FASTCALL GLint __gl ## from ## _size( GLenum e ) \\'
337		print '        __attribute__ ((alias( # to )));'
338		print '#  define ALIAS(from,to) ALIAS2( from, __gl ## to ## _size )'
339		print '#else'
340		print '#  define ALIAS(from,to) \\'
341		print '    _X_INTERNAL PURE FASTCALL GLint __gl ## from ## _size( GLenum e ) \\'
342		print '    { return __gl ## to ## _size( e ); }'
343		print '#endif'
344		print ''
345		print ''
346
347
348	def printBody(self, api):
349		enum_sigs = {}
350		aliases = []
351
352		for func in api.functionIterateGlx():
353			ef = glx_enum_function( func.name, api.enums_by_name )
354			if len(ef.enums) == 0:
355				continue
356
357			if (ef.is_set() and self.emit_set) or (not ef.is_set() and self.emit_get):
358				sig = ef.signature()
359				if enum_sigs.has_key( sig ):
360					aliases.append( [func.name, enum_sigs[ sig ]] )
361				else:
362					enum_sigs[ sig ] = func.name
363					ef.Print( func.name )
364
365
366		for [alias_name, real_name] in aliases:
367			print 'ALIAS( %s, %s )' % (alias_name, real_name)
368
369
370
371class PrintGlxSizeStubs_h(PrintGlxSizeStubs_common):
372	def printRealHeader(self):
373		print """/**
374 * \\file
375 * Prototypes for functions used to determine the number of data elements in
376 * various GLX protocol messages.
377 *
378 * \\author Ian Romanick <idr@us.ibm.com>
379 */
380"""
381		print '#include <X11/Xfuncproto.h>'
382		print ''
383		self.printPure();
384		print ''
385		self.printFastcall();
386		print ''
387
388
389	def printBody(self, api):
390		for func in api.functionIterateGlx():
391			ef = glx_enum_function( func.name, api.enums_by_name )
392			if len(ef.enums) == 0:
393				continue
394
395			if (ef.is_set() and self.emit_set) or (not ef.is_set() and self.emit_get):
396				print 'extern _X_INTERNAL PURE FASTCALL GLint __gl%s_size(GLenum);' % (func.name)
397
398
399class PrintGlxReqSize_common(gl_XML.gl_print_base):
400	"""Common base class for PrintGlxSizeReq_h and PrintGlxSizeReq_h.
401
402	The main purpose of this common base class is to provide the infrastructure
403	for the derrived classes to iterate over the same set of functions.
404	"""
405
406	def __init__(self):
407		gl_XML.gl_print_base.__init__(self)
408
409		self.name = "glX_proto_size.py (from Mesa)"
410		self.license = license.bsd_license_template % ( "(C) Copyright IBM Corporation 2005", "IBM")
411
412
413class PrintGlxReqSize_h(PrintGlxReqSize_common):
414	def __init__(self):
415		PrintGlxReqSize_common.__init__(self)
416		self.header_tag = "_INDIRECT_REQSIZE_H_"
417
418
419	def printRealHeader(self):
420		print '#include <X11/Xfuncproto.h>'
421		print ''
422		self.printPure()
423		print ''
424
425
426	def printBody(self, api):
427		for func in api.functionIterateGlx():
428			if not func.ignore and func.has_variable_size_request():
429				print 'extern PURE _X_HIDDEN int __glX%sReqSize(const GLbyte *pc, Bool swap);' % (func.name)
430
431
432class PrintGlxReqSize_c(PrintGlxReqSize_common):
433	"""Create the server-side 'request size' functions.
434
435	Create the server-side functions that are used to determine what the
436	size of a varible length command should be.  The server then uses
437	this value to determine if the incoming command packed it malformed.
438	"""
439
440	def __init__(self):
441		PrintGlxReqSize_common.__init__(self)
442		self.counter_sigs = {}
443
444
445	def printRealHeader(self):
446		print ''
447		print '#include <GL/gl.h>'
448		print '#include "glxserver.h"'
449		print '#include "glxbyteorder.h"'
450		print '#include "indirect_size.h"'
451		print '#include "indirect_reqsize.h"'
452		print ''
453		print '#define __GLX_PAD(x)  (((x) + 3) & ~3)'
454		print ''
455		print '#if defined(__CYGWIN__) || defined(__MINGW32__)'
456		print '#  undef HAVE_ALIAS'
457		print '#endif'
458		print '#ifdef HAVE_ALIAS'
459		print '#  define ALIAS2(from,to) \\'
460		print '    GLint __glX ## from ## ReqSize( const GLbyte * pc, Bool swap ) \\'
461		print '        __attribute__ ((alias( # to )));'
462		print '#  define ALIAS(from,to) ALIAS2( from, __glX ## to ## ReqSize )'
463		print '#else'
464		print '#  define ALIAS(from,to) \\'
465		print '    GLint __glX ## from ## ReqSize( const GLbyte * pc, Bool swap ) \\'
466		print '    { return __glX ## to ## ReqSize( pc, swap ); }'
467		print '#endif'
468		print ''
469		print ''
470
471
472	def printBody(self, api):
473		aliases = []
474		enum_functions = {}
475		enum_sigs = {}
476
477		for func in api.functionIterateGlx():
478			if not func.has_variable_size_request(): continue
479
480			ef = glx_server_enum_function( func, api.enums_by_name )
481			if len(ef.enums) == 0: continue
482
483			sig = ef.signature()
484
485			if not enum_functions.has_key(func.name):
486				enum_functions[ func.name ] = sig
487
488			if not enum_sigs.has_key( sig ):
489				enum_sigs[ sig ] = ef
490
491
492
493		for func in api.functionIterateGlx():
494			# Even though server-handcode fuctions are on "the
495			# list", and prototypes are generated for them, there
496			# isn't enough information to generate a size
497			# function.  If there was enough information, they
498			# probably wouldn't need to be handcoded in the first
499			# place!
500
501			if func.server_handcode: continue
502			if not func.has_variable_size_request(): continue
503
504			if enum_functions.has_key(func.name):
505				sig = enum_functions[func.name]
506				ef = enum_sigs[ sig ]
507
508				if ef.name != func.name:
509					aliases.append( [func.name, ef.name] )
510				else:
511					ef.Print( func.name, self )
512
513			elif func.images:
514				self.printPixelFunction(func)
515			elif func.has_variable_size_request():
516				a = self.printCountedFunction(func)
517				if a: aliases.append(a)
518
519
520		for [alias_name, real_name] in aliases:
521			print 'ALIAS( %s, %s )' % (alias_name, real_name)
522
523		return
524
525
526	def common_emit_fixups(self, fixup):
527		"""Utility function to emit conditional byte-swaps."""
528
529		if fixup:
530			print '    if (swap) {'
531			for name in fixup:
532				print '        %s = bswap_32(%s);' % (name, name)
533			print '    }'
534
535		return
536
537
538	def common_emit_one_arg(self, p, pc, adjust):
539		offset = p.offset
540		dst = p.string()
541		src = '(%s *)' % (p.type_string())
542		print '%-18s = *%11s(%s + %u);' % (dst, src, pc, offset + adjust);
543		return
544
545
546	def common_func_print_just_header(self, f):
547		print 'int'
548		print '__glX%sReqSize( const GLbyte * pc, Bool swap )' % (f.name)
549		print '{'
550
551
552	def printPixelFunction(self, f):
553		self.common_func_print_just_header(f)
554
555		f.offset_of( f.parameters[0].name )
556		[dim, w, h, d, junk] = f.get_images()[0].get_dimensions()
557
558		print '    GLint row_length   = *  (GLint *)(pc +  4);'
559
560		if dim < 3:
561			fixup = ['row_length', 'skip_rows', 'alignment']
562			print '    GLint image_height = 0;'
563			print '    GLint skip_images  = 0;'
564			print '    GLint skip_rows    = *  (GLint *)(pc +  8);'
565			print '    GLint alignment    = *  (GLint *)(pc + 16);'
566		else:
567			fixup = ['row_length', 'image_height', 'skip_rows', 'skip_images', 'alignment']
568			print '    GLint image_height = *  (GLint *)(pc +  8);'
569			print '    GLint skip_rows    = *  (GLint *)(pc + 16);'
570			print '    GLint skip_images  = *  (GLint *)(pc + 20);'
571			print '    GLint alignment    = *  (GLint *)(pc + 32);'
572
573		img = f.images[0]
574		for p in f.parameterIterateGlxSend():
575			if p.name in [w, h, d, img.img_format, img.img_type, img.img_target]:
576				self.common_emit_one_arg(p, "pc", 0)
577				fixup.append( p.name )
578
579		print ''
580
581		self.common_emit_fixups(fixup)
582
583		if img.img_null_flag:
584			print ''
585			print '	   if (*(CARD32 *) (pc + %s))' % (img.offset - 4)
586			print '	       return 0;'
587
588		print ''
589		print '    return __glXImageSize(%s, %s, %s, %s, %s, %s,' % (img.img_format, img.img_type, img.img_target, w, h, d )
590		print '                          image_height, row_length, skip_images,'
591		print '                          skip_rows, alignment);'
592		print '}'
593		print ''
594		return
595
596
597	def printCountedFunction(self, f):
598
599		sig = ""
600		offset = 0
601		fixup = []
602		params = []
603		plus = ''
604		size = ''
605		param_offsets = {}
606
607		# Calculate the offset of each counter parameter and the
608		# size string for the variable length parameter(s).  While
609		# that is being done, calculate a unique signature for this
610		# function.
611
612		for p in f.parameterIterateGlxSend():
613			if p.is_counter:
614				fixup.append( p.name )
615				params.append( p )
616			elif p.counter:
617				s = p.size()
618				if s == 0: s = 1
619
620				sig += "(%u,%u)" % (f.offset_of(p.counter), s)
621				size += '%s%s' % (plus, p.size_string())
622				plus = ' + '
623
624
625		# If the calculated signature matches a function that has
626		# already be emitted, don't emit this function.  Instead, add
627		# it to the list of function aliases.
628
629		if self.counter_sigs.has_key(sig):
630			n = self.counter_sigs[sig];
631			alias = [f.name, n]
632		else:
633			alias = None
634			self.counter_sigs[sig] = f.name
635
636			self.common_func_print_just_header(f)
637
638			for p in params:
639				self.common_emit_one_arg(p, "pc", 0)
640
641
642			print ''
643			self.common_emit_fixups(fixup)
644			print ''
645
646			print '    return __GLX_PAD(%s);' % (size)
647			print '}'
648			print ''
649
650		return alias
651
652
653def show_usage():
654	print "Usage: %s [-f input_file_name] -m output_mode [--only-get | --only-set] [--get-alias-set]" % sys.argv[0]
655	print "    -m output_mode   Output mode can be one of 'size_c' or 'size_h'."
656	print "    --only-get       Only emit 'get'-type functions."
657	print "    --only-set       Only emit 'set'-type functions."
658	print ""
659	print "By default, both 'get' and 'set'-type functions are emitted."
660	sys.exit(1)
661
662
663if __name__ == '__main__':
664	file_name = "gl_API.xml"
665
666	try:
667		(args, trail) = getopt.getopt(sys.argv[1:], "f:m:h:", ["only-get", "only-set", "header-tag"])
668	except Exception,e:
669		show_usage()
670
671	mode = None
672	header_tag = None
673	which_functions = PrintGlxSizeStubs_common.do_get | PrintGlxSizeStubs_common.do_set
674
675	for (arg,val) in args:
676		if arg == "-f":
677			file_name = val
678		elif arg == "-m":
679			mode = val
680		elif arg == "--only-get":
681			which_functions = PrintGlxSizeStubs_common.do_get
682		elif arg == "--only-set":
683			which_functions = PrintGlxSizeStubs_common.do_set
684		elif (arg == '-h') or (arg == "--header-tag"):
685			header_tag = val
686
687	if mode == "size_c":
688		printer = PrintGlxSizeStubs_c( which_functions )
689	elif mode == "size_h":
690		printer = PrintGlxSizeStubs_h( which_functions )
691		if header_tag:
692			printer.header_tag = header_tag
693	elif mode == "reqsize_c":
694		printer = PrintGlxReqSize_c()
695	elif mode == "reqsize_h":
696		printer = PrintGlxReqSize_h()
697	else:
698		show_usage()
699
700	api = gl_XML.parse_GL_API( file_name, glX_XML.glx_item_factory() )
701
702
703	printer.Print( api )
704