/*------------------------------------------------------------------------- * 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('framework.referencerenderer.rrFragmentOperations'); goog.require('framework.common.tcuTexture'); goog.require('framework.common.tcuTextureUtil'); goog.require('framework.delibs.debase.deMath'); goog.require('framework.referencerenderer.rrRenderState'); goog.scope(function() { var rrFragmentOperations = framework.referencerenderer.rrFragmentOperations; var deMath = framework.delibs.debase.deMath; var rrRenderState = framework.referencerenderer.rrRenderState; var tcuTexture = framework.common.tcuTexture; var tcuTextureUtil = framework.common.tcuTextureUtil; /** Return oldValue with the bits indicated by mask replaced by corresponding bits of newValue. * @param {number} oldValue * @param {number} newValue * @param {number} mask * @return {number} */ rrFragmentOperations.maskedBitReplace = function(oldValue, newValue, mask) { return (oldValue & ~mask) | (newValue & mask); }; /** * @param {Array} point * @param {?} rect * @return {boolean} */ rrFragmentOperations.isInsideRect = function(point, rect) { return deMath.deInBounds32(point[0], rect.left, rect.left + rect.width) && deMath.deInBounds32(point[1], rect.bottom, rect.bottom + rect.height); }; /** * @constructor * @param {Array} coefficents * @param {Array} coords * @param {number} depth */ rrFragmentOperations.Fragment = function(coefficents, coords, depth) { /** @type {Array} */ this.barycentric = coefficents; /** @type {Array} */ this.pixelCoord = coords; /** @type {boolean} */ this.isAlive = true; /** @type {boolean} */ this.stencilPassed = true; /** @type {boolean} */ this.depthPassed = true; /** @type {Array} */ this.sampleDepths = [depth]; /** @type {Array} */ this.clampedBlendSrcColor = []; /** @type {Array} */ this.clampedBlendSrc1Color = []; /** @type {Array} */ this.clampedBlendDstColor = []; /** @type {Array} */ this.blendSrcFactorRGB = []; /** @type {number} */ this.blendSrcFactorA = NaN; /** @type {Array} */ this.blendDstFactorRGB = []; /** @type {number} */ this.blendDstFactorA = NaN; /** @type {Array} */ this.blendedRGB = []; /** @type {number} */ this.blendedA = NaN; /** @type {Array} */ this.signedValue = []; //!< integer targets /** @type {Array} */ this.unsignedValue = []; //!< unsigned integer targets /** @type {Array} */ this.value = []; /*TODO: what type should it be? */ /** @type {Array} */ this.value1 = []; /*TODO: what type should it be? */ }; /** * @param {Array} inputFragments Fragments to write * @param {rrRenderState.WindowRectangle} scissorRect */ rrFragmentOperations.executeScissorTest = function(inputFragments, scissorRect) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { if (!rrFragmentOperations.isInsideRect(frag.pixelCoord, scissorRect)) frag.isAlive = false; } } }; /** * @param {Array} inputFragments Fragments to write * @param {rrRenderState.StencilState} stencilState * @param {number} numStencilBits * @param {tcuTexture.PixelBufferAccess} stencilBuffer */ rrFragmentOperations.executeStencilCompare = function(inputFragments, stencilState, numStencilBits, stencilBuffer) { var clampedStencilRef = deMath.clamp(stencilState.ref, 0, (1 << numStencilBits) - 1); /** * @param {function(number=,number=):boolean} expression */ var sample_register_stencil_compare = function(expression) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { var fragSampleNdx = 0; var stencilBufferValue = stencilBuffer.getPixStencil(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); var maskedRef = stencilState.compMask & clampedStencilRef; var maskedBuf = stencilState.compMask & stencilBufferValue; frag.stencilPassed = expression(maskedRef, maskedBuf); } } }; switch (stencilState.func) { case rrRenderState.TestFunc.NEVER: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return false;}); break; case rrRenderState.TestFunc.ALWAYS: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return true;}); break; case rrRenderState.TestFunc.LESS: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef < maskedBuf;}); break; case rrRenderState.TestFunc.LEQUAL: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef <= maskedBuf;}); break; case rrRenderState.TestFunc.GREATER: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef > maskedBuf;}); break; case rrRenderState.TestFunc.GEQUAL: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef >= maskedBuf;}); break; case rrRenderState.TestFunc.EQUAL: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef == maskedBuf;}); break; case rrRenderState.TestFunc.NOTEQUAL: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef != maskedBuf;}); break; default: throw new Error('Unrecognized stencil test function:' + stencilState.func); } }; /** * @param {Array} inputFragments Fragments to write * @param {rrRenderState.StencilState} stencilState * @param {number} numStencilBits * @param {tcuTexture.PixelBufferAccess} stencilBuffer */ rrFragmentOperations.executeStencilSFail = function(inputFragments, stencilState, numStencilBits, stencilBuffer) { var clampedStencilRef = deMath.clamp(stencilState.ref, 0, (1 << numStencilBits) - 1); /** * @param {function(number,number):number} expression */ var sample_register_sfail = function(expression) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive && !frag.stencilPassed) { var fragSampleNdx = 0; var stencilBufferValue = stencilBuffer.getPixStencil(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); stencilBuffer.setPixStencil(rrFragmentOperations.maskedBitReplace(stencilBufferValue, expression(stencilBufferValue, numStencilBits), stencilState.writeMask), fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); frag.isAlive = false; } } }; switch (stencilState.sFail) { case rrRenderState.StencilOp.KEEP: sample_register_sfail(function(stencilBufferValue, numStencilBits) { return stencilBufferValue;}); break; case rrRenderState.StencilOp.ZERO: sample_register_sfail(function(stencilBufferValue, numStencilBits) { return 0;}); break; case rrRenderState.StencilOp.REPLACE: sample_register_sfail(function(stencilBufferValue, numStencilBits) { return clampedStencilRef;}); break; case rrRenderState.StencilOp.INCR: sample_register_sfail(function(stencilBufferValue, numStencilBits) { return deMath.clamp(stencilBufferValue + 1, 0, (1 << numStencilBits) - 1);}); break; case rrRenderState.StencilOp.DECR: sample_register_sfail(function(stencilBufferValue, numStencilBits) { return deMath.clamp(stencilBufferValue - 1, 0, (1 << numStencilBits) - 1);}); break; case rrRenderState.StencilOp.INCR_WRAP: sample_register_sfail(function(stencilBufferValue, numStencilBits) { return (stencilBufferValue + 1) & ((1 << numStencilBits) - 1);}); break; case rrRenderState.StencilOp.DECR_WRAP: sample_register_sfail(function(stencilBufferValue, numStencilBits) { return (stencilBufferValue - 1) & ((1 << numStencilBits) - 1);}); break; case rrRenderState.StencilOp.INVERT: sample_register_sfail(function(stencilBufferValue, numStencilBits) { return (~stencilBufferValue) & ((1 << numStencilBits) - 1);}); break; default: throw new Error('Unrecognized stencil op:' + stencilState.sFail); } }; /** * @param {Array} inputFragments Fragments to write * @param {rrRenderState.TestFunc} depthFunc * @param {tcuTexture.PixelBufferAccess} depthBuffer */ rrFragmentOperations.executeDepthCompare = function(inputFragments, depthFunc, depthBuffer) { /** * @param {function(number=,number=):boolean} expression */ var convertToDepthBuffer = false; var access; if (depthBuffer.getFormat().type != tcuTexture.ChannelType.FLOAT && depthBuffer.getFormat().type != tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV) { access = new tcuTexture.PixelBufferAccess({ format: depthBuffer.getFormat(), width: 1, height: 1, depth: 1, data: new ArrayBuffer(8) }); convertToDepthBuffer = true; } var sample_register_depth_compare = function(expression) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { var fragSampleNdx = 0; var depthBufferValue = depthBuffer.getPixDepth(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); var sampleDepthFloat = frag.sampleDepths[fragSampleNdx]; var sampleDepth; if (convertToDepthBuffer) { /* convert input float to target buffer format for comparison */ access.setPixDepth(sampleDepthFloat, 0, 0, 0); sampleDepth = access.getPixDepth(0, 0, 0); } else { sampleDepth = deMath.clamp(sampleDepthFloat, 0.0, 1.0); } frag.depthPassed = expression(sampleDepth, depthBufferValue); } } }; switch (depthFunc) { case rrRenderState.TestFunc.NEVER: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return false;}); break; case rrRenderState.TestFunc.ALWAYS: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return true;}); break; case rrRenderState.TestFunc.LESS: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth < depthBufferValue;}); break; case rrRenderState.TestFunc.LEQUAL: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth <= depthBufferValue;}); break; case rrRenderState.TestFunc.GREATER: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth > depthBufferValue;}); break; case rrRenderState.TestFunc.GEQUAL: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth >= depthBufferValue;}); break; case rrRenderState.TestFunc.EQUAL: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth == depthBufferValue;}); break; case rrRenderState.TestFunc.NOTEQUAL: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth != depthBufferValue;}); break; default: throw new Error('Unrecognized depth function:' + depthFunc); } }; /** * @param {Array} inputFragments Fragments to write * @param {tcuTexture.PixelBufferAccess} depthBuffer */ rrFragmentOperations.executeDepthWrite = function(inputFragments, depthBuffer) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive && frag.depthPassed) { var fragSampleNdx = 0; var clampedDepth = deMath.clamp(frag.sampleDepths[fragSampleNdx], 0.0, 1.0); depthBuffer.setPixDepth(clampedDepth, fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); } } }; /** * @param {Array} inputFragments Fragments to write * @param {rrRenderState.StencilState} stencilState * @param {number} numStencilBits * @param {tcuTexture.PixelBufferAccess} stencilBuffer */ rrFragmentOperations.executeStencilDpFailAndPass = function(inputFragments, stencilState, numStencilBits, stencilBuffer) { var clampedStencilRef = deMath.clamp(stencilState.ref, 0, (1 << numStencilBits) - 1); /** * @param {function(boolean):boolean} condition * @param {function(number,number):number} expression */ var sample_register_dpfail_or_dppass = function(condition, expression) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive && condition(frag.depthPassed)) { var fragSampleNdx = 0; var stencilBufferValue = stencilBuffer.getPixStencil(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); stencilBuffer.setPixStencil(rrFragmentOperations.maskedBitReplace(stencilBufferValue, expression(stencilBufferValue, numStencilBits), stencilState.writeMask), fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); } } }; var switch_dpfail_or_dppass = function(op_name, condition) { switch (stencilState[op_name]) { case rrRenderState.StencilOp.KEEP: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return stencilBufferValue;}); break; case rrRenderState.StencilOp.ZERO: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return 0;}); break; case rrRenderState.StencilOp.REPLACE: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return clampedStencilRef;}); break; case rrRenderState.StencilOp.INCR: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return deMath.clamp(stencilBufferValue + 1, 0, (1 << numStencilBits) - 1);}); break; case rrRenderState.StencilOp.DECR: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return deMath.clamp(stencilBufferValue - 1, 0, (1 << numStencilBits) - 1);}); break; case rrRenderState.StencilOp.INCR_WRAP: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return (stencilBufferValue + 1) & ((1 << numStencilBits) - 1);}); break; case rrRenderState.StencilOp.DECR_WRAP: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return (stencilBufferValue - 1) & ((1 << numStencilBits) - 1);}); break; case rrRenderState.StencilOp.INVERT: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return (~stencilBufferValue) & ((1 << numStencilBits) - 1);}); break; default: throw new Error('Unrecognized stencil operation:' + op_name); } }; var passed = function(depthPassed) { return depthPassed;}; var failed = function(depthPassed) { return !depthPassed;}; switch_dpfail_or_dppass('dpFail', failed); switch_dpfail_or_dppass('dpPass', passed); }; /** * @param {Array} inputFragments Fragments to write * @param {Array} blendColor * @param {rrRenderState.BlendState} blendRGBState */ rrFragmentOperations.executeBlendFactorComputeRGB = function(inputFragments, blendColor, blendRGBState) { /** * @param {string} factor_name * @param {function(Array, Array, Array):Array} expression */ var sample_register_blend_factor = function(factor_name, expression) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { var src = frag.clampedBlendSrcColor; var src1 = frag.clampedBlendSrc1Color; var dst = frag.clampedBlendDstColor; frag[factor_name] = deMath.clampVector(expression(src, src1, dst), 0, 1); } } }; var switch_src_or_dst_factor_rgb = function(func_name, factor_name) { switch (blendRGBState[func_name]) { case rrRenderState.BlendFunc.ZERO: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [0, 0, 0];}); break; case rrRenderState.BlendFunc.ONE: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1, 1, 1];}); break; case rrRenderState.BlendFunc.SRC_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.swizzle(src, [0, 1, 2]);}); break; case rrRenderState.BlendFunc.ONE_MINUS_SRC_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.subtract([1, 1, 1], deMath.swizzle(src, [0, 1, 2]));}); break; case rrRenderState.BlendFunc.DST_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.swizzle(dst, [0, 1, 2]);}); break; case rrRenderState.BlendFunc.ONE_MINUS_DST_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.subtract([1, 1, 1], deMath.swizzle(dst, [0, 1, 2]));}); break; case rrRenderState.BlendFunc.SRC_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [src[3], src[3], src[3]];}); break; case rrRenderState.BlendFunc.ONE_MINUS_SRC_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1.0 - src[3], 1.0 - src[3], 1.0 - src[3]];}); break; case rrRenderState.BlendFunc.DST_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [dst[3], dst[3], dst[3]];}); break; case rrRenderState.BlendFunc.ONE_MINUS_DST_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1.0 - dst[3], 1.0 - dst[3], 1.0 - dst[3]];}); break; case rrRenderState.BlendFunc.CONSTANT_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.swizzle(blendColor, [0, 1, 2]);}); break; case rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.subtract([1, 1, 1], deMath.swizzle(blendColor, [0, 1, 2]));}); break; case rrRenderState.BlendFunc.CONSTANT_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [blendColor[3], blendColor[3], blendColor[3]];}); break; case rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1.0 - blendColor[3], 1.0 - blendColor[3], 1.0 - blendColor[3]];}); break; case rrRenderState.BlendFunc.SRC_ALPHA_SATURATE: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [Math.min(src[3], 1.0 - dst[3]), Math.min(src[3], 1.0 - dst[3]), Math.min(src[3], 1.0 - dst[3])];}); break; case rrRenderState.BlendFunc.SRC1_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.swizzle(src1, [0, 1, 2]);}); break; case rrRenderState.BlendFunc.ONE_MINUS_SRC1_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.subtract([1, 1, 1], deMath.swizzle(src1, [0, 1, 2]));}); break; case rrRenderState.BlendFunc.SRC1_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [src1[3], src1[3], src1[3]];}); break; case rrRenderState.BlendFunc.ONE_MINUS_SRC1_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1.0 - src1[3], 1.0 - src1[3], 1.0 - src1[3]];}); break; default: throw new Error('Unrecognized blend function:' + func_name); } }; switch_src_or_dst_factor_rgb('srcFunc', 'blendSrcFactorRGB'); switch_src_or_dst_factor_rgb('dstFunc', 'blendDstFactorRGB'); }; /** * @param {Array} inputFragments Fragments to write * @param {Array} blendColor * @param {rrRenderState.BlendState} blendAState */ rrFragmentOperations.executeBlendFactorComputeA = function(inputFragments, blendColor, blendAState) { /** * @param {string} factor_name * @param {function(Array, Array, Array):number} expression */ var sample_register_blend_factor = function(factor_name, expression) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { var src = frag.clampedBlendSrcColor; var src1 = frag.clampedBlendSrc1Color; var dst = frag.clampedBlendDstColor; frag[factor_name] = deMath.clamp(expression(src, src1, dst), 0, 1); } } }; var swictch_src_or_dst_factor_a = function(func_name, factor_name) { switch (blendAState[func_name]) { case rrRenderState.BlendFunc.ZERO: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 0.0;}); break; case rrRenderState.BlendFunc.ONE: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0;}); break; case rrRenderState.BlendFunc.SRC_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return src[3];}); break; case rrRenderState.BlendFunc.ONE_MINUS_SRC_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - src[3];}); break; case rrRenderState.BlendFunc.DST_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return dst[3];}); break; case rrRenderState.BlendFunc.ONE_MINUS_DST_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - dst[3];}); break; case rrRenderState.BlendFunc.SRC_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return src[3];}); break; case rrRenderState.BlendFunc.ONE_MINUS_SRC_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - src[3];}); break; case rrRenderState.BlendFunc.DST_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return dst[3];}); break; case rrRenderState.BlendFunc.ONE_MINUS_DST_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - dst[3];}); break; case rrRenderState.BlendFunc.CONSTANT_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return blendColor[3];}); break; case rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - blendColor[3];}); break; case rrRenderState.BlendFunc.CONSTANT_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return blendColor[3];}); break; case rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - blendColor[3];}); break; case rrRenderState.BlendFunc.SRC_ALPHA_SATURATE: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0;}); break; case rrRenderState.BlendFunc.SRC1_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return src1[3];}); break; case rrRenderState.BlendFunc.ONE_MINUS_SRC1_COLOR: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - src1[3];}); break; case rrRenderState.BlendFunc.SRC1_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return src1[3];}); break; case rrRenderState.BlendFunc.ONE_MINUS_SRC1_ALPHA: sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - src1[3];}); break; default: throw new Error('Unrecognized blend function:' + func_name); } }; swictch_src_or_dst_factor_a('srcFunc', 'blendSrcFactorA'); swictch_src_or_dst_factor_a('dstFunc', 'blendDstFactorA'); }; /** * @param {Array} inputFragments Fragments to write * @param {rrRenderState.BlendState} blendRGBState * @param {rrRenderState.BlendState} blendAState */ rrFragmentOperations.executeBlend = function(inputFragments, blendRGBState, blendAState) { var sample_register_blended_color = function(color_name, expression) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { var src = frag.clampedBlendSrcColor; var dst = frag.clampedBlendDstColor; frag[color_name] = expression(src, dst, frag); } } }; switch (blendRGBState.equation) { case rrRenderState.BlendEquation.ADD: sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.add(deMath.multiply(deMath.swizzle(src, [0, 1, 2]), frag.blendSrcFactorRGB), deMath.multiply(deMath.swizzle(dst, [0, 1, 2]), frag.blendDstFactorRGB));}); break; case rrRenderState.BlendEquation.SUBTRACT: sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.subtract(deMath.multiply(deMath.swizzle(src, [0, 1, 2]), frag.blendSrcFactorRGB), deMath.multiply(deMath.swizzle(dst, [0, 1, 2]), frag.blendDstFactorRGB));}); break; case rrRenderState.BlendEquation.REVERSE_SUBTRACT: sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.subtract(deMath.multiply(deMath.swizzle(dst, [0, 1, 2]), frag.blendDstFactorRGB), deMath.multiply(deMath.swizzle(src, [0, 1, 2]), frag.blendSrcFactorRGB));}); break; case rrRenderState.BlendEquation.MIN: sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.min(deMath.swizzle(src, [0, 1, 2]), deMath.swizzle(dst, [0, 1, 2]));}); break; case rrRenderState.BlendEquation.MAX: sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.max(deMath.swizzle(src, [0, 1, 2]), deMath.swizzle(dst, [0, 1, 2]));}); break; default: throw new Error('Unrecognized blend equation:' + blendRGBState.equation); } switch (blendAState.equation) { case rrRenderState.BlendEquation.ADD: sample_register_blended_color('blendedA', function(src, dst, frag) { return src[3] * frag.blendSrcFactorA + dst[3] * frag.blendDstFactorA;}); break; case rrRenderState.BlendEquation.SUBTRACT: sample_register_blended_color('blendedA', function(src, dst, frag) { return src[3] * frag.blendSrcFactorA - dst[3] * frag.blendDstFactorA;}); break; case rrRenderState.BlendEquation.REVERSE_SUBTRACT: sample_register_blended_color('blendedA', function(src, dst, frag) { return dst[3] * frag.blendDstFactorA - src[3] * frag.blendSrcFactorA;}); break; case rrRenderState.BlendEquation.MIN: sample_register_blended_color('blendedA', function(src, dst, frag) { return Math.min(src[3], dst[3]);}); break; case rrRenderState.BlendEquation.MAX: sample_register_blended_color('blendedA', function(src, dst, frag) { return Math.max(src[3], dst[3]);}); break; default: throw new Error('Unrecognized blend equation:' + blendAState.equation); } }; /** * @param {Array} inputFragments * @param {boolean} isSRGB * @param {tcuTexture.PixelBufferAccess} colorBuffer */ rrFragmentOperations.executeColorWrite = function(inputFragments, isSRGB, colorBuffer) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { var combinedColor = frag.blendedRGB.slice(); combinedColor[3] = frag.blendedA; if (isSRGB) combinedColor = tcuTextureUtil.linearToSRGB(combinedColor); colorBuffer.setPixel(combinedColor, 0, frag.pixelCoord[0], frag.pixelCoord[1]); } } }; /** * @param {Array} inputFragments * @param {Array} colorMaskFactor * @param {Array} colorMaskNegationFactor * @param {boolean} isSRGB * @param {tcuTexture.PixelBufferAccess} colorBuffer */ rrFragmentOperations.executeMaskedColorWrite = function(inputFragments, colorMaskFactor, colorMaskNegationFactor, isSRGB, colorBuffer) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { var fragSampleNdx = 0; var originalColor = colorBuffer.getPixel(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); var newColor = frag.blendedRGB.slice(); newColor[3] = frag.blendedA; if (isSRGB) newColor = tcuTextureUtil.linearToSRGB(newColor); newColor = deMath.add(deMath.multiply(colorMaskFactor, newColor), deMath.multiply(colorMaskNegationFactor, originalColor)); colorBuffer.setPixel(newColor, fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); } } }; /** * @param {Array} inputFragments * @param {Array} colorMask * @param {tcuTexture.PixelBufferAccess} colorBuffer */ rrFragmentOperations.executeSignedValueWrite = function(inputFragments, colorMask, colorBuffer) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { var fragSampleNdx = 0; var originalValue = colorBuffer.getPixelInt(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); var newValue = tcuTextureUtil.select(frag.signedValue, originalValue, colorMask); colorBuffer.setPixelInt(newValue, fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); } } }; /** * @param {Array} inputFragments * @param {Array} colorMask * @param {tcuTexture.PixelBufferAccess} colorBuffer */ rrFragmentOperations.executeUnsignedValueWrite = function(inputFragments, colorMask, colorBuffer) { for (var i = 0; i < inputFragments.length; i++) { var frag = inputFragments[i]; if (frag.isAlive) { var fragSampleNdx = 0; var originalValue = colorBuffer.getPixelInt(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); var newValue = tcuTextureUtil.select(frag.unsignedValue, originalValue, colorMask); colorBuffer.setPixelInt(newValue, fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); } } }; /** * @constructor */ rrFragmentOperations.FragmentProcessor = function() { /* TODO: implement */ }; });