| /*------------------------------------------------------------------------- |
| * 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; |
| }; |
| |
| }); |