tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

es3fShaderPackingFunctionTests.js (36071B)


      1 /*-------------------------------------------------------------------------
      2 * drawElements Quality Program OpenGL ES Utilities
      3 * ------------------------------------------------
      4 *
      5 * Copyright 2014 The Android Open Source Project
      6 *
      7 * Licensed under the Apache License, Version 2.0 (the "License");
      8 * you may not use this file except in compliance with the License.
      9 * You may obtain a copy of the License at
     10 *
     11 *      http://www.apache.org/licenses/LICENSE-2.0
     12 *
     13 * Unless required by applicable law or agreed to in writing, software
     14 * distributed under the License is distributed on an "AS IS" BASIS,
     15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16 * See the License for the specific language governing permissions and
     17 * limitations under the License.
     18 *
     19 */
     20 
     21 'use strict';
     22 goog.provide('functional.gles3.es3fShaderPackingFunctionTests');
     23 goog.require('framework.common.tcuFloat');
     24 goog.require('framework.common.tcuMatrixUtil');
     25 goog.require('framework.common.tcuTestCase');
     26 goog.require('framework.delibs.debase.deMath');
     27 goog.require('framework.delibs.debase.deRandom');
     28 goog.require('framework.delibs.debase.deString');
     29 goog.require('framework.opengl.gluShaderProgram');
     30 goog.require('framework.opengl.gluShaderUtil');
     31 goog.require('framework.opengl.gluVarType');
     32 goog.require('modules.shared.glsShaderExecUtil');
     33 
     34 
     35 
     36 goog.scope(function() {
     37 var es3fShaderPackingFunctionTests = functional.gles3.es3fShaderPackingFunctionTests;
     38 var tcuFloat = framework.common.tcuFloat;
     39 var tcuTestCase = framework.common.tcuTestCase;
     40 var deMath = framework.delibs.debase.deMath;
     41 var deRandom = framework.delibs.debase.deRandom;
     42 var deString = framework.delibs.debase.deString;
     43 var gluShaderProgram = framework.opengl.gluShaderProgram;
     44 var gluShaderUtil = framework.opengl.gluShaderUtil;
     45 var gluVarType = framework.opengl.gluVarType;
     46 var glsShaderExecUtil = modules.shared.glsShaderExecUtil;
     47 var tcuMatrixUtil = framework.common.tcuMatrixUtil;
     48 /**
     49  * @param {number} a
     50  * @param {number} b
     51  * @return {number}
     52  */
     53 es3fShaderPackingFunctionTests.getUlpDiff = function(a, b) {
     54 	/** @type {number} */ var aBits	= tcuFloat.newFloat32(a).bits();
     55 	/** @type {number} */ var bBits	= tcuFloat.newFloat32(b).bits();
     56 	return aBits > bBits ? aBits - bBits : bBits - aBits;
     57 };
     58 
     59 /**
     60  * @constructor
     61  * @extends {tcuTestCase.DeqpTest}
     62  * @param {string} name
     63  * @param {string} description
     64  * @param {gluShaderProgram.shaderType} shaderType
     65  */
     66 es3fShaderPackingFunctionTests.ShaderPackingFunctionCase = function(name, description, shaderType) {
     67 	tcuTestCase.DeqpTest.call(this, name, description);
     68 	/** @type {gluShaderProgram.shaderType} */ this.m_shaderType = shaderType;
     69 	/** @type {?glsShaderExecUtil.ShaderSpec} */ this.m_spec = new glsShaderExecUtil.ShaderSpec();
     70 	/** @type {?glsShaderExecUtil.ShaderExecutor} */ this.m_executor = null;
     71 };
     72 
     73 es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
     74 es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype.constructor = es3fShaderPackingFunctionTests.ShaderPackingFunctionCase;
     75 
     76 es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype.init = function() {
     77 	assertMsgOptions(!this.m_executor, 'Error: Executor is not null.', false, true);
     78 	this.m_executor = glsShaderExecUtil.createExecutor(this.m_shaderType, this.m_spec);
     79 	if (!this.m_executor.isOk())
     80 		throw new Error('Compile failed');
     81 };
     82 
     83 es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype.deinit = function() {
     84 	this.m_executor = null;
     85 };
     86 
     87 /**
     88  *	@param {gluShaderUtil.precision} precision
     89  * @return {string}
     90  */
     91 es3fShaderPackingFunctionTests.getPrecisionPostfix = function(precision) {
     92 	/** @type {Array<string>} */ var s_postfix = [
     93 		'_lowp',
     94 		'_mediump',
     95 		'_highp'
     96 	];
     97 	assertMsgOptions(0 <= precision && precision < s_postfix.length, 'Error: Out of range', false, true);
     98 	return s_postfix[precision];
     99 };
    100 
    101 /**
    102  *	@param {gluShaderProgram.shaderType} shaderType
    103  * @return {string}
    104  */
    105 es3fShaderPackingFunctionTests.getShaderTypePostfix = function(shaderType) {
    106 	/** @type {Array<string>} */ var s_postfix = [
    107 		'_vertex',
    108 		'_fragment'
    109 	];
    110 	assertMsgOptions(0 <= shaderType && shaderType < s_postfix.length, 'Error Out of range', false, true);
    111 	return s_postfix[shaderType];
    112 };
    113 
    114 /**
    115  * @constructor
    116  * @extends {es3fShaderPackingFunctionTests.ShaderPackingFunctionCase}
    117  * @param {gluShaderProgram.shaderType} shaderType
    118  * @param {gluShaderUtil.precision} precision
    119  */
    120 es3fShaderPackingFunctionTests.PackSnorm2x16Case = function(shaderType, precision) {
    121 	/** @const {string} */ var name = 'packsnorm2x16' +
    122 		es3fShaderPackingFunctionTests.getPrecisionPostfix(precision) +
    123 		es3fShaderPackingFunctionTests.getShaderTypePostfix(shaderType);
    124 	es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.call(this, name, 'packSnorm2x16', shaderType);
    125 	this.m_precision = precision;
    126 
    127 	this.m_spec.inputs.push(new glsShaderExecUtil.Symbol('in0', gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC2, precision)));
    128 	this.m_spec.outputs.push(new glsShaderExecUtil.Symbol('out0', gluVarType.newTypeBasic(gluShaderUtil.DataType.UINT, gluShaderUtil.precision.PRECISION_HIGHP)));
    129 	this.m_spec.source = 'out0 = packSnorm2x16(in0);';
    130 };
    131 
    132 es3fShaderPackingFunctionTests.PackSnorm2x16Case.prototype = Object.create(es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype);
    133 es3fShaderPackingFunctionTests.PackSnorm2x16Case.prototype.constructor = es3fShaderPackingFunctionTests.PackSnorm2x16Case;
    134 
    135 /**
    136  * @return {tcuTestCase.IterateResult}
    137  */
    138 es3fShaderPackingFunctionTests.PackSnorm2x16Case.prototype.iterate = function() {
    139 	/** @type {deRandom.Random} */ var rnd = new deRandom.Random(deString.deStringHash(this.name) ^ 0x776002);
    140 	/** @type {Array<Array<number>>} */ var inputs = [];
    141 	/** @type {goog.TypedArray} */ var outputs; // deUint32
    142 	/** @type {goog.TypedArray} */ var shaderExecutorOutput;
    143 	/** @type {number} */ var maxDiff = this.m_precision == gluShaderUtil.precision.PRECISION_HIGHP ? 1 : // Rounding only.
    144 										this.m_precision == gluShaderUtil.precision.PRECISION_MEDIUMP ? 33 : // (2^-10) * (2^15) + 1
    145 										this.m_precision == gluShaderUtil.precision.PRECISION_LOWP ? 129 : 0; // (2^-8) * (2^15) + 1
    146 	/** @type {number} */ var x;
    147 	/** @type {number} */ var y;
    148 	// Special values to check.
    149 	inputs.push([0.0, 0.0]);
    150 	inputs.push([-1.0, 1.0]);
    151 	inputs.push([0.5, -0.5]);
    152 	inputs.push([-1.5, 1.5]);
    153 	inputs.push([0.25, -0.75]);
    154 
    155 	// Random values, mostly in range.
    156 	for (var ndx = 0; ndx < 15; ndx++) {
    157 		x = rnd.getFloat() * 2.5 - 1.25;
    158 		y = rnd.getFloat() * 2.5 - 1.25;
    159 		inputs.push([x, y]);
    160 	}
    161 
    162 	// Large random values.
    163 	for (var ndx = 0; ndx < 80; ndx++) {
    164 		x = rnd.getFloat() * 1e6 - 0.5e6;
    165 		y = rnd.getFloat() * 1e6 - 0.5e6;
    166 		inputs.push([x, y]);
    167 	}
    168 
    169 	bufferedLogToConsole('Executing shader for ' + inputs.length + ' input values');
    170 
    171 	this.m_executor.useProgram();
    172 	shaderExecutorOutput = this.m_executor.execute(inputs.length, [tcuMatrixUtil.flatten(inputs)])[0];
    173 
    174 	// Convert outputs if we get them as Uint8Array.
    175 	// - VertexShaderExecutor.execute() returns either an array of Uint8Array
    176 	// - FragmentShaderExecutor.execute() returns either an array of Uint8Array or Uint32Array
    177     outputs = new Uint32Array(shaderExecutorOutput.buffer);
    178 
    179 	// Verify
    180 	/** @type {number} */ var numValues = inputs.length;
    181 	/** @type {number} */ var maxPrints = 10;
    182 	/** @type {number} */ var numFailed = 0;
    183 
    184 	for (var valNdx = 0; valNdx < numValues; valNdx++) {
    185 		/** @type {number} */ var ref0 = (deMath.clamp(Math.floor(deMath.clamp(inputs[valNdx][0], -1.0, 1.0) * 32767.0), -(1 << 15), (1 << 15) - 1)) & 0xFFFF;
    186 		/** @type {number} */ var ref1 = (deMath.clamp(Math.floor(deMath.clamp(inputs[valNdx][1], -1.0, 1.0) * 32767.0), -(1 << 15), (1 << 15) - 1)) & 0xFFFF;
    187 		/** @type {number} */ var ref = (ref1 << 16) | ref0;
    188 		/** @type {number} */ var res = outputs[valNdx];
    189 		/** @type {number} */ var res0 = (res & 0xffff);
    190 		/** @type {number} */ var res1 = deMath.shiftRight(res, 16);
    191 		/** @type {number} */ var diff0 = Math.abs(ref0 - res0);
    192 		/** @type {number} */ var diff1 = Math.abs(ref1 - res1);
    193 
    194 		if (diff0 > maxDiff || diff1 > maxDiff) {
    195 			if (numFailed < maxPrints) {
    196 				bufferedLogToConsole(
    197 					'ERROR: Mismatch in value ' + valNdx +
    198 					', expected packSnorm2x16(' + inputs[valNdx] + ') = ' + ref + //tcu::toHex(ref)
    199 					', got ' + res + // tcu::toHex(res)
    200 					'\n  diffs = (' + diff0 + ', ' + diff1 + '), max diff = ' + maxDiff);
    201 			}
    202 			else if (numFailed == maxPrints)
    203 				bufferedLogToConsole('...');
    204 
    205 			numFailed += 1;
    206 		}
    207 	}
    208 
    209 	bufferedLogToConsole((numValues - numFailed) + ' / ' + numValues + ' values passed');
    210 
    211 	/** @type {boolean} */ var isOk = numFailed === 0;
    212 	if (!isOk)
    213 		testFailedOptions('Result comparison failed', false);
    214        else
    215            testPassedOptions('Pass', true);
    216 
    217 	return tcuTestCase.IterateResult.STOP;
    218 };
    219 
    220 
    221 /**
    222  * @constructor
    223  * @extends {es3fShaderPackingFunctionTests.ShaderPackingFunctionCase}
    224  * @param {gluShaderProgram.shaderType} shaderType
    225  */
    226 es3fShaderPackingFunctionTests.UnpackSnorm2x16Case = function(shaderType) {
    227 	/** @const {string} */ var name = 'unpacksnorm2x16' + es3fShaderPackingFunctionTests.getShaderTypePostfix(shaderType);
    228 	es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.call(this, name, 'unpackSnorm2x16', shaderType);
    229 
    230 	this.m_spec.inputs.push(new glsShaderExecUtil.Symbol('in0', gluVarType.newTypeBasic(gluShaderUtil.DataType.UINT, gluShaderUtil.precision.PRECISION_HIGHP)));
    231 	this.m_spec.outputs.push(new glsShaderExecUtil.Symbol('out0', gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC2, gluShaderUtil.precision.PRECISION_HIGHP)));
    232 	this.m_spec.source = 'out0 = unpackSnorm2x16(in0);';
    233 };
    234 
    235 es3fShaderPackingFunctionTests.UnpackSnorm2x16Case.prototype = Object.create(es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype);
    236 es3fShaderPackingFunctionTests.UnpackSnorm2x16Case.prototype.constructor = es3fShaderPackingFunctionTests.UnpackSnorm2x16Case;
    237 
    238 /**
    239  * @return {tcuTestCase.IterateResult}
    240  */
    241 es3fShaderPackingFunctionTests.UnpackSnorm2x16Case.prototype.iterate = function() {
    242 	/** @type {number} */ var maxDiff = 1; // Rounding error.
    243 	/** @type {deRandom.Random} */ var rnd = new deRandom.Random(deString.deStringHash(this.name) ^ 0x776002);
    244 	/** @type {Array<number>} */ var inputs = [];
    245 	/** @type {goog.TypedArray} */ var shaderExecutorOutput; //vector<vec2<float>>
    246 	/** @type {goog.TypedArray} */ var outputs; //vector<vec2<float>>
    247 
    248 	inputs.push(0x00000000);
    249 	inputs.push(0x7fff8000);
    250 	inputs.push(0x80007fff);
    251 	inputs.push(0xffffffff);
    252 	inputs.push(0x0001fffe);
    253 
    254 	// Random values.
    255 	for (var ndx = 0; ndx < 95; ndx++)
    256 		inputs.push(rnd.getInt());
    257 
    258 	bufferedLogToConsole('Executing shader for ' + inputs.length + ' input values');
    259 
    260 	this.m_executor.useProgram();
    261 	shaderExecutorOutput = this.m_executor.execute(inputs.length, [inputs])[0]; // This test case only has one output
    262 
    263 	// Convert outputs if we get them as Uint8Array.
    264 	// - VertexShaderExecutor.execute() returns either an array of Uint8Array
    265 	// - FragmentShaderExecutor.execute() returns either an array of Uint8Array or Uint32Array
    266 	outputs = new Float32Array(shaderExecutorOutput.buffer);
    267 
    268 	// Verify
    269 	/** @type {number} */ var numValues = inputs.length;
    270 	/** @type {number} */ var maxPrints = 10;
    271 	/** @type {number} */ var numFailed = 0;
    272 
    273 	for (var valNdx = 0; valNdx < inputs.length; valNdx++) {
    274 		/** @type {number} */ var in0 = Math.floor(inputs[valNdx] & 0xffff);
    275 		// Convert 16-bit uint to 16-bit int
    276 		var view = new DataView(new ArrayBuffer(4));
    277 		view.setUint16(0, in0, true);
    278 		in0 = view.getInt16(0, true);
    279 		/** @type {number} */ var in1 = Math.floor(deMath.shiftRight(inputs[valNdx], 16));
    280 		// Convert 16-bit uint to 16-bit int
    281 		var view = new DataView(new ArrayBuffer(4));
    282 		view.setUint16(0, in1, true);
    283 		in1 = view.getInt16(0, true);
    284 		/** @type {number} */ var ref0 = deMath.clamp(in0 / 32767., -1.0, 1.0);
    285 		/** @type {number} */ var ref1 = deMath.clamp(in1 / 32767., -1.0, 1.0);
    286 		/** @type {number} */ var res0 = outputs[2 * valNdx];
    287 		/** @type {number} */ var res1 = outputs[2 * valNdx + 1];
    288 
    289 		/** @type {number} */ var diff0 = es3fShaderPackingFunctionTests.getUlpDiff(ref0, res0);
    290 		/** @type {number} */ var diff1 = es3fShaderPackingFunctionTests.getUlpDiff(ref1, res1);
    291 
    292 		if (diff0 > maxDiff || diff1 > maxDiff) {
    293 			if (numFailed < maxPrints)
    294 				bufferedLogToConsole('ERROR: Mismatch in value ' + valNdx + ',\n' +
    295 			    	'  expected unpackSnorm2x16(' + inputs[valNdx].toString(16) + ') = ' +
    296 			    	'vec2(' + ref0.toString(16) + ', ' + ref1.toString(16) + ')' +
    297 			    	', got vec2(' + res0.toString(16) + ', ' + res1.toString(16) + ')' +
    298 			    	'\n  ULP diffs = (' + diff0 + ', ' + diff1 + '), max diff = ' + maxDiff);
    299 			else if (numFailed == maxPrints)
    300 				bufferedLogToConsole('...');
    301 
    302 			numFailed += 1;
    303 		}
    304 	}
    305 
    306 	bufferedLogToConsole((numValues - numFailed) + ' / ' + numValues + ' values passed');
    307 
    308 	/** @type {boolean} */ var isOk = numFailed === 0;
    309 	if (!isOk)
    310 		testFailedOptions('Result comparison failed', false);
    311 	else
    312 		testPassedOptions('Pass', true);
    313 
    314 	return tcuTestCase.IterateResult.STOP;
    315 };
    316 
    317 /**
    318  * @constructor
    319  * @extends {es3fShaderPackingFunctionTests.ShaderPackingFunctionCase}
    320  * @param {gluShaderProgram.shaderType} shaderType
    321  * @param {gluShaderUtil.precision} precision
    322  */
    323 es3fShaderPackingFunctionTests.PackUnorm2x16Case = function(shaderType, precision) {
    324 	/** @const {string} */ var name = 'packunorm2x16' +
    325 	 	es3fShaderPackingFunctionTests.getPrecisionPostfix(precision) +
    326 		es3fShaderPackingFunctionTests.getShaderTypePostfix(shaderType);
    327 	es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.call(this, name, 'packUnorm2x16', shaderType);
    328 	this.m_precision = precision;
    329 
    330 	this.m_spec.inputs.push(new glsShaderExecUtil.Symbol('in0', gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC2, precision)));
    331 	this.m_spec.outputs.push(new glsShaderExecUtil.Symbol('out0', gluVarType.newTypeBasic(gluShaderUtil.DataType.UINT, gluShaderUtil.precision.PRECISION_HIGHP)));
    332 	this.m_spec.source = 'out0 = packUnorm2x16(in0);';
    333 };
    334 
    335 es3fShaderPackingFunctionTests.PackUnorm2x16Case.prototype = Object.create(es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype);
    336 es3fShaderPackingFunctionTests.PackUnorm2x16Case.prototype.constructor = es3fShaderPackingFunctionTests.PackUnorm2x16Case;
    337 
    338 /**
    339  * @return {tcuTestCase.IterateResult}
    340  */
    341 es3fShaderPackingFunctionTests.PackUnorm2x16Case.prototype.iterate = function() {
    342 	/** @type {deRandom.Random} */ var rnd = new deRandom.Random(deString.deStringHash(this.name) ^ 0x776002);
    343 	/** @type {Array<Array<number>>} */ var inputs = [];
    344 	/** @type {goog.TypedArray} */ var shaderExecutorOutput;
    345 	/** @type {goog.TypedArray} */ var outputs; // deUint32
    346 	/** @type {number} */ var maxDiff = this.m_precision == gluShaderUtil.precision.PRECISION_HIGHP ? 1 : // Rounding only.
    347 										this.m_precision == gluShaderUtil.precision.PRECISION_MEDIUMP ? 65 : // (2^-10) * (2^16) + 1
    348 										this.m_precision == gluShaderUtil.precision.PRECISION_LOWP ? 257 : 0; // (2^-8) * (2^16) + 1
    349 	/** @type {number} */ var x;
    350 	/** @type {number} */ var y;
    351 	// Special values to check.
    352 	inputs.push([0.0, 0.0]);
    353 	inputs.push([0.5, 1.0]);
    354 	inputs.push([1.0, 0.5]);
    355 	inputs.push([-0.5, 1.5]);
    356 	inputs.push([0.25, 0.75]);
    357 
    358 	// Random values, mostly in range.
    359 	for (var ndx = 0; ndx < 15; ndx++) {
    360 		x = rnd.getFloat() * 1.25;
    361 		y = rnd.getFloat() * 1.25;
    362 		inputs.push([x, y]);
    363 	}
    364 
    365 	// Large random values.
    366 	for (var ndx = 0; ndx < 80; ndx++) {
    367 		x = rnd.getFloat() * 1e6 - 1e5;
    368 		y = rnd.getFloat() * 1e6 - 1e5;
    369 		inputs.push([x, y]);
    370 	}
    371 
    372 	bufferedLogToConsole('Executing shader for ' + inputs.length + ' input values');
    373 
    374 	this.m_executor.useProgram();
    375 	shaderExecutorOutput  = this.m_executor.execute(inputs.length, [tcuMatrixUtil.flatten(inputs)])[0];
    376 
    377 	// Convert outputs if we get them as Uint8Array.
    378 	// - VertexShaderExecutor.execute() returns either an array of Uint8Array
    379 	// - FragmentShaderExecutor.execute() returns either an array of Uint8Array or Uint32Array
    380     outputs = new Uint32Array(shaderExecutorOutput.buffer);
    381 
    382 	// Verify
    383 	/** @type {number} */ var numValues = inputs.length;
    384 	/** @type {number} */ var maxPrints = 10;
    385 	/** @type {number} */ var numFailed = 0;
    386 
    387 	for (var valNdx = 0; valNdx < inputs.length; valNdx++) {
    388 		/** @type {number} */ var ref0 = deMath.clamp(Math.floor(deMath.clamp(inputs[valNdx][0], 0.0, 1.0) * 65535.0), 0, (1 << 16) - 1) & 0xFFFF;
    389 		/** @type {number} */ var ref1 = deMath.clamp(Math.floor(deMath.clamp(inputs[valNdx][1], 0.0, 1.0) * 65535.0), 0, (1 << 16) - 1) & 0xFFFF;
    390 		/** @type {number} */ var ref = (ref1 << 16) | ref0;
    391 		/** @type {number} */ var res = outputs[valNdx];
    392 		/** @type {number} */ var res0 = (res & 0xffff);
    393 		/** @type {number} */ var res1 = deMath.shiftRight(res, 16);
    394 		/** @type {number} */ var diff0 = Math.abs(ref0 - res0);
    395 		/** @type {number} */ var diff1 = Math.abs(ref1 - res1);
    396 
    397 		if (diff0 > maxDiff || diff1 > maxDiff) {
    398 			if (numFailed < maxPrints)
    399 				bufferedLogToConsole('ERROR: Mismatch in value ' + valNdx +
    400 									 ', expected packUnorm2x16(' + inputs[valNdx] + ') = ' + ref /*tcu::toHex(ref)*/ +
    401 									 ', got ' + res /*tcu::toHex(res)*/ +
    402 									 '\n  diffs = (' + diff0 + ', ' + diff1 + '), max diff = ' + maxDiff);
    403 			else if (numFailed === maxPrints)
    404 				bufferedLogToConsole('...');
    405 
    406 			numFailed += 1;
    407 		}
    408 	}
    409 
    410 	bufferedLogToConsole((numValues - numFailed) + ' / ' + numValues + ' values passed');
    411 
    412 	/** @type {boolean} */ var isOk = numFailed === 0;
    413 	if (!isOk)
    414 		testFailedOptions('Result comparison failed', false);
    415 	else
    416 		testPassedOptions('Pass', true);
    417 
    418 	return tcuTestCase.IterateResult.STOP;
    419 };
    420 
    421 /**
    422  * @constructor
    423  * @extends {es3fShaderPackingFunctionTests.ShaderPackingFunctionCase}
    424  * @param {gluShaderProgram.shaderType} shaderType
    425  */
    426 es3fShaderPackingFunctionTests.UnpackUnorm2x16Case = function(shaderType) {
    427 	/** @const {string} */ var name = 'unpackunorm2x16' +
    428 		es3fShaderPackingFunctionTests.getShaderTypePostfix(shaderType);
    429 	es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.call(this, name, 'unpackUnorm2x16', shaderType);
    430 
    431 	this.m_spec.inputs.push(new glsShaderExecUtil.Symbol('in0', gluVarType.newTypeBasic(gluShaderUtil.DataType.UINT, gluShaderUtil.precision.PRECISION_HIGHP)));
    432 	this.m_spec.outputs.push(new glsShaderExecUtil.Symbol('out0', gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC2, gluShaderUtil.precision.PRECISION_HIGHP)));
    433 	this.m_spec.source = 'out0 = unpackUnorm2x16(in0);';
    434 };
    435 
    436 es3fShaderPackingFunctionTests.UnpackUnorm2x16Case.prototype = Object.create(es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype);
    437 es3fShaderPackingFunctionTests.UnpackUnorm2x16Case.prototype.constructor = es3fShaderPackingFunctionTests.UnpackUnorm2x16Case;
    438 
    439 /**
    440  * @return {tcuTestCase.IterateResult}
    441  */
    442 es3fShaderPackingFunctionTests.UnpackUnorm2x16Case.prototype.iterate = function() {
    443 	/** @type {number} */ var maxDiff = 1; // Rounding error.
    444 	/** @type {deRandom.Random} */ var rnd = new deRandom.Random(deString.deStringHash(this.name) ^ 0x776002);
    445 	/** @type {Array<number>} */ var inputs = [];
    446 	/** @type {goog.TypedArray} */ var shaderExecutorOutput;
    447 	/** @type {goog.TypedArray} */ var outputs; //vector<vec2>
    448 
    449 	inputs.push(0x00000000);
    450 	inputs.push(0x7fff8000);
    451 	inputs.push(0x80007fff);
    452 	inputs.push(0xffffffff);
    453 	inputs.push(0x0001fffe);
    454 
    455 	// Random values.
    456 	for (var ndx = 0; ndx < 95; ndx++)
    457 		inputs.push(rnd.getInt());
    458 
    459 	bufferedLogToConsole('Executing shader for ' + inputs.length + ' input values');
    460 
    461 	this.m_executor.useProgram();
    462 	shaderExecutorOutput  = this.m_executor.execute(inputs.length, [inputs])[0];
    463 
    464 	// Convert outputs if we get them as Uint8Array.
    465 	// - VertexShaderExecutor.execute() returns either an array of Uint8Array
    466 	// - FragmentShaderExecutor.execute() returns either an array of Uint8Array or Uint32Array
    467 	outputs = new Float32Array(shaderExecutorOutput.buffer);
    468 
    469 	// Verify
    470 	/** @type {number} */ var numValues = inputs.length;
    471 	/** @type {number} */ var maxPrints = 10;
    472 	/** @type {number} */ var numFailed = 0;
    473 
    474 	for (var valNdx = 0; valNdx < inputs.length; valNdx++) {
    475 		/** @type {number} */ var in0 = Math.floor(inputs[valNdx] & 0xffff);
    476 		/** @type {number} */ var in1 = Math.floor(deMath.shiftRight(inputs[valNdx], 16));
    477 		/** @type {number} */ var ref0 = in0 / 65535.0;
    478 		/** @type {number} */ var ref1 = in1 / 65535.0;
    479 		/** @type {number} */ var res0 = outputs[2 * valNdx];
    480 		/** @type {number} */ var res1 = outputs[2 * valNdx + 1];
    481 
    482 		/** @type {number} */ var diff0 = es3fShaderPackingFunctionTests.getUlpDiff(ref0, res0);
    483 		/** @type {number} */ var diff1 = es3fShaderPackingFunctionTests.getUlpDiff(ref1, res1);
    484 
    485 		if (diff0 > maxDiff || diff1 > maxDiff) {
    486 			if (numFailed < maxPrints)
    487 				bufferedLogToConsole('ERROR: Mismatch in value ' + valNdx + ',\n' +
    488 								     '  expected unpackUnorm2x16(' + inputs[valNdx].toString(16) + ') = ' +
    489 								     'vec2(' + ref0.toString(16) + ', ' + ref1.toString(16) + ')' +
    490 								     ', got vec2(' + res0.toString(16) + ', ' + res1.toString(16) + ')' +
    491 								     '\n  ULP diffs = (' + diff0 + ', ' + diff1 + '), max diff = ' + maxDiff);
    492 			else if (numFailed === maxPrints)
    493 				bufferedLogToConsole('...');
    494 
    495 			numFailed += 1;
    496 		}
    497 	}
    498 
    499 	bufferedLogToConsole((numValues - numFailed) + ' / ' + numValues + ' values passed');
    500 
    501 	/** @type {boolean} */ var isOk = numFailed === 0;
    502 	if (!isOk)
    503 		testFailedOptions('Result comparison failed', false);
    504 	else
    505 		testPassedOptions('Pass', true);
    506 
    507 	return tcuTestCase.IterateResult.STOP;
    508 };
    509 
    510 /**
    511  * @constructor
    512  * @extends {es3fShaderPackingFunctionTests.ShaderPackingFunctionCase}
    513  * @param {gluShaderProgram.shaderType} shaderType
    514  */
    515 es3fShaderPackingFunctionTests.PackHalf2x16Case = function(shaderType) {
    516 	/** @const {string} */ var name = 'packhalf2x16' +
    517 		es3fShaderPackingFunctionTests.getShaderTypePostfix(shaderType);
    518 	es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.call(this, name, 'packHalf2x16', shaderType);
    519 
    520 	this.m_spec.inputs.push(new glsShaderExecUtil.Symbol('in0', gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC2, gluShaderUtil.precision.PRECISION_HIGHP)));
    521 	this.m_spec.outputs.push(new glsShaderExecUtil.Symbol('out0', gluVarType.newTypeBasic(gluShaderUtil.DataType.UINT, gluShaderUtil.precision.PRECISION_HIGHP)));
    522 	this.m_spec.source = 'out0 = packHalf2x16(in0);';
    523 };
    524 
    525 es3fShaderPackingFunctionTests.PackHalf2x16Case.prototype = Object.create(es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype);
    526 es3fShaderPackingFunctionTests.PackHalf2x16Case.prototype.constructor = es3fShaderPackingFunctionTests.PackHalf2x16Case;
    527 
    528 /**
    529  * @return {tcuTestCase.IterateResult}
    530  */
    531 es3fShaderPackingFunctionTests.PackHalf2x16Case.prototype.iterate = function() {
    532 	/** @type {number} */ var maxDiff = 0; // Values can be represented exactly in mediump.
    533 	/** @type {deRandom.Random} */ var rnd = new deRandom.Random(deString.deStringHash(this.name) ^ 0x776002);
    534 	/** @type {Array<Array<number>>} */ var inputs = [];
    535 	/** @type {goog.TypedArray} */ var shaderExecutorOutput;
    536 	/** @type {goog.TypedArray} */ var outputs; // deUint32
    537 
    538 	// Special values to check.
    539 	inputs.push([0.0, 0.0]);
    540 	inputs.push([0.5, 1.0]);
    541 	inputs.push([1.0, 0.5]);
    542 	inputs.push([-0.5, 1.5]);
    543 	inputs.push([0.25, 0.75]);
    544 
    545 	// Random values.
    546 	/** @type {number} */ var minExp = -14;
    547 	/** @type {number} */ var maxExp = 15;
    548 
    549 	/** @type {Array<number>} */ var v = [];
    550 	for (var ndx = 0; ndx < 95; ndx++) {
    551 		for (var c = 0; c < 2; c++) {
    552 			/** @type {number} */ var s = rnd.getBool() ? 1 : -1;
    553 			/** @type {number} */ var exp = rnd.getInt(minExp, maxExp);
    554 			/** @type {number} */ var mantissa = rnd.getInt(0) & ((1 << 23) - 1);
    555 
    556 			v[c] = (new tcuFloat.deFloat()).construct(s, exp ? exp : 1 /* avoid denormals */, (1 << 23) | mantissa).getValue();
    557 		}
    558 		inputs.push(v);
    559 	}
    560 
    561 	// Convert input values to fp16 and back to make sure they can be represented exactly in mediump.
    562 	for (var inVal in inputs)
    563 		inputs[inVal] = [tcuFloat.newFloat16(inputs[inVal][0]).getValue(), tcuFloat.newFloat16(inputs[inVal][1]).getValue()];
    564 
    565 	bufferedLogToConsole('Executing shader for ' + inputs.length + ' input values');
    566 
    567 	this.m_executor.useProgram();
    568 	shaderExecutorOutput  = this.m_executor.execute(inputs.length, [tcuMatrixUtil.flatten(inputs)])[0];
    569 
    570 	// Convert outputs if we get them as Uint8Array.
    571 	// - VertexShaderExecutor.execute() returns either an array of Uint8Array
    572 	// - FragmentShaderExecutor.execute() returns either an array of Uint8Array or Uint32Array
    573     outputs = new Uint32Array(shaderExecutorOutput.buffer);
    574 
    575 	// Verify
    576 	/** @type {number} */ var numValues = inputs.length;
    577 	/** @type {number} */ var maxPrints = 10;
    578 	/** @type {number} */ var numFailed = 0;
    579 
    580 	for (var valNdx = 0; valNdx < inputs.length; valNdx++) {
    581 		/** @type {number} */ var ref0 = tcuFloat.newFloat16(inputs[valNdx][0]).bits();
    582 		/** @type {number} */ var ref1 = tcuFloat.newFloat16(inputs[valNdx][1]).bits();
    583 		/** @type {number} */ var ref = (ref1 << 16) | ref0;
    584 		/** @type {number} */ var res = outputs[valNdx];
    585 		/** @type {number} */ var res0 = (res & 0xffff);
    586 		/** @type {number} */ var res1 = deMath.shiftRight(res, 16);
    587 		/** @type {number} */ var diff0 = Math.abs(ref0 - res0);
    588 		/** @type {number} */ var diff1 = Math.abs(ref1 - res1);
    589 
    590 		if (diff0 > maxDiff || diff1 > maxDiff) {
    591 			if (numFailed < maxPrints)
    592 				bufferedLogToConsole('ERROR: Mismatch in value ' + valNdx +
    593 								     ', expected packHalf2x16(' + inputs[valNdx] + ') = ' + ref /*tcu::toHex(ref)*/ +
    594 								     ', got ' + res /*tcu::toHex(res)*/ +
    595 								     '\n  diffs = (' + diff0 + ', ' + diff1 + '), max diff = ' + maxDiff);
    596 			else if (numFailed == maxPrints)
    597 				bufferedLogToConsole('...');
    598 
    599 			numFailed += 1;
    600 		}
    601 	}
    602 
    603 	bufferedLogToConsole((numValues - numFailed) + ' / ' + numValues + ' values passed');
    604 
    605 	/** @type {boolean} */ var isOk = numFailed === 0;
    606 	if (!isOk)
    607 		testFailedOptions('Result comparison failed', false);
    608 	else
    609 		testPassedOptions('Pass', true);
    610 
    611 	return tcuTestCase.IterateResult.STOP;
    612 
    613 };
    614 
    615 /**
    616  * @constructor
    617  * @extends {es3fShaderPackingFunctionTests.ShaderPackingFunctionCase}
    618  * @param {gluShaderProgram.shaderType} shaderType
    619  */
    620 es3fShaderPackingFunctionTests.UnpackHalf2x16Case = function(shaderType) {
    621 	/** @const {string} */ var name = 'unpackhalf2x16' +
    622 		es3fShaderPackingFunctionTests.getShaderTypePostfix(shaderType);
    623 	es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.call(this, name, 'unpackHalf2x16', shaderType);
    624 
    625 	this.m_spec.inputs.push(new glsShaderExecUtil.Symbol('in0', gluVarType.newTypeBasic(gluShaderUtil.DataType.UINT, gluShaderUtil.precision.PRECISION_HIGHP)));
    626 	this.m_spec.outputs.push(new glsShaderExecUtil.Symbol('out0', gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC2, gluShaderUtil.precision.PRECISION_MEDIUMP)));
    627 	this.m_spec.source = 'out0 = unpackHalf2x16(in0);';
    628 };
    629 
    630 es3fShaderPackingFunctionTests.UnpackHalf2x16Case.prototype = Object.create(es3fShaderPackingFunctionTests.ShaderPackingFunctionCase.prototype);
    631 es3fShaderPackingFunctionTests.UnpackHalf2x16Case.prototype.constructor = es3fShaderPackingFunctionTests.UnpackHalf2x16Case;
    632 
    633 /**
    634  * @return {tcuTestCase.IterateResult}
    635  */
    636 es3fShaderPackingFunctionTests.UnpackHalf2x16Case.prototype.iterate = function() {
    637 	/** @type {number} */ var maxDiff = 0; // All bits must be accurate.
    638 	/** @type {deRandom.Random} */ var rnd = new deRandom.Random(deString.deStringHash(this.name) ^ 0x776002);
    639 	/** @type {Array<number>} */ var inputs = [];
    640 	/** @type {goog.TypedArray} */ var outputs; // vector<vec2<float>>
    641 	/** @type {goog.TypedArray} */ var shaderExecutorOutput;
    642 
    643 	// Special values.
    644 	inputs.push((tcuFloat.newFloat16(0.0).bits() << 16) | tcuFloat.newFloat16(1.0).bits());
    645 	inputs.push((tcuFloat.newFloat16(1.0).bits() << 16) | tcuFloat.newFloat16(0.0).bits());
    646 	inputs.push((tcuFloat.newFloat16(-1.0).bits() << 16) | tcuFloat.newFloat16(0.5).bits());
    647 	inputs.push((tcuFloat.newFloat16(0.5).bits() << 16) | tcuFloat.newFloat16(-0.5).bits());
    648 
    649 	// Construct random values.
    650 	/** @type {number} */ var minExp = -14;
    651 	/** @type {number} */ var maxExp = 15;
    652 	/** @type {number} */ var mantBits = 10;
    653 
    654 	/** @type {number} */ var inVal = 0;
    655 	for (var ndx = 0; ndx < 96; ndx++) {
    656 		for (var c = 0; c < 2; c++) {
    657 			/** @type {number} */ var s = rnd.getBool() ? 1 : -1;
    658 			/** @type {number} */ var exp = rnd.getInt(minExp, maxExp);
    659 			/** @type {number} */ var mantissa = rnd.getInt(0) & ((1 << mantBits) - 1);
    660 			/** @type {number} */ var value = tcuFloat.newFloat16(0).construct(s, exp ? exp : 1 /* avoid denorm */, (1 << 10) | mantissa).bits();
    661 
    662 			inVal |= value << (16 * c);
    663 		}
    664 		inputs.push(inVal);
    665 	}
    666 
    667 	bufferedLogToConsole('Executing shader for ' + inputs.length + ' input values');
    668 
    669 	this.m_executor.useProgram();
    670 	shaderExecutorOutput  = this.m_executor.execute(inputs.length, [inputs])[0];
    671 
    672 	// Convert outputs if we get them as Uint8Array.
    673 	// - VertexShaderExecutor.execute() returns either an array of Uint8Array
    674 	// - FragmentShaderExecutor.execute() returns either an array of Uint8Array or Uint32Array
    675 	outputs = new Float32Array(shaderExecutorOutput.buffer);
    676 
    677 	// Verify
    678 	/** @type {number} */ var numValues = inputs.length
    679 	/** @type {number} */ var maxPrints = 10;
    680 	/** @type {number} */ var numFailed = 0;
    681 
    682 	for (var valNdx = 0; valNdx < inputs.length; valNdx++) {
    683 		/** @type {number} */ var in0 = (inputs[valNdx] & 0xffff);
    684 		/** @type {number} */ var in1 = deMath.shiftRight(inputs[valNdx], 16);
    685 		/** @type {number} */ var ref0 = tcuFloat.halfFloatToNumber(in0);
    686 		/** @type {number} */ var ref1 = tcuFloat.halfFloatToNumber(in1);
    687 		/** @type {number} */ var res0 = outputs[2 * valNdx];
    688 		/** @type {number} */ var res1 = outputs[2 * valNdx + 1];
    689 		/** @type {number} */ var refBits0 = tcuFloat.newFloat32(ref0).bits();
    690 		/** @type {number} */ var refBits1 = tcuFloat.newFloat32(ref1).bits();
    691 		/** @type {number} */ var resBits0 = tcuFloat.newFloat32(res0).bits();
    692 		/** @type {number} */ var resBits1 = tcuFloat.newFloat32(res1).bits();
    693 
    694 		/** @type {number} */ var diff0 = Math.abs(refBits0 - resBits0);
    695 		/** @type {number} */ var diff1 = Math.abs(refBits1 - resBits1);
    696 
    697 		if (isNaN(ref0) && isNaN(res0))
    698 		    diff0 = 0;
    699 		if (isNaN(ref1) && isNaN(res1))
    700 		    diff1 = 0;
    701 
    702 		if (diff0 > maxDiff || diff1 > maxDiff) {
    703 			if (numFailed < maxPrints)
    704 				bufferedLogToConsole('ERROR: Mismatch in value ' + valNdx + ',\n' +
    705 								 '  expected unpackHalf2x16(' + inputs[valNdx] /*tcu::toHex(inputs[valNdx])*/ + ') = ' +
    706 								 'vec2(' + ref0 + ' / ' + refBits0 /*tcu::toHex(refBits0)*/ + ', ' + ref1 + ' / ' + refBits1 /*tcu::toHex(refBits1)*/ + ')' +
    707 								 ', got vec2(' + res0 + ' / ' + resBits0 /*tcu::toHex(resBits0)*/ + ', ' + res1 + ' / ' + resBits1 /*tcu::toHex(resBits1)*/ + ')' +
    708 								 '\n  ULP diffs = (' + diff0 + ', ' + diff1 + '), max diff = ' + maxDiff);
    709 			else if (numFailed == maxPrints)
    710 				bufferedLogToConsole('...');
    711 
    712 			numFailed += 1;
    713 		}
    714 	}
    715 
    716 	bufferedLogToConsole((numValues - numFailed) + ' / ' + numValues + ' values passed');
    717 
    718 	/** @type {boolean} */ var isOk = numFailed === 0;
    719 	if (!isOk)
    720 		testFailedOptions('Result comparison failed', false);
    721 	else
    722 		testPassedOptions('Pass', true);
    723 
    724 	return tcuTestCase.IterateResult.STOP;
    725 };
    726 
    727 /**
    728  * @constructor
    729  * @extends {tcuTestCase.DeqpTest}
    730  */
    731 es3fShaderPackingFunctionTests.ShaderPackingFunctionTests = function() {
    732 	tcuTestCase.DeqpTest.call(this, 'pack_unpack', 'Floating-point pack and unpack function tests');
    733 };
    734 
    735 es3fShaderPackingFunctionTests.ShaderPackingFunctionTests.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
    736 es3fShaderPackingFunctionTests.ShaderPackingFunctionTests.prototype.constructor = es3fShaderPackingFunctionTests.ShaderPackingFunctionTests;
    737 
    738 es3fShaderPackingFunctionTests.ShaderPackingFunctionTests.prototype.init = function() {
    739 	var testGroup = tcuTestCase.runner.testCases;
    740 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackSnorm2x16Case(gluShaderProgram.shaderType.VERTEX, gluShaderUtil.precision.PRECISION_LOWP));
    741 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackSnorm2x16Case(gluShaderProgram.shaderType.FRAGMENT, gluShaderUtil.precision.PRECISION_LOWP));
    742 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackSnorm2x16Case(gluShaderProgram.shaderType.VERTEX, gluShaderUtil.precision.PRECISION_MEDIUMP));
    743 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackSnorm2x16Case(gluShaderProgram.shaderType.FRAGMENT, gluShaderUtil.precision.PRECISION_MEDIUMP));
    744 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackSnorm2x16Case(gluShaderProgram.shaderType.VERTEX, gluShaderUtil.precision.PRECISION_HIGHP));
    745 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackSnorm2x16Case(gluShaderProgram.shaderType.FRAGMENT, gluShaderUtil.precision.PRECISION_HIGHP));
    746 
    747 	testGroup.addChild(new es3fShaderPackingFunctionTests.UnpackSnorm2x16Case(gluShaderProgram.shaderType.VERTEX));
    748 	testGroup.addChild(new es3fShaderPackingFunctionTests.UnpackSnorm2x16Case(gluShaderProgram.shaderType.FRAGMENT));
    749 
    750 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackUnorm2x16Case(gluShaderProgram.shaderType.VERTEX, gluShaderUtil.precision.PRECISION_LOWP));
    751 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackUnorm2x16Case(gluShaderProgram.shaderType.FRAGMENT, gluShaderUtil.precision.PRECISION_LOWP));
    752 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackUnorm2x16Case(gluShaderProgram.shaderType.VERTEX, gluShaderUtil.precision.PRECISION_MEDIUMP));
    753 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackUnorm2x16Case(gluShaderProgram.shaderType.FRAGMENT, gluShaderUtil.precision.PRECISION_MEDIUMP));
    754 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackUnorm2x16Case(gluShaderProgram.shaderType.VERTEX, gluShaderUtil.precision.PRECISION_HIGHP));
    755 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackUnorm2x16Case(gluShaderProgram.shaderType.FRAGMENT, gluShaderUtil.precision.PRECISION_HIGHP));
    756 
    757 	testGroup.addChild(new es3fShaderPackingFunctionTests.UnpackUnorm2x16Case(gluShaderProgram.shaderType.VERTEX));
    758 	testGroup.addChild(new es3fShaderPackingFunctionTests.UnpackUnorm2x16Case(gluShaderProgram.shaderType.FRAGMENT));
    759 
    760 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackHalf2x16Case(gluShaderProgram.shaderType.VERTEX));
    761 	testGroup.addChild(new es3fShaderPackingFunctionTests.PackHalf2x16Case(gluShaderProgram.shaderType.FRAGMENT));
    762 
    763 	testGroup.addChild(new es3fShaderPackingFunctionTests.UnpackHalf2x16Case(gluShaderProgram.shaderType.VERTEX));
    764 	testGroup.addChild(new es3fShaderPackingFunctionTests.UnpackHalf2x16Case(gluShaderProgram.shaderType.FRAGMENT));
    765 };
    766 
    767    /**
    768    * Run test
    769    * @param {WebGL2RenderingContext} context
    770    */
    771    es3fShaderPackingFunctionTests.run = function(context) {
    772    	gl = context;
    773    	//Set up Test Root parameters
    774    	var state = tcuTestCase.runner;
    775    	state.setRoot(new es3fShaderPackingFunctionTests.ShaderPackingFunctionTests());
    776 
    777    	//Set up name and description of this test series.
    778    	setCurrentTestName(state.testCases.fullName());
    779    	description(state.testCases.getDescription());
    780 
    781    	try {
    782    		//Run test cases
    783    		tcuTestCase.runTestCases();
    784    	}
    785    	catch (err) {
    786    		testFailedOptions('Failed to es3fShaderPackingFunctionTests.run tests', false);
    787    		tcuTestCase.runner.terminate();
    788    	}
    789    };
    790 
    791 });