2 # Copyright (c) 2015-2016, 2019 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
28 from framework
import exceptions
29 from framework
.core
import lazy_property
30 from framework
.options
import OPTIONS
31 # from framework.test import piglit_test
34 class StopWflinfo(exceptions
.PiglitException
):
35 """Exception called when wlfinfo getter should stop."""
36 def __init__(self
, reason
):
37 super(StopWflinfo
, self
).__init
__()
41 class ProfileInfo(object):
42 """Information about a single profile (core, compat, es1, es2, etc)."""
44 def __init__(self
, shader_version
, language_version
, extensions
):
45 self
.shader_version
= shader_version
46 self
.api_version
= language_version
47 self
.extensions
= extensions
50 class WflInfo(object):
51 """Class representing platform information as provided by wflinfo.
53 The design of this is odd to say the least, it's basically a bag with some
54 lazy property evaluators in it, used to avoid calculating the values
55 provided by wflinfo more than once.
58 - Needs to be shared with all subclasses
59 - Needs to evaluate only once
60 - cannot evaluate until user sets OPTIONS.env['PIGLIT_PLATFORM']
62 This solves all of that.
67 __core_lock
= threading
.Lock()
69 __compat_lock
= threading
.Lock()
71 __es1_lock
= threading
.Lock()
73 __es2_lock
= threading
.Lock()
75 def __new__(cls
, *args
, **kwargs
):
76 # Implement the borg pattern:
77 # https://code.activestate.com/recipes/66531-singleton-we-dont-need-no-stinkin-singleton-the-bo/
79 # This is something like a singleton, but much easier to implement
80 self
= super(WflInfo
, cls
).__new
__(cls
, *args
, **kwargs
)
81 self
.__dict
__ = cls
.__shared
_state
85 def __call_wflinfo(opts
):
86 """Helper to call wflinfo and reduce code duplication.
88 This catches and handles CalledProcessError and OSError.ernno == 2
89 gracefully: it passes them to allow platforms without a particular
90 gl/gles version or wflinfo (resepctively) to work.
93 opts -- arguments to pass to wflinfo other than verbose and platform
96 with
open(os
.devnull
, 'w') as d
:
98 # Get the piglit platform string and, if needed, convert it
99 # to something that wflinfo understands.
100 platform
= OPTIONS
.env
['PIGLIT_PLATFORM']
101 if platform
== "mixed_glx_egl":
104 if sys
.platform
in ['windows', 'cygwin']:
109 cmd
= [bin
, '--platform', platform
] + opts
111 # setup execution environment where we extend the PATH env var
112 # to include the piglit TEST_BIN_DIR
113 new_env
= os
.environ
.copy()
114 # new_env['PATH'] = ':'.join([piglit_test.TEST_BIN_DIR,
115 # os.environ['PATH']])
117 raw
= subprocess
.check_output(cmd
, env
=new_env
, stderr
=d
)
119 except subprocess
.CalledProcessError
:
120 # When we hit this error it usually going to be because we have
121 # an incompatible platform/profile combination
122 raise StopWflinfo('Called')
124 # If we get a 'no wflinfo' warning then just return
125 print("wflinfo utility not found.", file=sys
.stderr
)
126 if e
.errno
== errno
.ENOENT
:
127 raise StopWflinfo('OSError')
129 return raw
.decode('utf-8')
132 def __getline(lines
, name
):
133 """Find a line in a list return it."""
135 if line
.startswith(name
):
137 raise Exception('Unreachable')
139 def __get_shader_version(self
, profile
):
140 """Calculate the maximum OpenGL Shader Language version."""
142 if profile
in ['core', 'compat', 'none']:
144 raw
= self
.__call
_wflinfo
(
145 ['--verbose', '--api', 'gl', '--profile', profile
])
146 except StopWflinfo
as e
:
147 if e
.reason
not in ['Called', 'OSError']:
151 # GLSL versions are M.mm formatted
152 line
= self
.__getline
(raw
.split('\n'), 'OpenGL shading language')
153 ret
= float(line
.split(":")[1][:5])
154 except (IndexError, ValueError):
155 # This is caused by wflinfo returning an error
157 elif profile
in ['gles2', 'gles3']:
159 raw
= self
.__call
_wflinfo
(['--verbose', '--api', profile
])
160 except StopWflinfo
as e
:
161 if e
.reason
not in ['Called', 'OSError']:
165 # GLSL ES version numbering is insane.
166 # For version >= 3 the numbers are 3.00, 3.10, etc.
167 # For version 2, they are 1.0.xx
168 ret
= float(self
.__getline
(
170 'OpenGL shading language').split()[-1][:3])
171 except (IndexError, ValueError):
172 # Handle wflinfo internal errors
176 def __get_language_version(self
, profile
):
178 if profile
in ['core', 'compat', 'none']:
180 raw
= self
.__call
_wflinfo
(['--api', 'gl', '--profile', profile
])
181 except StopWflinfo
as e
:
182 if e
.reason
not in ['Called', 'OSError']:
186 # Grab the GL version string, trim any release_number values
187 ret
= float(self
.__getline
(
189 'OpenGL version string').split()[3][:3])
190 except (IndexError, ValueError):
191 # This is caused by wlfinfo returning an error
195 raw
= self
.__call
_wflinfo
(['--api', profile
])
196 except StopWflinfo
as e
:
197 if e
.reason
not in ['Called', 'OSError']:
201 # Yes, search for "OpenGL version string" in GLES
202 # GLES doesn't support patch versions.
203 ret
= float(self
.__getline
(
205 'OpenGL version string').split()[5])
206 except (IndexError, ValueError):
207 # This is caused by wlfinfo returning an error
211 def __get_extensions(self
, profile
):
212 """Call wflinfo to get opengl extensions.
214 This provides a very conservative set of extensions, it provides every
215 extension from gles1, 2 and 3 and from GL both core and compat profile
216 as a single set. This may let a few tests execute that will still skip
217 manually, but it helps to ensure that this method never skips when it
221 _trim
= len('OpenGL extensions: ')
225 """Helper function to reduce code duplication."""
226 # This is a pretty fragile function but it really does help with
228 ret
= self
.__call
_wflinfo
(args
)
229 all_
.update(set(self
.__getline
(
230 ret
.split('\n'), 'OpenGL extensions')[_trim
:].split()))
233 if profile
in ['core', 'compat', 'none']:
234 helper(['--verbose', '--api', 'gl', '--profile', profile
])
236 helper(['--verbose', '--api', profile
])
237 except StopWflinfo
as e
:
238 # Handle wflinfo not being installed by returning an empty set. This
239 # will essentially make FastSkipMixin a no-op.
240 if e
.reason
in ['OSError', 'Called']:
244 # Don't return a set with only WFLINFO_GL_ERROR.
245 ret
= {e
.strip() for e
in all_
}
246 if ret
== {'WFLINFO_GL_ERROR'}:
250 def __build_info(self
, profile
):
252 self
.__get
_shader
_version
(profile
),
253 self
.__get
_language
_version
(profile
),
254 self
.__get
_extensions
(profile
)
259 with self
.__core
_lock
:
260 if not self
.__core
_init
:
261 self
.__core
_init
= True
262 return self
.__build
_info
('core')
267 with self
.__compat
_lock
:
268 if not self
.__compat
_init
:
269 self
.__compat
_init
= True
270 comp
= self
.__build
_info
('compat')
271 if comp
.api_version
== 0.0:
272 # In this case there are not compat profiles, try again
273 # with a "legacy" profile, which could be promoted to
275 return self
.__build
_info
('none')
281 with self
.__es
1_lock
:
282 if not self
.__es
1_init
:
283 self
.__es
1_init
= True
284 return self
.__build
_info
('gles1')
289 with self
.__es
2_lock
:
290 if not self
.__es
2_init
:
291 self
.__es
2_init
= True
292 return self
.__build
_info
('gles2')