summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Paul <brianp@vmware.com>2017-11-01 15:00:16 -0600
committerBrian Paul <brianp@vmware.com>2017-11-09 21:10:27 -0700
commitedc41a1db7e21489eda463745a117feec10df5da (patch)
treeed8043cda6aa17e2a7c5354386121d0d23022ad5
parent733e3ab212fcce735f47ed9f8659ccdf6f625a70 (diff)
framework: move WflInfo class into new wflinfo.py module
Reduce the clutter in opengl.py Reviewed-by: Dylan Baker <dylan@pnwbakers.com>
-rw-r--r--framework/test/opengl.py250
-rw-r--r--framework/wflinfo.py274
2 files changed, 276 insertions, 248 deletions
diff --git a/framework/test/opengl.py b/framework/test/opengl.py
index b037652ff..4941d8ce8 100644
--- a/framework/test/opengl.py
+++ b/framework/test/opengl.py
@@ -23,15 +23,12 @@
from __future__ import (
absolute_import, division, print_function, unicode_literals
)
-import errno
import os
-import subprocess
import warnings
import six
-from framework import exceptions, core
-from framework.options import OPTIONS
+from framework import wflinfo
from .base import TestIsSkip
# pylint: disable=too-few-public-methods
@@ -42,249 +39,6 @@ __all__ = [
]
-
-class StopWflinfo(exceptions.PiglitException):
- """Exception called when wlfinfo getter should stop."""
- def __init__(self, reason):
- super(StopWflinfo, self).__init__()
- self.reason = reason
-
-
-class WflInfo(object):
- """Class representing platform information as provided by wflinfo.
-
- The design of this is odd to say the least, it's basically a bag with some
- lazy property evaluators in it, used to avoid calculating the values
- provided by wflinfo more than once.
-
- The problems:
- - Needs to be shared with all subclasses
- - Needs to evaluate only once
- - cannot evaluate until user sets OPTIONS.env['PIGLIT_PLATFORM']
-
- This solves all of that.
-
- """
- __shared_state = {}
- def __new__(cls, *args, **kwargs):
- # Implement the borg pattern:
- # https://code.activestate.com/recipes/66531-singleton-we-dont-need-no-stinkin-singleton-the-bo/
- #
- # This is something like a singleton, but much easier to implement
- self = super(WflInfo, cls).__new__(cls, *args, **kwargs)
- self.__dict__ = cls.__shared_state
- return self
-
- @staticmethod
- def __call_wflinfo(opts):
- """Helper to call wflinfo and reduce code duplication.
-
- This catches and handles CalledProcessError and OSError.ernno == 2
- gracefully: it passes them to allow platforms without a particular
- gl/gles version or wflinfo (resepctively) to work.
-
- Arguments:
- opts -- arguments to pass to wflinfo other than verbose and platform
-
- """
- with open(os.devnull, 'w') as d:
- try:
- raw = subprocess.check_output(
- ['wflinfo',
- '--platform', OPTIONS.env['PIGLIT_PLATFORM']] + opts,
- stderr=d)
- except subprocess.CalledProcessError:
- # When we hit this error it usually going to be because we have
- # an incompatible platform/profile combination
- raise StopWflinfo('Called')
- except OSError as e:
- # If we get a 'no wflinfo' warning then just return
- if e.errno == errno.ENOENT:
- raise StopWflinfo('OSError')
- raise
- return raw.decode('utf-8')
-
- @staticmethod
- def __getline(lines, name):
- """Find a line in a list return it."""
- for line in lines:
- if line.startswith(name):
- return line
- raise Exception('Unreachable')
-
- @core.lazy_property
- def gl_extensions(self):
- """Call wflinfo to get opengl extensions.
-
- This provides a very conservative set of extensions, it provides every
- extension from gles1, 2 and 3 and from GL both core and compat profile
- as a single set. This may let a few tests execute that will still skip
- manually, but it helps to ensure that this method never skips when it
- shouldn't.
-
- """
- _trim = len('OpenGL extensions: ')
- all_ = set()
-
- def helper(const, vars_):
- """Helper function to reduce code duplication."""
- # This is a pretty fragile function but it really does help with
- # duplication
- for var in vars_:
- try:
- ret = self.__call_wflinfo(const + [var])
- except StopWflinfo as e:
- # This means tat the particular api or profile is
- # unsupported
- if e.reason == 'Called':
- continue
- else:
- raise
- all_.update(set(self.__getline(
- ret.split('\n'), 'OpenGL extensions')[_trim:].split()))
-
- try:
- helper(['--verbose', '--api'], ['gles1', 'gles2', 'gles3'])
- helper(['--verbose', '--api', 'gl', '--profile'],
- ['core', 'compat', 'none'])
- except StopWflinfo as e:
- # Handle wflinfo not being installed by returning an empty set. This
- # will essentially make FastSkipMixin a no-op.
- if e.reason == 'OSError':
- return set()
- raise
-
- # Don't return a set with only WFLINFO_GL_ERROR.
- ret = {e.strip() for e in all_}
- if ret == {'WFLINFO_GL_ERROR'}:
- return set()
- return ret
-
- @core.lazy_property
- def gl_version(self):
- """Calculate the maximum opengl version.
-
- This will try (in order): core, compat, and finally no profile,
- stopping when it finds a profile. It assumes that most implementations
- will have core and compat as equals, or core as superior to compat in
- terms of support.
-
- """
- ret = None
- for profile in ['core', 'compat', 'none']:
- try:
- raw = self.__call_wflinfo(['--api', 'gl', '--profile', profile])
- except StopWflinfo as e:
- if e.reason == 'Called':
- continue
- elif e.reason == 'OSError':
- break
- raise
- else:
- try:
- # Grab the GL version string, trim any release_number values
- ret = float(self.__getline(
- raw.split('\n'),
- 'OpenGL version string').split()[3][:3])
- except (IndexError, ValueError):
- # This is caused by wlfinfo returning an error
- pass
- break
- return ret
-
- @core.lazy_property
- def gles_version(self):
- """Calculate the maximum opengl es version.
-
- The design of this function isn't 100% correct. GLES1 and GLES2+ behave
- differently, since 2+ can be silently promoted, but 1 cannot. This
- means that a driver can implement 2, 3, 3.1, etc, but never have 1
- support.
-
- I don't think this is a big deal for a couple of reasons. First, piglit
- has a very small set of GLES1 tests, so they shouldn't have big impact
- on runtime, and second, the design of the FastSkipMixin is
- conservative: it would rather run a few tests that should be skipped
- than skip a few tests that should be run.
-
- """
- ret = None
- for api in ['gles3', 'gles2', 'gles1']:
- try:
- raw = self.__call_wflinfo(['--api', api])
- except StopWflinfo as e:
- if e.reason == 'Called':
- continue
- elif e.reason == 'OSError':
- break
- raise
- else:
- try:
- # Yes, search for "OpenGL version string" in GLES
- # GLES doesn't support patch versions.
- ret = float(self.__getline(
- raw.split('\n'),
- 'OpenGL version string').split()[5])
- except (IndexError, ValueError):
- # This is caused by wlfinfo returning an error
- pass
- break
- return ret
-
- @core.lazy_property
- def glsl_version(self):
- """Calculate the maximum OpenGL Shader Language version."""
- ret = None
- for profile in ['core', 'compat', 'none']:
- try:
- raw = self.__call_wflinfo(
- ['--verbose', '--api', 'gl', '--profile', profile])
- except StopWflinfo as e:
- if e.reason == 'Called':
- continue
- elif e.reason == 'OSError':
- break
- raise
- else:
- try:
- # GLSL versions are M.mm formatted
- ret = float(self.__getline(
- raw.split('\n'),
- 'OpenGL shading language').split()[-1][:4])
- except (IndexError, ValueError):
- # This is caused by wflinfo returning an error
- pass
- break
- return ret
-
- @core.lazy_property
- def glsl_es_version(self):
- """Calculate the maximum OpenGL ES Shader Language version."""
- ret = None
- for api in ['gles3', 'gles2']:
- try:
- raw = self.__call_wflinfo(['--verbose', '--api', api])
- except StopWflinfo as e:
- if e.reason == 'Called':
- continue
- elif e.reason == 'OSError':
- break
- raise
- else:
- try:
- # GLSL ES version numbering is insane.
- # For version >= 3 the numbers are 3.00, 3.10, etc.
- # For version 2, they are 1.0.xx
- ret = float(self.__getline(
- raw.split('\n'),
- 'OpenGL shading language').split()[-1][:3])
- except (IndexError, ValueError):
- # Handle wflinfo internal errors
- pass
- break
- return ret
-
-
class FastSkip(object):
"""A class for testing OpenGL requirements.
@@ -313,7 +67,7 @@ class FastSkip(object):
__slots__ = ['gl_required', 'gl_version', 'gles_version', 'glsl_version',
'glsl_es_version']
- info = WflInfo()
+ info = wflinfo.WflInfo()
def __init__(self, gl_required=None, gl_version=None, gles_version=None,
glsl_version=None, glsl_es_version=None):
diff --git a/framework/wflinfo.py b/framework/wflinfo.py
new file mode 100644
index 000000000..b9a05f8ee
--- /dev/null
+++ b/framework/wflinfo.py
@@ -0,0 +1,274 @@
+# Copyright (c) 2015-2016 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+from __future__ import (
+ absolute_import, division, print_function, unicode_literals
+)
+import errno
+import os
+import subprocess
+
+import six
+
+from framework import exceptions, core
+from framework.options import OPTIONS
+
+
+class StopWflinfo(exceptions.PiglitException):
+ """Exception called when wlfinfo getter should stop."""
+ def __init__(self, reason):
+ super(StopWflinfo, self).__init__()
+ self.reason = reason
+
+
+class WflInfo(object):
+ """Class representing platform information as provided by wflinfo.
+
+ The design of this is odd to say the least, it's basically a bag with some
+ lazy property evaluators in it, used to avoid calculating the values
+ provided by wflinfo more than once.
+
+ The problems:
+ - Needs to be shared with all subclasses
+ - Needs to evaluate only once
+ - cannot evaluate until user sets OPTIONS.env['PIGLIT_PLATFORM']
+
+ This solves all of that.
+
+ """
+ __shared_state = {}
+ def __new__(cls, *args, **kwargs):
+ # Implement the borg pattern:
+ # https://code.activestate.com/recipes/66531-singleton-we-dont-need-no-stinkin-singleton-the-bo/
+ #
+ # This is something like a singleton, but much easier to implement
+ self = super(WflInfo, cls).__new__(cls, *args, **kwargs)
+ self.__dict__ = cls.__shared_state
+ return self
+
+ @staticmethod
+ def __call_wflinfo(opts):
+ """Helper to call wflinfo and reduce code duplication.
+
+ This catches and handles CalledProcessError and OSError.ernno == 2
+ gracefully: it passes them to allow platforms without a particular
+ gl/gles version or wflinfo (resepctively) to work.
+
+ Arguments:
+ opts -- arguments to pass to wflinfo other than verbose and platform
+
+ """
+ with open(os.devnull, 'w') as d:
+ try:
+ raw = subprocess.check_output(
+ ['wflinfo',
+ '--platform', OPTIONS.env['PIGLIT_PLATFORM']] + opts,
+ stderr=d)
+ except subprocess.CalledProcessError:
+ # When we hit this error it usually going to be because we have
+ # an incompatible platform/profile combination
+ raise StopWflinfo('Called')
+ except OSError as e:
+ # If we get a 'no wflinfo' warning then just return
+ if e.errno == errno.ENOENT:
+ raise StopWflinfo('OSError')
+ raise
+ return raw.decode('utf-8')
+
+ @staticmethod
+ def __getline(lines, name):
+ """Find a line in a list return it."""
+ for line in lines:
+ if line.startswith(name):
+ return line
+ raise Exception('Unreachable')
+
+ @core.lazy_property
+ def gl_extensions(self):
+ """Call wflinfo to get opengl extensions.
+
+ This provides a very conservative set of extensions, it provides every
+ extension from gles1, 2 and 3 and from GL both core and compat profile
+ as a single set. This may let a few tests execute that will still skip
+ manually, but it helps to ensure that this method never skips when it
+ shouldn't.
+
+ """
+ _trim = len('OpenGL extensions: ')
+ all_ = set()
+
+ def helper(const, vars_):
+ """Helper function to reduce code duplication."""
+ # This is a pretty fragile function but it really does help with
+ # duplication
+ for var in vars_:
+ try:
+ ret = self.__call_wflinfo(const + [var])
+ except StopWflinfo as e:
+ # This means tat the particular api or profile is
+ # unsupported
+ if e.reason == 'Called':
+ continue
+ else:
+ raise
+ all_.update(set(self.__getline(
+ ret.split('\n'), 'OpenGL extensions')[_trim:].split()))
+
+ try:
+ helper(['--verbose', '--api'], ['gles1', 'gles2', 'gles3'])
+ helper(['--verbose', '--api', 'gl', '--profile'],
+ ['core', 'compat', 'none'])
+ except StopWflinfo as e:
+ # Handle wflinfo not being installed by returning an empty set. This
+ # will essentially make FastSkipMixin a no-op.
+ if e.reason == 'OSError':
+ return set()
+ raise
+
+ # Don't return a set with only WFLINFO_GL_ERROR.
+ ret = {e.strip() for e in all_}
+ if ret == {'WFLINFO_GL_ERROR'}:
+ return set()
+ return ret
+
+ @core.lazy_property
+ def gl_version(self):
+ """Calculate the maximum opengl version.
+
+ This will try (in order): core, compat, and finally no profile,
+ stopping when it finds a profile. It assumes that most implementations
+ will have core and compat as equals, or core as superior to compat in
+ terms of support.
+
+ """
+ ret = None
+ for profile in ['core', 'compat', 'none']:
+ try:
+ raw = self.__call_wflinfo(['--api', 'gl', '--profile', profile])
+ except StopWflinfo as e:
+ if e.reason == 'Called':
+ continue
+ elif e.reason == 'OSError':
+ break
+ raise
+ else:
+ try:
+ # Grab the GL version string, trim any release_number values
+ ret = float(self.__getline(
+ raw.split('\n'),
+ 'OpenGL version string').split()[3][:3])
+ except (IndexError, ValueError):
+ # This is caused by wlfinfo returning an error
+ pass
+ break
+ return ret
+
+ @core.lazy_property
+ def gles_version(self):
+ """Calculate the maximum opengl es version.
+
+ The design of this function isn't 100% correct. GLES1 and GLES2+ behave
+ differently, since 2+ can be silently promoted, but 1 cannot. This
+ means that a driver can implement 2, 3, 3.1, etc, but never have 1
+ support.
+
+ I don't think this is a big deal for a couple of reasons. First, piglit
+ has a very small set of GLES1 tests, so they shouldn't have big impact
+ on runtime, and second, the design of the FastSkipMixin is
+ conservative: it would rather run a few tests that should be skipped
+ than skip a few tests that should be run.
+
+ """
+ ret = None
+ for api in ['gles3', 'gles2', 'gles1']:
+ try:
+ raw = self.__call_wflinfo(['--api', api])
+ except StopWflinfo as e:
+ if e.reason == 'Called':
+ continue
+ elif e.reason == 'OSError':
+ break
+ raise
+ else:
+ try:
+ # Yes, search for "OpenGL version string" in GLES
+ # GLES doesn't support patch versions.
+ ret = float(self.__getline(
+ raw.split('\n'),
+ 'OpenGL version string').split()[5])
+ except (IndexError, ValueError):
+ # This is caused by wlfinfo returning an error
+ pass
+ break
+ return ret
+
+ @core.lazy_property
+ def glsl_version(self):
+ """Calculate the maximum OpenGL Shader Language version."""
+ ret = None
+ for profile in ['core', 'compat', 'none']:
+ try:
+ raw = self.__call_wflinfo(
+ ['--verbose', '--api', 'gl', '--profile', profile])
+ except StopWflinfo as e:
+ if e.reason == 'Called':
+ continue
+ elif e.reason == 'OSError':
+ break
+ raise
+ else:
+ try:
+ # GLSL versions are M.mm formatted
+ ret = float(self.__getline(
+ raw.split('\n'),
+ 'OpenGL shading language').split()[-1][:4])
+ except (IndexError, ValueError):
+ # This is caused by wflinfo returning an error
+ pass
+ break
+ return ret
+
+ @core.lazy_property
+ def glsl_es_version(self):
+ """Calculate the maximum OpenGL ES Shader Language version."""
+ ret = None
+ for api in ['gles3', 'gles2']:
+ try:
+ raw = self.__call_wflinfo(['--verbose', '--api', api])
+ except StopWflinfo as e:
+ if e.reason == 'Called':
+ continue
+ elif e.reason == 'OSError':
+ break
+ raise
+ else:
+ try:
+ # GLSL ES version numbering is insane.
+ # For version >= 3 the numbers are 3.00, 3.10, etc.
+ # For version 2, they are 1.0.xx
+ ret = float(self.__getline(
+ raw.split('\n'),
+ 'OpenGL shading language').split()[-1][:3])
+ except (IndexError, ValueError):
+ # Handle wflinfo internal errors
+ pass
+ break
+ return ret
+