2 # Copyright © 2016 Intel Corporation
4 # Permission is hereby granted, free of charge, to any person obtaining a copy
5 # of this software and associated documentation files (the "Software"), to deal
6 # in the Software without restriction, including without limitation the rights
7 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 # copies of the Software, and to permit persons to whom the Software is
9 # furnished to do so, subject to the following conditions:
11 # The above copyright notice and this permission notice shall be included in
12 # all copies or substantial portions of the Software.
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 """Provides helper classes for representing glsl data."""
33 class _Version(object):
34 """Factory class for glsl versions.
36 provides an object cache to reduce duplication of objects. This object
37 should not be initialized more than once, to avoid that use the Version
38 constant provided by the module.
40 It would provide either a GLSLVersion or a GLSLESVersion.
68 def __call__(self
, ver
):
69 """Make a Version object, or provide one from the cache."""
70 assert isinstance(ver
, str)
72 # Try to get an object from the cache, if that fails create a new one
73 # and add it to the cache before returning it.
75 return self
.__cache
[ver
]
77 if ver
in self
._es
_versions
:
78 obj
= GLSLESVersion(ver
)
79 elif ver
in self
._versions
:
80 obj
= GLSLVersion(ver
)
82 raise Exception('Undefined version {}'.format(ver
))
84 self
.__cache
[ver
] = obj
88 Version
= _Version() # pylint: disable=invalid-name
91 @functools.total_ordering
92 class GLSLVersion(object):
93 """A Representation of an OpenGL Shading Language version.
95 This object provides a bunch of the niceties that one would want. It's
96 orderable (can be sorted, and can be compared with the standard ==, !=, <,
97 etc), can be called with str() (which provides the integer version, like
98 120), and a method to print the decimal version (1.20).
100 Do not initialize this directly.
103 def __init__(self
, ver
):
104 assert ver
in ['110', '120', '130', '140', '150', '330', '400', '410',
105 '420', '430', '440', '450']
110 return self
._internal
113 return int(self
._internal
)
116 return float(int(self
) / 100)
118 def __eq__(self
, other
):
119 # If the other version is ES then we know they're not equal
120 if isinstance(other
, GLSLESVersion
):
122 elif isinstance(other
, (GLSLVersion
, int)):
123 return int(self
) == int(other
)
124 elif isinstance(other
, float):
125 return float(self
) == float(other
)
127 return NotImplemented
129 def __ne__(self
, other
):
130 return not self
== other
132 def __lt__(self
, other
):
133 if isinstance(other
, GLSLESVersion
):
134 raise TypeError('Unorderable types GLSLVersion and GLSLESVersion')
135 elif isinstance(other
, (GLSLVersion
, int)):
136 return int(self
) < int(other
)
137 elif isinstance(other
, float):
138 return float(self
) < other
140 return NotImplemented
142 def print_float(self
):
143 """A version suitable to print as a decimal with two trailing digits."""
144 return '{:.2f}'.format(float(self
))
147 @functools.total_ordering
148 class GLSLESVersion(object):
149 """Represents a GLSL ES version.
151 Do not initialize this directly.
154 def __init__(self
, ver
):
158 self
._internal
= ver
[:-3] # drop " es"
162 if self
._internal
== '100':
163 return self
._internal
165 return self
._internal
+ " es"
168 return int(self
._internal
)
171 return float(int(self
) / 100)
173 def __eq__(self
, other
):
174 # If the other version is ES then we know they're not equal
175 if isinstance(other
, GLSLVersion
):
177 elif isinstance(other
, (GLSLESVersion
, int)):
178 return int(self
) == int(other
)
179 elif isinstance(other
, float):
180 return float(self
) == float(other
)
182 return NotImplemented
184 def __ne__(self
, other
):
185 return not self
== other
187 def __lt__(self
, other
):
188 if isinstance(other
, GLSLVersion
):
189 raise TypeError('Unorderable types GLSLESVersion and GLSLVersion')
190 elif isinstance(other
, (GLSLESVersion
, int)):
191 return int(self
) < int(other
)
192 elif isinstance(other
, float):
193 return float(self
) < other
195 return NotImplemented
197 def print_float(self
):
198 """A version suitable to print as a decimal with two trailing digits."""
199 if self
._internal
== '100':
200 return '{:.2f}'.format(float(self
))
202 return '{:.2f} es'.format(float(self
))
205 class _MinVersion(object):
206 """A factory class for sorting GLSL and GLSLES versions.
208 This stores the minimum version required for various operations (currently
209 only for_stage and for_stage_with_ext).
211 This class is not meant to be reinitialized, instead use the provided
216 'frag': Version('110'),
217 'vert': Version('110'),
218 'geom': Version('150'),
219 'tesc': Version('400'),
220 'tese': Version('400'),
221 'comp': Version('430'),
223 # Only versions that actaly change are here, the function will return the
224 # values from __gl_stage_min if they're not here
225 __gl_stage_min_ext
= {
226 # geometry_shader4 is not included here intentionally. It is
227 # significantly different than the geometry shaders in OpenGL 3.2
228 'tesc': (Version('140'), 'GL_ARB_tessellation_shader'),
229 'tese': (Version('140'), 'GL_ARB_tessellation_shader'),
230 'comp': (Version('140'), 'GL_ARB_compute_shader'),
233 'frag': Version('100'),
234 'vert': Version('100'),
235 'comp': Version('310 es'),
236 'geom': Version('320 es'),
237 'tesc': Version('320 es'),
238 'tese': Version('320 es'),
240 # Only versions that actaly change are here, the function will return the
241 # values from __gles_stage_min if they're not here
242 __gles_stage_min_ext
= {
243 'geom': (Version('310 es'), 'GL_OES_geometry_shader'),
244 'tesc': (Version('310 es'), 'GL_OES_tessellation_shader'),
245 'tese': (Version('310 es'), 'GL_OES_tessellation_shader'),
248 def for_stage(self
, stage
, version
):
249 """Return max(stage minimum version, requested version).
251 When provided a stage and a version, it will return the greater of the
252 provided version and the minimum version of that stage without an
253 extension. For example, in OpenGL teselation is available in GLSL
254 4.00+, or in 1.40+ with ARB_tessellation_shader. Given Version('150')
255 and 'tesc' this method returns Version('400').
258 stage -- A stage named by the extensions glslparsertest uses (frag,
259 vert, geom, tesc, tese, comp)
260 version -- A version as returned by the Version function.
262 >>> m = _MinVersion()
263 >>> m.for_stage('geom', Version('300 es'))
265 >>> m.for_stage('frag', Version('130'))
269 assert isinstance(version
, (GLSLVersion
, GLSLESVersion
))
270 if isinstance(version
, GLSLVersion
):
271 _stage
= self
.__gl
_stage
_min
[stage
]
272 elif isinstance(version
, GLSLESVersion
):
273 _stage
= self
.__gles
_stage
_min
[stage
]
275 return _stage
if _stage
> version
else version
277 def for_stage_with_ext(self
, stage
, version
):
278 """Return the earliest GLSL version that a stage is supported in with
281 When provided a stage and a version, it will return the greater of the
282 provided version and the minimum version of that stage with an
283 extension, and if necissary the extension as a string. For example, in
284 OpenGL teselation is available in GLSL 4.00+, or in 1.40+ with
285 ARB_tessellation_shader. Given Version('150') and 'tesc' this method
286 returns (Version('150'), 'GL_ARB_tessellation_shader'); but given
287 Version('400') and 'tesc' it returns (Version('400'), None)
289 If there is no extension (like with fragment and vertex) then None will
290 be returned as the secon value. It is up to the caller to handle this
291 appropriately. It will also return None for the extension when the GLSL
292 version is high enough to not require an extension.
294 Takes the same arguments as for_stage.
296 >>> m = _MinVersion()
297 >>> m.for_stage_with_ext('geom', Version('300 es'))
298 (Version('310 es'), 'GL_OES_geometry_shader')
299 >>> m.for_stage_with_ext('frag', Version('130'))
300 (Version('130'), None)
303 assert isinstance(version
, (GLSLVersion
, GLSLESVersion
))
304 if isinstance(version
, GLSLVersion
):
306 _stage
, ext
= self
.__gl
_stage
_min
_ext
[stage
]
308 _stage
, ext
= self
.__gl
_stage
_min
[stage
], None
309 elif isinstance(version
, GLSLESVersion
):
311 _stage
, ext
= self
.__gles
_stage
_min
_ext
[stage
]
313 _stage
, ext
= self
.__gles
_stage
_min
[stage
], None
315 # If the version queried is less than the required, return the require
319 # If the requested version is greater or equal to the version that the
320 # feature became core in, return the version and None
321 elif self
.for_stage(stage
, version
) <= version
:
322 return (version
, None)
323 # Otherwise the requested version and the extension are returned
325 return (version
, ext
)
328 MinVersion
= _MinVersion() # pylint: disable=invalid-name