blob: 446782b4b651416d6c6e2cb5874f3568476842fb [file] [log] [blame]
/*-------------------------------------------------------------------------
* drawElements Quality Program OpenGL ES Utilities
* ------------------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
'use strict';
goog.provide('modules.shared.glsBufferTestUtil');
goog.require('framework.common.tcuImageCompare');
goog.require('framework.common.tcuRGBA');
goog.require('framework.common.tcuSurface');
goog.require('framework.common.tcuTestCase');
goog.require('framework.common.tcuTexture');
goog.require('framework.delibs.debase.deMath');
goog.require('framework.delibs.debase.deRandom');
goog.require('framework.delibs.debase.deString');
goog.require('framework.delibs.debase.deUtil');
goog.require('framework.opengl.gluDrawUtil');
goog.require('framework.opengl.gluShaderProgram');
goog.require('framework.opengl.gluShaderUtil');
goog.scope(function() {
var glsBufferTestUtil = modules.shared.glsBufferTestUtil;
var tcuImageCompare = framework.common.tcuImageCompare;
var tcuRGBA = framework.common.tcuRGBA;
var tcuSurface = framework.common.tcuSurface;
var tcuTestCase = framework.common.tcuTestCase;
var tcuTexture = framework.common.tcuTexture;
var gluShaderProgram = framework.opengl.gluShaderProgram;
var gluShaderUtil = framework.opengl.gluShaderUtil;
var gluDrawUtil = framework.opengl.gluDrawUtil;
var deUtil = framework.delibs.debase.deUtil;
var deMath = framework.delibs.debase.deMath;
var deRandom = framework.delibs.debase.deRandom;
var deString = framework.delibs.debase.deString;
glsBufferTestUtil.VERIFY_QUAD_SIZE = 8; //!< Quad size in VertexArrayVerifier
glsBufferTestUtil.MAX_LINES_PER_INDEX_ARRAY_DRAW = 128; //!< Maximum number of lines per one draw in IndexArrayVerifier
glsBufferTestUtil.INDEX_ARRAY_DRAW_VIEWPORT_WIDTH = 128;
glsBufferTestUtil.INDEX_ARRAY_DRAW_VIEWPORT_HEIGHT = 128;
// Helper functions.
/**
* @param {Uint8Array} ptr
* @param {number} numBytes
* @param {number} seed
*/
glsBufferTestUtil.fillWithRandomBytes = function(ptr, numBytes, seed) {
var rnd = new deRandom.Random(seed);
for (var left = numBytes; left > 0; left--)
ptr[left - 1] = rnd.getInt();
};
/**
* @param {Uint8Array} resPtr
* @param {Uint8Array} refPtr
* @param {number} numBytes
* @return {boolean}
*/
glsBufferTestUtil.compareByteArrays = function(resPtr, refPtr, numBytes) {
var isOk = true;
var maxSpanLen = 8;
var maxDiffSpans = 4;
var numDiffSpans = 0;
var diffSpanStart = -1;
var ndx = 0;
var log = 'Verification result: ';
for (; ndx < numBytes; ndx++) {
if (resPtr[ndx] != refPtr[ndx]) {
if (diffSpanStart < 0)
diffSpanStart = ndx;
isOk = false;
} else if (diffSpanStart >= 0) {
if (numDiffSpans < maxDiffSpans) {
var len = ndx - diffSpanStart;
var printLen = Math.min(len, maxSpanLen);
log += len + ' byte difference at offset ' + diffSpanStart + '\n' +
' expected ' + refPtr.subarray(diffSpanStart, diffSpanStart + printLen) +
' got ' + resPtr.subarray(diffSpanStart, diffSpanStart + printLen);
} else
log += '(output too long, truncated)';
numDiffSpans += 1;
diffSpanStart = -1;
}
}
if (diffSpanStart >= 0) {
if (numDiffSpans < maxDiffSpans) {
var len = ndx - diffSpanStart;
var printLen = Math.min(len, maxSpanLen);
log += len + ' byte difference at offset ' + diffSpanStart + '\n' +
' expected ' + refPtr.subarray(diffSpanStart, diffSpanStart + printLen) +
' got ' + resPtr.subarray(diffSpanStart, diffSpanStart + printLen);
} else
log += '(output too long, truncated)';
}
log += (isOk ? 'Verification passed.' : 'Verification FAILED!');
bufferedLogToConsole(log);
return isOk;
};
/**
* @param {number} target
* @return {string}
*/
glsBufferTestUtil.getBufferTargetName = function(target) {
switch (target) {
case gl.ARRAY_BUFFER: return 'array';
case gl.COPY_READ_BUFFER: return 'copy_read';
case gl.COPY_WRITE_BUFFER: return 'copy_write';
case gl.ELEMENT_ARRAY_BUFFER: return 'element_array';
case gl.PIXEL_PACK_BUFFER: return 'pixel_pack';
case gl.PIXEL_UNPACK_BUFFER: return 'pixel_unpack';
//case gl.TEXTURE_BUFFER: return "texture"; //TODO: Unimplemented in WebGL 2. Remove?
case gl.TRANSFORM_FEEDBACK_BUFFER: return 'transform_feedback';
case gl.UNIFORM_BUFFER: return 'uniform';
default:
throw new Error('Invalid buffer target');
}
};
/**
* @param {number} hint
* @return {string}
*/
glsBufferTestUtil.getUsageHintName = function(hint) {
switch (hint) {
case gl.STREAM_DRAW: return 'stream_draw';
case gl.STREAM_READ: return 'stream_read';
case gl.STREAM_COPY: return 'stream_copy';
case gl.STATIC_DRAW: return 'static_draw';
case gl.STATIC_READ: return 'static_read';
case gl.STATIC_COPY: return 'static_copy';
case gl.DYNAMIC_DRAW: return 'dynamic_draw';
case gl.DYNAMIC_READ: return 'dynamic_read';
case gl.DYNAMIC_COPY: return 'dynamic_copy';
default:
throw new Error('Invalid buffer usage hint');
}
};
// Base class for buffer cases.
// BufferCase
/**
* @constructor
* @extends {tcuTestCase.DeqpTest}
* @param {string} name
* @param {string} description
*/
glsBufferTestUtil.BufferCase = function(name, description) {
tcuTestCase.DeqpTest.call(this, name, description);
/** @type {Array<WebGLBuffer>} */ this.m_allocatedBuffers = [];
};
glsBufferTestUtil.BufferCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
glsBufferTestUtil.BufferCase.prototype.constructor = glsBufferTestUtil.BufferCase;
/**
* init
*/
glsBufferTestUtil.BufferCase.prototype.init = function() {};
/**
* deinit
*/
glsBufferTestUtil.BufferCase.prototype.deinit = function() {
for (var ndx = 0; ndx < this.m_allocatedBuffers.length; ndx++)
this.deleteBuffer(this.m_allocatedBuffers[ndx]);
};
/**
* @return {WebGLBuffer}
*/
glsBufferTestUtil.BufferCase.prototype.genBuffer = function() {
var buf = 0;
buf = gl.createBuffer();
if (buf != 0) {
try {
deUtil.dePushUniqueToArray(this.m_allocatedBuffers, buf);
}
catch (err) {
gl.deleteBuffer(buf);
throw err;
}
}
return buf;
};
/**
* @param {WebGLBuffer} buffer
*/
glsBufferTestUtil.BufferCase.prototype.deleteBuffer = function(buffer) {
gl.deleteBuffer(buffer);
this.m_allocatedBuffers.splice(this.m_allocatedBuffers.indexOf(buffer), 1);
};
glsBufferTestUtil.BufferCase.prototype.checkError = function() {
/** @type {number} */ var err = gl.getError();
if (err != gl.NO_ERROR)
throw new TestFailedException('Got ' + WebGLTestUtils.glEnumToString(gl, err));
};
// Reference buffer.
/**
* @constructor
*/
glsBufferTestUtil.ReferenceBuffer = function() {
/** @type {ArrayBuffer} */ this.m_data;
};
/**
* @param {number=} offset
* @return {Uint8Array}
*/
glsBufferTestUtil.ReferenceBuffer.prototype.getPtr = function(offset) {
offset = offset ? offset : 0; return new Uint8Array(this.m_data, offset);
};
/**
* @param {number} numBytes
*/
glsBufferTestUtil.ReferenceBuffer.prototype.setSize = function(numBytes) {
this.m_data = new ArrayBuffer(numBytes);
};
/**
* @param {number} numBytes
* @param {Uint8Array} bytes
*/
glsBufferTestUtil.ReferenceBuffer.prototype.setData = function(numBytes, bytes) {
this.setSize(numBytes);
var dst = new Uint8Array(this.m_data);
dst.set(bytes.subarray(numBytes));
};
/**
* @param {number} offset
* @param {number} numBytes
* @param {Uint8Array} bytes
*/
glsBufferTestUtil.ReferenceBuffer.prototype.setSubData = function(offset, numBytes, bytes) {
assertMsgOptions(deMath.deInBounds32(offset, 0, this.m_data.byteLength) && deMath.deInRange32(offset + numBytes, offset, this.m_data.byteLength),
'Parameters not in buffer bounds or range', false, true);
var dst = new Uint8Array(this.m_data, offset);
dst.set(bytes.subarray(offset, offset + numBytes));
};
// Buffer writer system.
/**
* @enum {number}
*/
glsBufferTestUtil.WriteType = {
BUFFER_SUB_DATA: 0,
BUFFER_WRITE_MAP: 1,
TRANSFORM_FEEDBACK: 2,
PIXEL_PACK: 3
};
/**
* @param {glsBufferTestUtil.WriteType} write
* @return {string}
*/
glsBufferTestUtil.getWriteTypeDescription = function(write) {
/** @type {Array<string>} */ var s_desc = [
'gl.bufferSubData()',
'gl.mapBufferRange()',
'transform feedback',
'gl.readPixels() into PBO binding'
];
return /** @type {string} */ (deUtil.getArrayElement(s_desc, write));
};
// BufferWriterBase
/**
* @constructor
*/
glsBufferTestUtil.BufferWriterBase = function() {};
/**
* //Meant to be overriden
* @return {number}
*/
glsBufferTestUtil.BufferWriterBase.prototype.getMinSize = function() { throw new Error('Must be overriden'); };
/**
* //Meant to be overriden
* @return {number}
*/
glsBufferTestUtil.BufferWriterBase.prototype.getAlignment = function() { throw new Error('Must be overriden'); };
/**
* //Meant to be overriden
* @param {WebGLBuffer} buffer
* @param {number} offset
* @param {number} numBytes
* @param {Uint8Array} bytes
*/
glsBufferTestUtil.BufferWriterBase.prototype.writeNoTarget = function(buffer, offset, numBytes, bytes) { throw new Error('Must be overriden'); };
/**
* @param {WebGLBuffer} buffer
* @param {number} offset
* @param {number} numBytes
* @param {Uint8Array} bytes
* @param {number} targetHint
*/
glsBufferTestUtil.BufferWriterBase.prototype.write = function(buffer, offset, numBytes, bytes, targetHint) {
this.writeNoTarget(buffer, offset, numBytes, bytes);
};
// BufferWriter
/**
* @constructor
* @param {glsBufferTestUtil.WriteType} writeType
*/
glsBufferTestUtil.BufferWriter = function(writeType) {
/** @type {glsBufferTestUtil.BufferWriterBase} */ this.m_writer = null;
switch (writeType) {
case glsBufferTestUtil.WriteType.BUFFER_SUB_DATA: this.m_writer = new glsBufferTestUtil.BufferSubDataWriter(); break;
default:
testFailed('Unsupported writer');
}
};
/**
* @return {number}
*/
glsBufferTestUtil.BufferWriter.prototype.getMinSize = function() {return this.m_writer.getMinSize();};
/**
* @return {number}
*/
glsBufferTestUtil.BufferWriter.prototype.getAlignment = function() {return this.m_writer.getAlignment();};
/**
* @param {WebGLBuffer} buffer
* @param {number} offset
* @param {number} numBytes
* @param {Uint8Array} bytes
*/
glsBufferTestUtil.BufferWriter.prototype.writeNoTarget = function(buffer, offset, numBytes, bytes) {
assertMsgOptions(numBytes >= this.getMinSize(), 'Number of bytes to write is smaller than the minimum size.', false, true);
assertMsgOptions(offset % this.getAlignment() == 0, 'Offset is not aligned.', false, true);
assertMsgOptions((offset + numBytes) % this.getAlignment() == 0, 'Buffer segment is not aligned', false, true);
return this.m_writer.writeNoTarget(buffer, offset, numBytes, bytes);
};
/**
* @param {WebGLBuffer} buffer
* @param {number} offset
* @param {number} numBytes
* @param {Uint8Array} bytes
* @param {number} targetHint
*/
glsBufferTestUtil.BufferWriter.prototype.write = function(buffer, offset, numBytes, bytes, targetHint) {
assertMsgOptions(numBytes >= this.getMinSize(), 'Number of bytes to write is smaller than the minimum size.', false, true);
assertMsgOptions(offset % this.getAlignment() == 0, 'Offset is not aligned.', false, true);
assertMsgOptions((offset + numBytes) % this.getAlignment() == 0, 'Buffer segment is not aligned', false, true);
return this.m_writer.write(buffer, offset, numBytes, bytes, targetHint);
};
// BufferSubDataWriter
/**
* @constructor
* @extends {glsBufferTestUtil.BufferWriterBase}
*/
glsBufferTestUtil.BufferSubDataWriter = function() {
glsBufferTestUtil.BufferWriterBase.call(this);
};
glsBufferTestUtil.BufferSubDataWriter.prototype = Object.create(glsBufferTestUtil.BufferWriterBase.prototype);
glsBufferTestUtil.BufferSubDataWriter.prototype.constructor = glsBufferTestUtil.BufferSubDataWriter;
/**
* @return {number}
*/
glsBufferTestUtil.BufferSubDataWriter.prototype.getMinSize = function() { return 1; };
/**
* @return {number}
*/
glsBufferTestUtil.BufferSubDataWriter.prototype.getAlignment = function() { return 1; };
/**
* @param {WebGLBuffer} buffer
* @param {number} offset
* @param {number} numBytes
* @param {Uint8Array} bytes
*/
glsBufferTestUtil.BufferSubDataWriter.prototype.writeNoTarget = function(buffer, offset, numBytes, bytes) {
this.write(buffer, offset, numBytes, bytes, gl.ARRAY_BUFFER);
};
/**
* @param {WebGLBuffer} buffer
* @param {number} offset
* @param {number} numBytes
* @param {Uint8Array} bytes
* @param {number} target
*/
glsBufferTestUtil.BufferSubDataWriter.prototype.write = function(buffer, offset, numBytes, bytes, target) {
gl.bindBuffer(target, buffer);
gl.bufferSubData(target, offset, bytes);
gl.bindBuffer(target, null);
};
// Buffer verifier system.
/**
* @enum {number}
*/
glsBufferTestUtil.VerifyType = {
AS_VERTEX_ARRAY: 0,
AS_INDEX_ARRAY: 1,
AS_UNIFORM_BUFFER: 2,
AS_PIXEL_UNPACK_BUFFER: 3,
BUFFER_READ_MAP: 4
};
/**
* @param {glsBufferTestUtil.VerifyType} verify
* @return {string}
*/
glsBufferTestUtil.getVerifyTypeDescription = function(verify) {
/** @type {Array<string>} */ var s_desc =
[
'rendering as vertex data',
'rendering as index data',
'reading in shader as uniform buffer data',
'using as PBO and uploading to texture',
'reading back using glMapBufferRange()'
];
return /** @type {string} */ (deUtil.getArrayElement(s_desc, verify));
};
/**
* @constructor
*/
glsBufferTestUtil.BufferVerifierBase = function() {};
/**
* //Meant to be overriden
* @return {number}
*/
glsBufferTestUtil.BufferVerifierBase.prototype.getMinSize = function() { throw new Error('Must be overriden'); };
/**
* //Meant to be overriden
* @return {number}
*/
glsBufferTestUtil.BufferVerifierBase.prototype.getAlignment = function() { throw new Error('Must be overriden'); };
/**
* @param {WebGLBuffer} buffer
* @param {Uint8Array} reference
* @param {number} offset
* @param {number} numBytes
* @return {boolean}
*/
glsBufferTestUtil.BufferVerifierBase.prototype.verifyNoTarget = function(buffer, reference, offset, numBytes) {
throw new Error('Must be overriden');
};
/**
* //Meant to be overriden
* @param {WebGLBuffer} buffer
* @param {Uint8Array} reference
* @param {number} offset
* @param {number} numBytes
* @param {number} targetHint
* @return {boolean}
*/
glsBufferTestUtil.BufferVerifierBase.prototype.verify = function(buffer, reference, offset, numBytes, targetHint) {
//In WebGL 2, ELEMENT_ARRAY_BUFFER and TRANSFORM_FEEDBACK_BUFFER cannot be rebound to a different
//type of buffer, so, let's copy their data to an ARRAY BUFFER and pass that one instead to be verified.
var wasReadBufferCreated = false;
try {
if (targetHint == gl.ELEMENT_ARRAY_BUFFER || targetHint == gl.TRANSFORM_FEEDBACK_BUFFER) {
var readBuffer = new Uint8Array(offset + numBytes);
gl.getBufferSubData(targetHint, 0, readBuffer);
buffer = gl.createBuffer();
wasReadBufferCreated = true;
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, readBuffer, gl.STATIC_DRAW);
}
var result = this.verifyNoTarget(buffer, reference, offset, numBytes);
if (wasReadBufferCreated)
gl.deleteBuffer(buffer);
return result;
} catch (err) {
if (wasReadBufferCreated)
gl.deleteBuffer(buffer);
throw err;
}
};
// BufferVerifier
/**
* @constructor
* @param {glsBufferTestUtil.VerifyType} verifyType
*/
glsBufferTestUtil.BufferVerifier = function(verifyType) {
/** @type {glsBufferTestUtil.BufferVerifierBase} */ this.m_verifier = null;
switch (verifyType) {
case glsBufferTestUtil.VerifyType.AS_VERTEX_ARRAY: this.m_verifier = new glsBufferTestUtil.VertexArrayVerifier(); break;
case glsBufferTestUtil.VerifyType.AS_INDEX_ARRAY: this.m_verifier = new glsBufferTestUtil.IndexArrayVerifier(); break;
default:
testFailed('Unsupported verifier type');
}
};
/**
* @return {number}
*/
glsBufferTestUtil.BufferVerifier.prototype.getMinSize = function() { return this.m_verifier.getMinSize(); };
/**
* @return {number}
*/
glsBufferTestUtil.BufferVerifier.prototype.getAlignment = function() { return this.m_verifier.getAlignment(); };
/**
* @param {WebGLBuffer} buffer
* @param {Uint8Array} reference
* @param {number} numBytes
* @return {boolean}
*/
glsBufferTestUtil.BufferVerifier.prototype.verifyNoTarget = function(buffer, reference, offset, numBytes) {
assertMsgOptions(numBytes >= this.getMinSize(), 'Number of bytes to write is smaller than the minimum size.', false, true);
assertMsgOptions(offset % this.getAlignment() == 0, 'Offset is not aligned.', false, true);
assertMsgOptions((offset + numBytes) % this.getAlignment() == 0, 'Buffer segment is not aligned', false, true);
return this.m_verifier.verifyNoTarget(buffer, reference, offset, numBytes);
};
/**
* @param {WebGLBuffer} buffer
* @param {Uint8Array} reference
* @param {number} offset
* @param {number} numBytes
* @param {number} targetHint
* @return {boolean}
*/
glsBufferTestUtil.BufferVerifier.prototype.verify = function(buffer, reference, offset, numBytes, targetHint) {
assertMsgOptions(numBytes >= this.getMinSize(), 'Number of bytes to write is smaller than the minimum size.', false, true);
assertMsgOptions(offset % this.getAlignment() == 0, 'Offset is not aligned.', false, true);
assertMsgOptions((offset + numBytes) % this.getAlignment() == 0, 'Buffer segment is not aligned', false, true);
return this.m_verifier.verify(buffer, reference, offset, numBytes, targetHint);
};
// VertexArrayVerifier
/**
* @constructor
* @extends {glsBufferTestUtil.BufferVerifierBase}
*/
glsBufferTestUtil.VertexArrayVerifier = function() {
glsBufferTestUtil.BufferVerifierBase.call(this);
/** @type {gluShaderProgram.ShaderProgram} */ this.m_program = null;
this.m_posLoc = 0;
this.m_byteVecLoc = 0;
/** @type {WebGLVertexArrayObject} */ this.m_vao = null;
/** @type {gluShaderUtil.GLSLVersion} */ var glslVersion = gluShaderUtil.getGLSLVersion(gl);
assertMsgOptions(gluShaderUtil.isGLSLVersionSupported(gl, glslVersion), 'Unsupported GLSL version', false, true);
this.m_program = new gluShaderProgram.ShaderProgram(gl, gluShaderProgram.makeVtxFragSources(
gluShaderUtil.getGLSLVersionDeclaration(glslVersion) + '\n' +
'in highp vec2 a_position;\n' +
'in mediump vec3 a_byteVec;\n' +
'out mediump vec3 v_byteVec;\n' +
'void main (void)\n' +
'{\n' +
' gl_Position = vec4(a_position, 0.0, 1.0);\n' +
' v_byteVec = a_byteVec;\n' +
'}\n',
gluShaderUtil.getGLSLVersionDeclaration(glslVersion) + '\n' +
'in mediump vec3 v_byteVec;\n' +
'layout(location = 0) out mediump vec4 o_color;\n' +
'void main (void)\n' +
'{\n' +
' o_color = vec4(v_byteVec, 1.0);\n' +
'}\n'
));
if (!this.m_program.isOk()) {
testFailed('Compile failed');
}
this.m_posLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_position');
this.m_byteVecLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_byteVec');
this.m_vao = gl.createVertexArray();
this.m_positionBuf = gl.createBuffer();
this.m_indexBuf = gl.createBuffer();
};
glsBufferTestUtil.VertexArrayVerifier.prototype = Object.create(glsBufferTestUtil.BufferVerifierBase.prototype);
glsBufferTestUtil.VertexArrayVerifier.prototype.constructor = glsBufferTestUtil.VertexArrayVerifier;
/**
* @return {number}
*/
glsBufferTestUtil.VertexArrayVerifier.prototype.getMinSize = function() { return 3 * 4; };
/**
* @return {number}
*/
glsBufferTestUtil.VertexArrayVerifier.prototype.getAlignment = function() { return 1; };
/**
* deinit
*/
glsBufferTestUtil.VertexArrayVerifier.prototype.deinit = function() {
if (this.m_vao) gl.deleteVertexArray(this.m_vao);
if (this.m_positionBuf) gl.deleteBuffer(this.m_positionBuf);
if (this.m_indexBuf) gl.deleteBuffer(this.m_indexBuf);
};
/**
* @param {number} gridSizeX
* @param {number} gridSizeY
* @return {Array<number>}
*/
glsBufferTestUtil.computePositions = function(gridSizeX, gridSizeY) {
var positions = [];
for (var y = 0; y < gridSizeY; y++)
for (var x = 0; x < gridSizeX; x++) {
/** @type {number} */ var sx0 = (x + 0) / gridSizeX;
/** @type {number} */ var sy0 = (y + 0) / gridSizeY;
/** @type {number} */ var sx1 = (x + 1) / gridSizeX;
/** @type {number} */ var sy1 = (y + 1) / gridSizeY;
/** @type {number} */ var fx0 = 2.0 * sx0 - 1.0;
/** @type {number} */ var fy0 = 2.0 * sy0 - 1.0;
/** @type {number} */ var fx1 = 2.0 * sx1 - 1.0;
/** @type {number} */ var fy1 = 2.0 * sy1 - 1.0;
/** @type {number} */ var baseNdx = (y * gridSizeX + x) * 8;
positions[baseNdx + 0] = fx0; positions[baseNdx + 1] = fy0;
positions[baseNdx + 2] = fx0; positions[baseNdx + 3] = fy1;
positions[baseNdx + 4] = fx1; positions[baseNdx + 5] = fy0;
positions[baseNdx + 6] = fx1; positions[baseNdx + 7] = fy1;
}
return positions;
};
/**
* @param {number} gridSizeX
* @param {number} gridSizeY
* @return {Uint16Array}
*/
glsBufferTestUtil.computeIndices = function(gridSizeX, gridSizeY) {
var indices = new Uint16Array(3 * 2 * gridSizeX * gridSizeY);
for (var quadNdx = 0; quadNdx < gridSizeX * gridSizeY; quadNdx++) {
/** @type {number} */ var v00 = quadNdx * 4 + 0;
/** @type {number} */ var v01 = quadNdx * 4 + 1;
/** @type {number} */ var v10 = quadNdx * 4 + 2;
/** @type {number} */ var v11 = quadNdx * 4 + 3;
assertMsgOptions(v11 < (1 << 16), 'Vertex index value won\'t fit into a 16-bit number', false, true);
indices[quadNdx * 6 + 0] = v10;
indices[quadNdx * 6 + 1] = v00;
indices[quadNdx * 6 + 2] = v01;
indices[quadNdx * 6 + 3] = v10;
indices[quadNdx * 6 + 4] = v01;
indices[quadNdx * 6 + 5] = v11;
}
return indices;
};
/**
* @param {Uint8Array} ptr
* @param {number} vtxNdx
* @return {Array<number>}
*/
glsBufferTestUtil.fetchVtxColor = function(ptr, vtxNdx) {
return new tcuRGBA.RGBA([
ptr[vtxNdx * 3 + 0],
ptr[vtxNdx * 3 + 1],
ptr[vtxNdx * 3 + 2],
255]).toVec();
};
/**
* @param {tcuSurface.Surface} dst
* @param {number} numQuads
* @param {number} rowLength
* @param {Uint8Array} inPtr
*/
glsBufferTestUtil.renderQuadGridReference = function(dst, numQuads, rowLength, inPtr) {
dst.setSize(rowLength * glsBufferTestUtil.VERIFY_QUAD_SIZE, (Math.floor(numQuads / rowLength) + (numQuads % rowLength != 0 ? 1 : 0)) * glsBufferTestUtil.VERIFY_QUAD_SIZE);
/** @type {tcuTexture.PixelBufferAccess} */ var dstAccess = dst.getAccess();
dstAccess.clear([0, 0, 0, 1.0]);
for (var quadNdx = 0; quadNdx < numQuads; quadNdx++) {
/** @type {number} */ var x0 = (quadNdx % rowLength) * glsBufferTestUtil.VERIFY_QUAD_SIZE;
/** @type {number} */ var y0 = Math.floor(quadNdx / rowLength) * glsBufferTestUtil.VERIFY_QUAD_SIZE;
/** @type {Array<number>} */ var v00 = glsBufferTestUtil.fetchVtxColor(inPtr, quadNdx * 4 + 0);
/** @type {Array<number>} */ var v10 = glsBufferTestUtil.fetchVtxColor(inPtr, quadNdx * 4 + 1);
/** @type {Array<number>} */ var v01 = glsBufferTestUtil.fetchVtxColor(inPtr, quadNdx * 4 + 2);
/** @type {Array<number>} */ var v11 = glsBufferTestUtil.fetchVtxColor(inPtr, quadNdx * 4 + 3);
for (var y = 0; y < glsBufferTestUtil.VERIFY_QUAD_SIZE; y++)
for (var x = 0; x < glsBufferTestUtil.VERIFY_QUAD_SIZE; x++) {
/** @type {number} */ var fx = (x + 0.5) / glsBufferTestUtil.VERIFY_QUAD_SIZE;
/** @type {number} */ var fy = (y + 0.5) / glsBufferTestUtil.VERIFY_QUAD_SIZE;
/** @type {boolean} */ var tri = fx + fy <= 1.0;
/** @type {number} */ var tx = tri ? fx : (1.0 - fx);
/** @type {number} */ var ty = tri ? fy : (1.0 - fy);
/** @type {Array<number>} */ var t0 = tri ? v00 : v11;
/** @type {Array<number>} */ var t1 = tri ? v01 : v10;
/** @type {Array<number>} */ var t2 = tri ? v10 : v01;
/** @type {Array<number>} */ var color = deMath.add(
deMath.add(t0, deMath.scale(deMath.subtract(t1, t0), tx)),
deMath.scale(deMath.subtract(t2, t0), ty)
);
dstAccess.setPixel(color, x0 + x, y0 + y);
}
}
};
/**
* @param {WebGLBuffer} buffer
* @param {Uint8Array} refPtr
* @param {number} offset
* @param {number} numBytes
* @return {boolean}
*/
glsBufferTestUtil.VertexArrayVerifier.prototype.verifyNoTarget = function(buffer, refPtr, offset, numBytes) {
var numBytesInVtx = 3;
var numBytesInQuad = numBytesInVtx * 4;
var maxQuadsX = Math.min(128, Math.floor(gl.drawingBufferWidth / glsBufferTestUtil.VERIFY_QUAD_SIZE));
var maxQuadsY = Math.min(128, Math.floor(gl.drawingBufferHeight / glsBufferTestUtil.VERIFY_QUAD_SIZE));
var maxQuadsPerBatch = maxQuadsX * maxQuadsY;
var numVerified = 0;
var program = this.m_program.getProgram();
/** @type {tcuRGBA.RGBA} */ var threshold = /*TODO: renderTarget.getPixelFormat().getColorThreshold() + tcu::RGBA(3,3,3,3);*/ new tcuRGBA.RGBA([3, 3, 3, 3]);
var isOk = true;
/** @type {Array<number>} */ var positions = [];
/** @type {Uint16Array} */var indices;
/** @type {tcuSurface.Surface} */ var rendered = new tcuSurface.Surface();
/** @type {tcuSurface.Surface} */ var reference = new tcuSurface.Surface();
// Can't render full quad with smaller buffers.
assertMsgOptions(numBytes >= numBytesInQuad, 'Number of bytes must be bigger than number of bytes per quad', false, true);
positions = glsBufferTestUtil.computePositions(maxQuadsX, maxQuadsY);
indices = glsBufferTestUtil.computeIndices(maxQuadsX, maxQuadsY);
// Reset buffer bindings.
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
// Setup rendering state.
gl.viewport(0, 0, maxQuadsX * glsBufferTestUtil.VERIFY_QUAD_SIZE, maxQuadsY * glsBufferTestUtil.VERIFY_QUAD_SIZE);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.useProgram(program);
gl.bindVertexArray(this.m_vao);
// Upload positions
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_positionBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
gl.enableVertexAttribArray(this.m_posLoc);
gl.vertexAttribPointer(this.m_posLoc, 2, gl.FLOAT, false, 0, 0);
// Upload indices
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.m_indexBuf);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
gl.enableVertexAttribArray(this.m_byteVecLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
while (numVerified < numBytes) {
/** @type {number} */ var numRemaining = numBytes - numVerified;
var isLeftoverBatch = numRemaining < numBytesInQuad;
/** @type {number} */ var numBytesToVerify = isLeftoverBatch ? numBytesInQuad : Math.min(maxQuadsPerBatch * numBytesInQuad, numRemaining - numRemaining % numBytesInQuad);
/** @type {number} */ var curOffset = isLeftoverBatch ? (numBytes - numBytesInQuad) : numVerified;
/** @type {number} */ var numQuads = Math.floor(numBytesToVerify / numBytesInQuad);
/** @type {number} */ var numCols = Math.min(maxQuadsX, numQuads);
/** @type {number} */ var numRows = Math.floor(numQuads / maxQuadsX) + (numQuads % maxQuadsX != 0 ? 1 : 0);
/** @type {string} */ var imageSetDesc = 'Bytes ' + (offset + curOffset) + ' to ' + (offset + curOffset + numBytesToVerify - 1);
assertMsgOptions(numBytesToVerify > 0 && numBytesToVerify % numBytesInQuad == 0, 'Bytes to verify must be greater than zero and must be a multiple of the bytes per quad', false, true);
assertMsgOptions(deMath.deInBounds32(curOffset, 0, numBytes), 'Offset out of bounds', false, true);
assertMsgOptions(deMath.deInRange32(curOffset + numBytesToVerify, curOffset, numBytes), 'Range of bytes to verify outside of bounds', false, true);
// Render batch.
gl.clear(gl.COLOR_BUFFER_BIT);
gl.vertexAttribPointer(this.m_byteVecLoc, 3, gl.UNSIGNED_BYTE, true, 0, offset + curOffset);
gl.drawElements(gl.TRIANGLES, numQuads * 6, gl.UNSIGNED_SHORT, 0);
glsBufferTestUtil.renderQuadGridReference(reference, numQuads, numCols, refPtr.subarray(offset + curOffset));
rendered.setSize(numCols * glsBufferTestUtil.VERIFY_QUAD_SIZE, numRows * glsBufferTestUtil.VERIFY_QUAD_SIZE);
rendered.readViewport(gl, [0, 0, numCols * glsBufferTestUtil.VERIFY_QUAD_SIZE, numRows * glsBufferTestUtil.VERIFY_QUAD_SIZE]);
if (!tcuImageCompare.pixelThresholdCompare('RenderResult', imageSetDesc, reference, rendered, threshold.toIVec(), tcuImageCompare.CompareLogMode.RESULT)) {
isOk = false;
break;
}
numVerified += isLeftoverBatch ? numRemaining : numBytesToVerify;
}
gl.bindVertexArray(null);
return isOk;
};
// IndexArrayVerifier
/**
* @constructor
* @extends {glsBufferTestUtil.BufferVerifierBase}
*/
glsBufferTestUtil.IndexArrayVerifier = function() {
glsBufferTestUtil.BufferVerifierBase.call(this);
this.m_program = null;
this.m_posLoc = 0;
this.m_colorLoc = 0;
/** @type {gluShaderUtil.GLSLVersion} */ var glslVersion = gluShaderUtil.GLSLVersion.V300_ES;
assertMsgOptions(gluShaderUtil.isGLSLVersionSupported(gl, glslVersion), 'GLSL version not supported', false, true);
this.m_program = new gluShaderProgram.ShaderProgram(gl, gluShaderProgram.makeVtxFragSources(
gluShaderUtil.getGLSLVersionDeclaration(glslVersion) + '\n' +
'in highp vec2 a_position;\n' +
'in mediump vec3 a_color;\n' +
'out mediump vec3 v_color;\n' +
'void main (void)\n' +
'{\n' +
' gl_Position = vec4(a_position, 0.0, 1.0);\n' +
' v_color = a_color;\n' +
'}\n',
gluShaderUtil.getGLSLVersionDeclaration(glslVersion) + '\n' +
'in mediump vec3 v_color;\n' +
'layout(location = 0) out mediump vec4 o_color;\n' +
'void main (void)\n' +
'{\n' +
' o_color = vec4(v_color, 1.0);\n' +
'}\n'));
if (!this.m_program.isOk()) {
testFailed('Compile failed');
}
this.m_posLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_position');
this.m_colorLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_color');
this.m_vao = gl.createVertexArray();
this.m_positionBuf = gl.createBuffer();
this.m_colorBuf = gl.createBuffer();
};
glsBufferTestUtil.IndexArrayVerifier.prototype = Object.create(glsBufferTestUtil.BufferVerifierBase.prototype);
glsBufferTestUtil.IndexArrayVerifier.prototype.constructor = glsBufferTestUtil.IndexArrayVerifier;
/**
* deinit
*/
glsBufferTestUtil.IndexArrayVerifier.prototype.deinit = function() {
if (this.m_vao) gl.deleteVertexArray(this.m_vao);
if (this.m_positionBuf) gl.deleteBuffer(this.m_positionBuf);
if (this.m_colorBuf) gl.deleteBuffer(this.m_colorBuf);
};
/**
* @return {Array<number>}
*/
glsBufferTestUtil.computeIndexVerifierPositions = function() {
var numPosX = 16;
var numPosY = 16;
var dst = [];
for (var y = 0; y < numPosY; y++) {
for (var x = 0; x < numPosX; x++) {
var xf = x / (numPosX - 1);
var yf = y / (numPosY - 1);
var offset = 2 * (y * numPosX + x);
dst[offset] = 2.0 * xf - 1.0;
dst[offset + 1] = 2.0 * yf - 1.0;
}
}
return dst;
};
/**
* @return {Array<number>}
*/
glsBufferTestUtil.computeIndexVerifierColors = function() {
/** @type {number} */ var numColors = 256;
/** @type {number} */ var minVal = 0.1;
/** @type {number} */ var maxVal = 0.5;
var rnd = new deRandom.Random(0xabc231);
var dst = [];
for (var i = 0; i < numColors; ++i) {
dst[3 * i] = rnd.getFloat(minVal, maxVal);
dst[3 * i + 1] = rnd.getFloat(minVal, maxVal);
dst[3 * i + 2] = rnd.getFloat(minVal, maxVal);
}
return dst;
};
/**
* @param {Array<number>} dst
* @param {Array<number>} src
* @param {Uint8Array} indices
* @param {number} numIndices
*/
glsBufferTestUtil.execVertexFetch = function(dst, src, indices, numIndices) {
for (var i = 0; i < numIndices; ++i)
dst[i] = src[indices[i]];
};
/**
* @param {WebGLBuffer} buffer
* @param {Uint8Array} refPtr
* @param {number} offset
* @param {number} numBytes
* @return {boolean}
*/
glsBufferTestUtil.IndexArrayVerifier.prototype.verify = function(buffer, refPtr, offset, numBytes) {
var viewportW = Math.min(glsBufferTestUtil.INDEX_ARRAY_DRAW_VIEWPORT_WIDTH, gl.drawingBufferWidth);
var viewportH = Math.min(glsBufferTestUtil.INDEX_ARRAY_DRAW_VIEWPORT_HEIGHT, gl.drawingBufferHeight);
var minBytesPerBatch = 2;
/** @type {tcuRGBA.RGBA} */ var threshold = new tcuRGBA.RGBA([0, 0, 0, 0]);
var positions = [];
var colors = [];
var fetchedPos = [];
var fetchedColor = [];
/** @type {tcuSurface.Surface} */ var indexBufferImg = new tcuSurface.Surface(viewportW, viewportH);
/** @type {tcuSurface.Surface} */ var referenceImg = new tcuSurface.Surface(viewportW, viewportH);
var numVerified = 0;
var isOk = true;
positions = glsBufferTestUtil.computeIndexVerifierPositions();
colors = glsBufferTestUtil.computeIndexVerifierColors();
// Reset buffer bindings.
gl.bindVertexArray(this.m_vao);
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
// Setup rendering state.
gl.viewport(0, 0, viewportW, viewportH);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.useProgram(this.m_program.getProgram());
gl.enableVertexAttribArray(this.m_posLoc);
gl.enableVertexAttribArray(this.m_colorLoc);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE);
gl.blendEquation(gl.FUNC_ADD);
while (numVerified < numBytes) {
var numRemaining = numBytes - numVerified;
var isLeftoverBatch = numRemaining < minBytesPerBatch;
var numBytesToVerify = isLeftoverBatch ? minBytesPerBatch : Math.min(glsBufferTestUtil.MAX_LINES_PER_INDEX_ARRAY_DRAW + 1, numRemaining);
var curOffset = isLeftoverBatch ? (numBytes - minBytesPerBatch) : numVerified;
/** @type {string} */ var imageSetDesc = 'Bytes ' + (offset + curOffset) + ' to ' + (offset + curOffset + numBytesToVerify - 1);
// Step 1: Render using index buffer.
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_positionBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STREAM_DRAW);
gl.vertexAttribPointer(this.m_posLoc, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_colorBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STREAM_DRAW);
gl.vertexAttribPointer(this.m_colorLoc, 3, gl.FLOAT, false, 0, 0);
gl.drawElements(gl.LINE_STRIP, numBytesToVerify, gl.UNSIGNED_BYTE, offset + curOffset);
indexBufferImg.readViewport(gl);
// Step 2: Do manual fetch and render without index buffer.
glsBufferTestUtil.execVertexFetch(fetchedPos, positions, refPtr.subarray(offset + curOffset), numBytesToVerify);
glsBufferTestUtil.execVertexFetch(fetchedColor, colors, refPtr.subarray(offset + curOffset), numBytesToVerify);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_positionBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(fetchedPos), gl.STREAM_DRAW);
gl.vertexAttribPointer(this.m_posLoc, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_colorBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(fetchedColor), gl.STREAM_DRAW);
gl.vertexAttribPointer(this.m_colorLoc, 3, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINE_STRIP, 0, numBytesToVerify);
referenceImg.readViewport(gl, [0, 0, viewportW, viewportH]);
if (!tcuImageCompare.pixelThresholdCompare('RenderResult', imageSetDesc, referenceImg, indexBufferImg, threshold.toIVec(), tcuImageCompare.CompareLogMode.RESULT)) {
isOk = false;
break;
}
numVerified += isLeftoverBatch ? numRemaining : numBytesToVerify;
}
gl.bindVertexArray(null);
return isOk;
};
});