1# coding=utf-8
2# Copyright (c) 2015-2016 Intel Corporation
3
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:
10
11# The above copyright notice and this permission notice shall be included in
12# all copies or substantial portions of the Software.
13
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
20# SOFTWARE.
21
22"""Generate tests for variable index writes.
23
24This creates a TestParams object for each invocation (using a factory to reduce
25the number of duplicate objects), and then passes that into a mako template.
26The template then formats that information into a shader_test for either the
27fragment shader stage or the vertex shader stage.
28
29"""
30import copy
31import itertools
32import os
33
34from modules import utils, glsl
35from templates import template_dir
36
37_TEMPLATES = template_dir(os.path.basename(os.path.splitext(__file__)[0]))
38_VS_TEMPLATE = _TEMPLATES.get_template('vs.shader_test.mako')
39_FS_TEMPLATE = _TEMPLATES.get_template('fs.shader_test.mako')
40_DIRNAME = os.path.join('spec', 'glsl-{}', 'execution', 'variable-indexing')
41
42
43class TestParams(object):
44    """Object representing all of the parameters of a single test instance.
45
46    Provides all of the values using lazy properties, which tie in with the
47    ParamsFactory to store all of the values, speeding up run times.
48
49    """
50    def __init__(self, mode, array_dim, matrix_dim, index_value, col,
51                 value_type, glsl_version):
52        # pylint: disable=too-many-arguments
53        assert array_dim in [0, 3]
54        assert matrix_dim in [2, 3, 4]
55
56        self.mode = mode
57        self.array_dim = array_dim
58        self.matrix_dim = matrix_dim
59        self.index_value = index_value
60        self.col = '[{}]'.format(col)
61        self.value_type = value_type
62        self.version = glsl.Version(glsl_version)
63
64    @utils.lazy_property
65    def varying_comps(self):
66        if self.array_dim != 0:
67            return self.matrix_dim**2 * self.array_dim
68        else:
69            return self.matrix_dim**2
70
71    @utils.lazy_property
72    def base_type(self):
73        if int(self.version) >= 120:
74            return 'mat{0}x{0}'.format(self.matrix_dim)
75        else:
76            return 'mat{}'.format(self.matrix_dim)
77
78    @utils.lazy_property
79    def type(self):
80        if self.array_dim != 0 and int(self.version) >= 120:
81            return '{}[{}]'.format(self.base_type, self.array_dim)
82        else:
83            return self.base_type
84
85    @utils.lazy_property
86    def dim(self):
87        if self.array_dim != 0 and int(self.version) < 120:
88            return '[{}]'.format(self.array_dim)
89        else:
90            return ''
91
92    @utils.lazy_property
93    def row(self):
94        if self.value_type == 'float':
95            return '[row]'
96        else:
97            return ''
98
99    @utils.lazy_property
100    def idx(self):
101        if self.array_dim != 0:
102            return '[{}]'.format(self.index_value)
103        else:
104            return ''
105
106    @utils.lazy_property
107    def idx2(self):
108        if self.array_dim != 0:
109            if self.index_value == 'index':
110                return '[{}2]'.format(self.index_value)
111            else:
112                return '[{}]'.format(self.index_value)
113        else:
114            return ''
115
116    @utils.lazy_property
117    def test_vec(self):
118        if self.matrix_dim == 2:
119            return ["0.803161418975390", "0.852987140792140"]
120        elif self.matrix_dim == 3:
121            return ["0.681652305322399", "0.210426138878113",
122                    "0.185916924650237"]
123        elif self.matrix_dim == 4:
124            return ["0.0394868046587045", "0.8922408276905568",
125                    "0.3337495624366961", "0.8732295730825839"]
126
127    @utils.lazy_property
128    def test_exp(self):
129        if self.matrix_dim == 2:
130            return ["0.708718134966688", "1.452243795483797"]
131        elif self.matrix_dim == 3:
132            return ["0.610649606928364", "0.711906885823636",
133                    "0.312244778977868"]
134        elif self.matrix_dim == 4:
135            return ["1.03935908892461", "1.18846180713529", "1.10078681232072",
136                    "1.72434439561820"]
137
138    @utils.lazy_property
139    def test_mat(self):
140        if self.matrix_dim == 2:
141            return [["0.241498998195656", "0.861223395812970"],
142                    ["0.603473877011433", "0.891622340451180"]]
143        elif self.matrix_dim == 3:
144            return [
145                ["0.493944462129466", "0.722190133917966", "0.239853948232558"],
146                ["0.550143078409278", "0.591962645398579", "0.467616286531193"],
147                ["0.850846377186973", "0.511303112962423", "0.270815003356504"]
148            ]
149        elif self.matrix_dim == 4:
150            return [["0.922040144261674", "0.158053783109488",
151                     "0.357016429866574", "0.836368810383957"],
152                    ["0.560251913703792", "0.171634921595771",
153                     "0.602494709909111", "0.693273570571311"],
154                    ["0.350720358904176", "0.912192627475775",
155                     "0.688544081259531", "0.913891056231967"],
156                    ["0.442058176039301", "0.829835836794679",
157                     "0.365674411003021", "0.879197364462782"]]
158
159    @utils.lazy_property
160    def test_sizes(self):
161        if self.array_dim == 0:
162            return [1]
163        elif self.index_value == 'index':
164            return list(range(1, self.array_dim + 1))
165        else:
166            return [2]
167
168    @utils.lazy_property
169    def test_columns(self):
170        if self.col == '[col]':
171            return list(range(1, self.matrix_dim + 1))
172        return [2]
173
174    @utils.lazy_property
175    def test_rows(self):
176        if self.value_type == 'float':
177            return list(range(1, self.matrix_dim + 1))
178        return [1]
179
180    @utils.lazy_property
181    def test_type(self):
182        # shader_runner always uses matDxD format
183        return 'mat{0}x{0}'.format(self.matrix_dim)
184
185    def test_matrix(self, column, row):
186        """Generate the matrix used in a test section.
187
188        This will take the matrix used by the test, and replace specific values
189        with sentinal values, and return the matrix as a string.
190
191        """
192        bad = ['666.0', '777.0', '888.0', '999.0']
193        mat = copy.deepcopy(self.test_mat)
194
195        if self.value_type == 'float':
196            mat[column][row] = bad[0]
197        else:
198            mat[column] = bad[0:self.matrix_dim]
199
200        ret = ''
201        for c in mat:
202            ret += ' '.join(c)
203            ret += ' '
204        return ret
205
206    @utils.lazy_property
207    def formated_version(self):
208        # Note: GLSLVersion.float() does division by 100
209        return '{:.2f}'.format(float(self.version))
210
211
212class ParamsFactory(object):  # pylint: disable=too-few-public-methods
213    """A factory class that provides TestParam objects.
214
215    This cuts the number of new objects created by roughly 3/5.
216
217    """
218    def __init__(self):
219        self.__stored = {}
220
221    def get(self, *args):
222        args = tuple(args)
223        try:
224            return self.__stored[args]
225        except KeyError:
226            params = TestParams(*args)
227            self.__stored[args] = params
228            return params
229
230
231def make_vs(name, params):
232    """Create a vertex shader test."""
233    dirname = _DIRNAME.format(params.formated_version)
234    utils.safe_makedirs(dirname)
235    with open(os.path.join(dirname, name), 'w') as f:
236        f.write(_VS_TEMPLATE.render_unicode(params=params))
237    print(name)
238
239
240def make_fs(name, params):
241    """Create a fragment shader test."""
242    dirname = _DIRNAME.format(params.formated_version)
243    utils.safe_makedirs(dirname)
244    with open(os.path.join(dirname, name), 'w') as f:
245        f.write(_FS_TEMPLATE.render_unicode(params=params))
246    print(name)
247
248
249def main():
250    """The main function."""
251    # Various choices that can be put together to produce a single test.
252    iter_ = itertools.product(
253        ['110', '120'],       # GLSL versions
254        [0, 3],               # Array dimensions
255        [2, 3, 4],            # Matrix dimensions
256        ['varying', 'temp'],  # modes
257        ['col', 1],           # columns
258        ['fs', 'vs'],         # shader stages
259    )
260
261    factory = ParamsFactory()
262
263    # This can be filled in to produce the file name for the test.
264    # Note that idx, col, row, and arr will need to have a '-' added to the end
265    # of the value if it is not empty
266    name = '{stage}-{mode}-{arr}mat{matrix_dim}-{idx}{col}{row}wr.shader_test'
267
268    for v, a, d, m, c, s in iter_:
269        for t in ['float', 'vec{}'.format(d)]:
270            if s == 'vs':
271                func = make_vs
272            elif s == 'fs':
273                if m == 'varying':
274                    # Fragment shaders cannot write varyings
275                    continue
276                func = make_fs
277
278            if a != 0:
279                arr = 'array-'
280
281                func(
282                    name.format(stage=s,
283                                mode=m,
284                                matrix_dim=d,
285                                arr=arr,
286                                idx='',
287                                col='col-' if c == 'col' else '',
288                                row='row-' if t == 'float' else ''),
289                    factory.get(m, a, d, 1, c, t, v))
290            else:
291                arr = ''
292
293            func(
294                name.format(stage=s,
295                            mode=m,
296                            matrix_dim=d,
297                            arr=arr,
298                            idx='index-' if a != 0 else '',
299                            col='col-' if c == 'col' else '',
300                            row='row-' if t == 'float' else ''),
301                factory.get(m, a, d, 'index', c, t, v))
302
303
304if __name__ == '__main__':
305    main()
306