2 # Copyright (c) 2015-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 from __future__
import (
23 absolute_import
, division
, print_function
, unicode_literals
33 from framework
import exceptions
34 from framework
.core
import lazy_property
35 from framework
.options
import OPTIONS
36 # from framework.test import piglit_test
39 class StopWflinfo(exceptions
.PiglitException
):
40 """Exception called when wlfinfo getter should stop."""
41 def __init__(self
, reason
):
42 super(StopWflinfo
, self
).__init
__()
46 class ProfileInfo(object):
47 """Information about a single profile (core, compat, es1, es2, etc)."""
49 def __init__(self
, shader_version
, language_version
, extensions
):
50 self
.shader_version
= shader_version
51 self
.api_version
= language_version
52 self
.extensions
= extensions
55 class WflInfo(object):
56 """Class representing platform information as provided by wflinfo.
58 The design of this is odd to say the least, it's basically a bag with some
59 lazy property evaluators in it, used to avoid calculating the values
60 provided by wflinfo more than once.
63 - Needs to be shared with all subclasses
64 - Needs to evaluate only once
65 - cannot evaluate until user sets OPTIONS.env['PIGLIT_PLATFORM']
67 This solves all of that.
72 __core_lock
= threading
.Lock()
74 __compat_lock
= threading
.Lock()
76 __es1_lock
= threading
.Lock()
78 __es2_lock
= threading
.Lock()
80 def __new__(cls
, *args
, **kwargs
):
81 # Implement the borg pattern:
82 # https://code.activestate.com/recipes/66531-singleton-we-dont-need-no-stinkin-singleton-the-bo/
84 # This is something like a singleton, but much easier to implement
85 self
= super(WflInfo
, cls
).__new
__(cls
, *args
, **kwargs
)
86 self
.__dict
__ = cls
.__shared
_state
90 def __call_wflinfo(opts
):
91 """Helper to call wflinfo and reduce code duplication.
93 This catches and handles CalledProcessError and OSError.ernno == 2
94 gracefully: it passes them to allow platforms without a particular
95 gl/gles version or wflinfo (resepctively) to work.
98 opts -- arguments to pass to wflinfo other than verbose and platform
101 with
open(os
.devnull
, 'w') as d
:
103 # Get the piglit platform string and, if needed, convert it
104 # to something that wflinfo understands.
105 platform
= OPTIONS
.env
['PIGLIT_PLATFORM']
106 if platform
== "mixed_glx_egl":
109 if sys
.platform
in ['windows', 'cygwin']:
114 cmd
= [bin
, '--platform', platform
] + opts
116 # setup execution environment where we extend the PATH env var
117 # to include the piglit TEST_BIN_DIR
118 new_env
= os
.environ
.copy()
119 # new_env['PATH'] = ':'.join([piglit_test.TEST_BIN_DIR,
120 # os.environ['PATH']])
122 raw
= subprocess
.check_output(cmd
, env
=new_env
, stderr
=d
)
124 except subprocess
.CalledProcessError
:
125 # When we hit this error it usually going to be because we have
126 # an incompatible platform/profile combination
127 raise StopWflinfo('Called')
129 # If we get a 'no wflinfo' warning then just return
130 print("wflinfo utility not found.", file=sys
.stderr
)
131 if e
.errno
== errno
.ENOENT
:
132 raise StopWflinfo('OSError')
134 return raw
.decode('utf-8')
137 def __getline(lines
, name
):
138 """Find a line in a list return it."""
140 if line
.startswith(name
):
142 raise Exception('Unreachable')
144 def __get_shader_version(self
, profile
):
145 """Calculate the maximum OpenGL Shader Language version."""
147 if profile
in ['core', 'compat', 'none']:
149 raw
= self
.__call
_wflinfo
(
150 ['--verbose', '--api', 'gl', '--profile', profile
])
151 except StopWflinfo
as e
:
152 if e
.reason
not in ['Called', 'OSError']:
156 # GLSL versions are M.mm formatted
157 line
= self
.__getline
(raw
.split('\n'), 'OpenGL shading language')
158 ret
= float(line
.split(":")[1][:5])
159 except (IndexError, ValueError):
160 # This is caused by wflinfo returning an error
162 elif profile
in ['gles2', 'gles3']:
164 raw
= self
.__call
_wflinfo
(['--verbose', '--api', profile
])
165 except StopWflinfo
as e
:
166 if e
.reason
not in ['Called', 'OSError']:
170 # GLSL ES version numbering is insane.
171 # For version >= 3 the numbers are 3.00, 3.10, etc.
172 # For version 2, they are 1.0.xx
173 ret
= float(self
.__getline
(
175 'OpenGL shading language').split()[-1][:3])
176 except (IndexError, ValueError):
177 # Handle wflinfo internal errors
181 def __get_language_version(self
, profile
):
183 if profile
in ['core', 'compat', 'none']:
185 raw
= self
.__call
_wflinfo
(['--api', 'gl', '--profile', profile
])
186 except StopWflinfo
as e
:
187 if e
.reason
not in ['Called', 'OSError']:
191 # Grab the GL version string, trim any release_number values
192 ret
= float(self
.__getline
(
194 'OpenGL version string').split()[3][:3])
195 except (IndexError, ValueError):
196 # This is caused by wlfinfo returning an error
200 raw
= self
.__call
_wflinfo
(['--api', profile
])
201 except StopWflinfo
as e
:
202 if e
.reason
not in ['Called', 'OSError']:
206 # Yes, search for "OpenGL version string" in GLES
207 # GLES doesn't support patch versions.
208 ret
= float(self
.__getline
(
210 'OpenGL version string').split()[5])
211 except (IndexError, ValueError):
212 # This is caused by wlfinfo returning an error
216 def __get_extensions(self
, profile
):
217 """Call wflinfo to get opengl extensions.
219 This provides a very conservative set of extensions, it provides every
220 extension from gles1, 2 and 3 and from GL both core and compat profile
221 as a single set. This may let a few tests execute that will still skip
222 manually, but it helps to ensure that this method never skips when it
226 _trim
= len('OpenGL extensions: ')
230 """Helper function to reduce code duplication."""
231 # This is a pretty fragile function but it really does help with
233 ret
= self
.__call
_wflinfo
(args
)
234 all_
.update(set(self
.__getline
(
235 ret
.split('\n'), 'OpenGL extensions')[_trim
:].split()))
238 if profile
in ['core', 'compat', 'none']:
239 helper(['--verbose', '--api', 'gl', '--profile', profile
])
241 helper(['--verbose', '--api', profile
])
242 except StopWflinfo
as e
:
243 # Handle wflinfo not being installed by returning an empty set. This
244 # will essentially make FastSkipMixin a no-op.
245 if e
.reason
in ['OSError', 'Called']:
249 # Don't return a set with only WFLINFO_GL_ERROR.
250 ret
= {e
.strip() for e
in all_
}
251 if ret
== {'WFLINFO_GL_ERROR'}:
255 def __build_info(self
, profile
):
257 self
.__get
_shader
_version
(profile
),
258 self
.__get
_language
_version
(profile
),
259 self
.__get
_extensions
(profile
)
264 with self
.__core
_lock
:
265 if not self
.__core
_init
:
266 self
.__core
_init
= True
267 return self
.__build
_info
('core')
272 with self
.__compat
_lock
:
273 if not self
.__compat
_init
:
274 self
.__compat
_init
= True
275 comp
= self
.__build
_info
('compat')
276 if comp
.api_version
== 0.0:
277 # In this case there are not compat profiles, try agian
278 # with a "legacy" profile, which could be promoted to
280 return self
.__build
_info
('none')
286 with self
.__es
1_lock
:
287 if not self
.__es
1_init
:
288 self
.__es
1_init
= True
289 return self
.__build
_info
('gles1')
294 with self
.__es
2_lock
:
295 if not self
.__es
2_init
:
296 self
.__es
2_init
= True
297 return self
.__build
_info
('gles2')