// Copyright 2016 The SwiftShader Authors. All Rights Reserved. // // 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. #include "Context.hpp" #include "Primitive.hpp" #include "Surface.hpp" #include "Shader/PixelShader.hpp" #include "Shader/VertexShader.hpp" #include "Common/Memory.hpp" #include "Common/Debug.hpp" #include namespace sw { extern bool perspectiveCorrection; bool halfIntegerCoordinates = false; // Pixel centers are not at integer coordinates bool symmetricNormalizedDepth = false; // [-1, 1] instead of [0, 1] bool booleanFaceRegister = false; bool fullPixelPositionRegister = false; bool leadingVertexFirst = false; // Flat shading uses first vertex, else last bool secondaryColor = false; // Specular lighting is applied after texturing bool colorsDefaultToZero = false; bool forceWindowed = false; bool quadLayoutEnabled = false; bool veryEarlyDepthTest = true; bool complementaryDepthBuffer = false; bool postBlendSRGB = false; bool exactColorRounding = false; TransparencyAntialiasing transparencyAntialiasing = TRANSPARENCY_NONE; bool forceClearRegisters = false; Context::Context() { init(); } Context::~Context() { } void *Context::operator new(size_t bytes) { return allocate((unsigned int)bytes); } void Context::operator delete(void *pointer, size_t bytes) { deallocate(pointer); } bool Context::isDrawPoint(bool fillModeAware) const { switch(drawType) { case DRAW_POINTLIST: case DRAW_INDEXEDPOINTLIST8: case DRAW_INDEXEDPOINTLIST16: case DRAW_INDEXEDPOINTLIST32: return true; case DRAW_LINELIST: case DRAW_LINESTRIP: case DRAW_LINELOOP: case DRAW_INDEXEDLINELIST8: case DRAW_INDEXEDLINESTRIP8: case DRAW_INDEXEDLINELOOP8: case DRAW_INDEXEDLINELIST16: case DRAW_INDEXEDLINESTRIP16: case DRAW_INDEXEDLINELOOP16: case DRAW_INDEXEDLINELIST32: case DRAW_INDEXEDLINESTRIP32: case DRAW_INDEXEDLINELOOP32: return false; case DRAW_TRIANGLELIST: case DRAW_TRIANGLESTRIP: case DRAW_TRIANGLEFAN: case DRAW_INDEXEDTRIANGLELIST8: case DRAW_INDEXEDTRIANGLESTRIP8: case DRAW_INDEXEDTRIANGLEFAN8: case DRAW_INDEXEDTRIANGLELIST16: case DRAW_INDEXEDTRIANGLESTRIP16: case DRAW_INDEXEDTRIANGLEFAN16: case DRAW_INDEXEDTRIANGLELIST32: case DRAW_INDEXEDTRIANGLESTRIP32: case DRAW_INDEXEDTRIANGLEFAN32: return fillModeAware ? fillMode == FILL_VERTEX : false; case DRAW_QUADLIST: return false; default: ASSERT(false); } return false; } bool Context::isDrawLine(bool fillModeAware) const { switch(drawType) { case DRAW_POINTLIST: case DRAW_INDEXEDPOINTLIST8: case DRAW_INDEXEDPOINTLIST16: case DRAW_INDEXEDPOINTLIST32: return false; case DRAW_LINELIST: case DRAW_LINESTRIP: case DRAW_LINELOOP: case DRAW_INDEXEDLINELIST8: case DRAW_INDEXEDLINESTRIP8: case DRAW_INDEXEDLINELOOP8: case DRAW_INDEXEDLINELIST16: case DRAW_INDEXEDLINESTRIP16: case DRAW_INDEXEDLINELOOP16: case DRAW_INDEXEDLINELIST32: case DRAW_INDEXEDLINESTRIP32: case DRAW_INDEXEDLINELOOP32: return true; case DRAW_TRIANGLELIST: case DRAW_TRIANGLESTRIP: case DRAW_TRIANGLEFAN: case DRAW_INDEXEDTRIANGLELIST8: case DRAW_INDEXEDTRIANGLESTRIP8: case DRAW_INDEXEDTRIANGLEFAN8: case DRAW_INDEXEDTRIANGLELIST16: case DRAW_INDEXEDTRIANGLESTRIP16: case DRAW_INDEXEDTRIANGLEFAN16: case DRAW_INDEXEDTRIANGLELIST32: case DRAW_INDEXEDTRIANGLESTRIP32: case DRAW_INDEXEDTRIANGLEFAN32: return fillModeAware ? fillMode == FILL_WIREFRAME : false; case DRAW_QUADLIST: return false; default: ASSERT(false); } return false; } bool Context::isDrawTriangle(bool fillModeAware) const { switch(drawType) { case DRAW_POINTLIST: case DRAW_INDEXEDPOINTLIST8: case DRAW_INDEXEDPOINTLIST16: case DRAW_INDEXEDPOINTLIST32: return false; case DRAW_LINELIST: case DRAW_LINESTRIP: case DRAW_LINELOOP: case DRAW_INDEXEDLINELIST8: case DRAW_INDEXEDLINESTRIP8: case DRAW_INDEXEDLINELOOP8: case DRAW_INDEXEDLINELIST16: case DRAW_INDEXEDLINESTRIP16: case DRAW_INDEXEDLINELOOP16: case DRAW_INDEXEDLINELIST32: case DRAW_INDEXEDLINESTRIP32: case DRAW_INDEXEDLINELOOP32: return false; case DRAW_TRIANGLELIST: case DRAW_TRIANGLESTRIP: case DRAW_TRIANGLEFAN: case DRAW_INDEXEDTRIANGLELIST8: case DRAW_INDEXEDTRIANGLESTRIP8: case DRAW_INDEXEDTRIANGLEFAN8: case DRAW_INDEXEDTRIANGLELIST16: case DRAW_INDEXEDTRIANGLESTRIP16: case DRAW_INDEXEDTRIANGLEFAN16: case DRAW_INDEXEDTRIANGLELIST32: case DRAW_INDEXEDTRIANGLESTRIP32: case DRAW_INDEXEDTRIANGLEFAN32: return fillModeAware ? fillMode == FILL_SOLID : true; case DRAW_QUADLIST: // Quads are broken up into triangles return fillModeAware ? fillMode == FILL_SOLID : true; default: ASSERT(false); } return true; } void Context::init() { for(int i = 0; i < 8; i++) { textureStage[i].init(i, &sampler[i], (i >= 1) ? &textureStage[i - 1] : 0); } // Set vertex streams to null stream for(int i = 0; i < MAX_VERTEX_INPUTS; i++) { input[i].defaults(); } fogStart = 0.0f; fogEnd = 1.0f; for(int i = 0; i < TEXTURE_IMAGE_UNITS; i++) textureWrap[i] = 0; for(int i = 0; i < 8; i++) texGen[i] = TEXGEN_PASSTHRU; for(int i = 0; i < 8; i++) textureTransformCount[i] = 0; for(int i = 0; i < 8; i++) textureTransformProject[i] = false; textureWrapActive = false; localViewer = true; normalizeNormals = false; for(int i = 0; i < RENDERTARGETS; ++i) { renderTarget[i] = nullptr; } depthBuffer = nullptr; stencilBuffer = nullptr; stencilEnable = false; stencilCompareMode = STENCIL_ALWAYS; stencilReference = 0; stencilMask = 0xFFFFFFFF; stencilFailOperation = OPERATION_KEEP; stencilPassOperation = OPERATION_KEEP; stencilZFailOperation = OPERATION_KEEP; stencilWriteMask = 0xFFFFFFFF; twoSidedStencil = false; stencilCompareModeCCW = STENCIL_ALWAYS; stencilReferenceCCW = 0; stencilMaskCCW = 0xFFFFFFFF; stencilFailOperationCCW = OPERATION_KEEP; stencilPassOperationCCW = OPERATION_KEEP; stencilZFailOperationCCW = OPERATION_KEEP; stencilWriteMaskCCW = 0xFFFFFFFF; setGlobalMipmapBias(0); lightingEnable = true; specularEnable = false; for(int i = 0; i < 8; i++) lightEnable[i] = false; for(int i = 0; i < 8; i++) worldLightPosition[i] = 0; alphaCompareMode = ALPHA_ALWAYS; alphaTestEnable = false; fillMode = FILL_SOLID; shadingMode = SHADING_GOURAUD; rasterizerDiscard = false; depthCompareMode = DEPTH_LESS; depthBufferEnable = true; depthWriteEnable = true; alphaBlendEnable = false; sourceBlendFactorState = BLEND_ONE; destBlendFactorState = BLEND_ZERO; blendOperationState = BLENDOP_ADD; separateAlphaBlendEnable = false; sourceBlendFactorStateAlpha = BLEND_ONE; destBlendFactorStateAlpha = BLEND_ZERO; blendOperationStateAlpha = BLENDOP_ADD; cullMode = CULL_CLOCKWISE; frontFacingCCW = true; alphaReference = 0.0f; depthBias = 0.0f; slopeDepthBias = 0.0f; for(int i = 0; i < RENDERTARGETS; i++) { colorWriteMask[i] = 0x0000000F; } ambientMaterialSource = MATERIAL_MATERIAL; diffuseMaterialSource = MATERIAL_COLOR1; specularMaterialSource = MATERIAL_COLOR2; emissiveMaterialSource = MATERIAL_MATERIAL; colorVertexEnable = true; fogEnable = false; pixelFogMode = FOG_NONE; vertexFogMode = FOG_NONE; wBasedFog = false; rangeFogEnable = false; indexedVertexBlendEnable = false; vertexBlendMatrixCount = 0; pixelShader = 0; vertexShader = 0; instanceID = 0; occlusionEnabled = false; transformFeedbackQueryEnabled = false; transformFeedbackEnabled = 0; pointSpriteEnable = false; pointScaleEnable = false; lineWidth = 1.0f; writeSRGB = false; sampleMask = 0xFFFFFFFF; colorLogicOpEnabled = false; logicalOperation = LOGICALOP_COPY; } const float &Context::exp2Bias() { return bias; } const Point &Context::getLightPosition(int light) { return worldLightPosition[light]; } void Context::setGlobalMipmapBias(float bias) { this->bias = exp2(bias + 0.5f); } void Context::setLightingEnable(bool lightingEnable) { this->lightingEnable = lightingEnable; } void Context::setSpecularEnable(bool specularEnable) { Context::specularEnable = specularEnable; } void Context::setLightEnable(int light, bool lightEnable) { Context::lightEnable[light] = lightEnable; } void Context::setLightPosition(int light, Point worldLightPosition) { Context::worldLightPosition[light] = worldLightPosition; } void Context::setAmbientMaterialSource(MaterialSource ambientMaterialSource) { Context::ambientMaterialSource = ambientMaterialSource; } void Context::setDiffuseMaterialSource(MaterialSource diffuseMaterialSource) { Context::diffuseMaterialSource = diffuseMaterialSource; } void Context::setSpecularMaterialSource(MaterialSource specularMaterialSource) { Context::specularMaterialSource = specularMaterialSource; } void Context::setEmissiveMaterialSource(MaterialSource emissiveMaterialSource) { Context::emissiveMaterialSource = emissiveMaterialSource; } void Context::setPointSpriteEnable(bool pointSpriteEnable) { Context::pointSpriteEnable = pointSpriteEnable; } void Context::setPointScaleEnable(bool pointScaleEnable) { Context::pointScaleEnable = pointScaleEnable; } bool Context::setDepthBufferEnable(bool depthBufferEnable) { bool modified = (Context::depthBufferEnable != depthBufferEnable); Context::depthBufferEnable = depthBufferEnable; return modified; } bool Context::setAlphaBlendEnable(bool alphaBlendEnable) { bool modified = (Context::alphaBlendEnable != alphaBlendEnable); Context::alphaBlendEnable = alphaBlendEnable; return modified; } bool Context::setSourceBlendFactor(BlendFactor sourceBlendFactor) { bool modified = (Context::sourceBlendFactorState != sourceBlendFactor); Context::sourceBlendFactorState = sourceBlendFactor; return modified; } bool Context::setDestBlendFactor(BlendFactor destBlendFactor) { bool modified = (Context::destBlendFactorState != destBlendFactor); Context::destBlendFactorState = destBlendFactor; return modified; } bool Context::setBlendOperation(BlendOperation blendOperation) { bool modified = (Context::blendOperationState != blendOperation); Context::blendOperationState = blendOperation; return modified; } bool Context::setSeparateAlphaBlendEnable(bool separateAlphaBlendEnable) { bool modified = (Context::separateAlphaBlendEnable != separateAlphaBlendEnable); Context::separateAlphaBlendEnable = separateAlphaBlendEnable; return modified; } bool Context::setSourceBlendFactorAlpha(BlendFactor sourceBlendFactorAlpha) { bool modified = (Context::sourceBlendFactorStateAlpha != sourceBlendFactorAlpha); Context::sourceBlendFactorStateAlpha = sourceBlendFactorAlpha; return modified; } bool Context::setDestBlendFactorAlpha(BlendFactor destBlendFactorAlpha) { bool modified = (Context::destBlendFactorStateAlpha != destBlendFactorAlpha); Context::destBlendFactorStateAlpha = destBlendFactorAlpha; return modified; } bool Context::setBlendOperationAlpha(BlendOperation blendOperationAlpha) { bool modified = (Context::blendOperationStateAlpha != blendOperationAlpha); Context::blendOperationStateAlpha = blendOperationAlpha; return modified; } bool Context::setColorWriteMask(int index, int colorWriteMask) { bool modified = (Context::colorWriteMask[index] != colorWriteMask); Context::colorWriteMask[index] = colorWriteMask; return modified; } bool Context::setWriteSRGB(bool sRGB) { bool modified = (Context::writeSRGB != sRGB); Context::writeSRGB = sRGB; return modified; } bool Context::setColorLogicOpEnabled(bool enabled) { bool modified = (Context::colorLogicOpEnabled != enabled); Context::colorLogicOpEnabled = enabled; return modified; } bool Context::setLogicalOperation(LogicalOperation logicalOperation) { bool modified = (Context::logicalOperation != logicalOperation); Context::logicalOperation = logicalOperation; return modified; } void Context::setColorVertexEnable(bool colorVertexEnable) { Context::colorVertexEnable = colorVertexEnable; } bool Context::fogActive() { if(!colorUsed()) return false; if(pixelShaderModel() >= 0x0300) return false; return fogEnable; } bool Context::pointSizeActive() { if(vertexShader) { return false; } return isDrawPoint(true) && (input[PointSize] || (!preTransformed && pointScaleActive())); } FogMode Context::pixelFogActive() { if(fogActive()) { return pixelFogMode; } return FOG_NONE; } bool Context::depthWriteActive() { if(!depthBufferActive()) return false; return depthWriteEnable; } bool Context::alphaTestActive() { if(transparencyAntialiasing != TRANSPARENCY_NONE) return true; if(!alphaTestEnable) return false; if(alphaCompareMode == ALPHA_ALWAYS) return false; if(alphaReference == 0.0f && alphaCompareMode == ALPHA_GREATEREQUAL) return false; return true; } bool Context::depthBufferActive() { return depthBuffer && depthBufferEnable; } bool Context::stencilActive() { return stencilBuffer && stencilEnable; } bool Context::vertexLightingActive() { if(vertexShader) { return false; } return lightingEnable && !preTransformed; } bool Context::texCoordActive(int coordinate, int component) { bool hasTexture = pointSpriteActive(); if(vertexShader) { if(!preTransformed) { if(vertexShader->getOutput(T0 + coordinate, component).usage == Shader::USAGE_TEXCOORD) { hasTexture = true; } } else { hasTexture = true; // FIXME: Check vertex buffer streams } } else { switch(texGen[coordinate]) { case TEXGEN_NONE: hasTexture = true; break; case TEXGEN_PASSTHRU: hasTexture = hasTexture || (component < input[TexCoord0 + textureStage[coordinate].texCoordIndex].count); break; case TEXGEN_NORMAL: hasTexture = hasTexture || (component <= 2); break; case TEXGEN_POSITION: hasTexture = hasTexture || (component <= 2); break; case TEXGEN_REFLECTION: hasTexture = hasTexture || (component <= 2); break; case TEXGEN_SPHEREMAP: hasTexture = hasTexture || (component <= 1); break; default: ASSERT(false); } } bool project = isProjectionComponent(coordinate, component); bool usesTexture = false; if(pixelShader) { usesTexture = pixelShader->usesTexture(coordinate, component) || project; } else { usesTexture = textureStage[coordinate].usesTexture() || project; } return hasTexture && usesTexture; } bool Context::texCoordActive(int coordinate) { return texCoordActive(coordinate, 0) || texCoordActive(coordinate, 1) || texCoordActive(coordinate, 2) || texCoordActive(coordinate, 3); } bool Context::isProjectionComponent(unsigned int coordinate, int component) { if(pixelShaderModel() <= 0x0103 && coordinate < 8 && textureTransformProject[coordinate]) { if(textureTransformCount[coordinate] == 2) { if(component == 1) return true; } else if(textureTransformCount[coordinate] == 3) { if(component == 2) return true; } else if(textureTransformCount[coordinate] == 4 || textureTransformCount[coordinate] == 0) { if(component == 3) return true; } } return false; } bool Context::vertexSpecularActive() { return vertexLightingActive() && specularEnable && vertexNormalActive(); } bool Context::vertexNormalActive() { if(vertexShader) { return false; } return input[Normal]; } bool Context::vertexLightActive(int i) { if(vertexShader) { return false; } return lightingEnable && lightEnable[i]; } MaterialSource Context::vertexDiffuseMaterialSourceActive() { if(vertexShader) { return MATERIAL_MATERIAL; } if(diffuseMaterialSource == MATERIAL_MATERIAL || !colorVertexEnable || (diffuseMaterialSource == MATERIAL_COLOR1 && !input[Color0]) || (diffuseMaterialSource == MATERIAL_COLOR2 && !input[Color1])) { return MATERIAL_MATERIAL; } return diffuseMaterialSource; } MaterialSource Context::vertexSpecularMaterialSourceActive() { if(vertexShader) { return MATERIAL_MATERIAL; } if(!colorVertexEnable || (specularMaterialSource == MATERIAL_COLOR1 && !input[Color0]) || (specularMaterialSource == MATERIAL_COLOR2 && !input[Color1])) { return MATERIAL_MATERIAL; } return specularMaterialSource; } MaterialSource Context::vertexAmbientMaterialSourceActive() { if(vertexShader) { return MATERIAL_MATERIAL; } if(!colorVertexEnable || (ambientMaterialSource == MATERIAL_COLOR1 && !input[Color0]) || (ambientMaterialSource == MATERIAL_COLOR2 && !input[Color1])) { return MATERIAL_MATERIAL; } return ambientMaterialSource; } MaterialSource Context::vertexEmissiveMaterialSourceActive() { if(vertexShader) { return MATERIAL_MATERIAL; } if(!colorVertexEnable || (emissiveMaterialSource == MATERIAL_COLOR1 && !input[Color0]) || (emissiveMaterialSource == MATERIAL_COLOR2 && !input[Color1])) { return MATERIAL_MATERIAL; } return emissiveMaterialSource; } bool Context::pointSpriteActive() { return isDrawPoint(true) && pointSpriteEnable; } bool Context::pointScaleActive() { if(vertexShader) { return false; } return isDrawPoint(true) && pointScaleEnable; } bool Context::alphaBlendActive() { if(!alphaBlendEnable) { return false; } if(!colorUsed()) { return false; } bool colorBlend = !(blendOperation() == BLENDOP_SOURCE && sourceBlendFactor() == BLEND_ONE); bool alphaBlend = separateAlphaBlendEnable ? !(blendOperationAlpha() == BLENDOP_SOURCE && sourceBlendFactorAlpha() == BLEND_ONE) : colorBlend; return colorBlend || alphaBlend; } LogicalOperation Context::colorLogicOp() { return colorLogicOpEnabled ? logicalOperation : LOGICALOP_COPY; } BlendFactor Context::sourceBlendFactor() { if(!alphaBlendEnable) return BLEND_ONE; switch(blendOperationState) { case BLENDOP_ADD: case BLENDOP_SUB: case BLENDOP_INVSUB: return sourceBlendFactorState; case BLENDOP_MIN: return BLEND_ONE; case BLENDOP_MAX: return BLEND_ONE; default: ASSERT(false); } return sourceBlendFactorState; } BlendFactor Context::destBlendFactor() { if(!alphaBlendEnable) return BLEND_ZERO; switch(blendOperationState) { case BLENDOP_ADD: case BLENDOP_SUB: case BLENDOP_INVSUB: return destBlendFactorState; case BLENDOP_MIN: return BLEND_ONE; case BLENDOP_MAX: return BLEND_ONE; default: ASSERT(false); } return destBlendFactorState; } BlendOperation Context::blendOperation() { if(!alphaBlendEnable) return BLENDOP_SOURCE; switch(blendOperationState) { case BLENDOP_ADD: if(sourceBlendFactor() == BLEND_ZERO) { if(destBlendFactor() == BLEND_ZERO) { return BLENDOP_NULL; } else { return BLENDOP_DEST; } } else if(sourceBlendFactor() == BLEND_ONE) { if(destBlendFactor() == BLEND_ZERO) { return BLENDOP_SOURCE; } else { return BLENDOP_ADD; } } else { if(destBlendFactor() == BLEND_ZERO) { return BLENDOP_SOURCE; } else { return BLENDOP_ADD; } } case BLENDOP_SUB: if(sourceBlendFactor() == BLEND_ZERO) { return BLENDOP_NULL; // Negative, clamped to zero } else if(sourceBlendFactor() == BLEND_ONE) { if(destBlendFactor() == BLEND_ZERO) { return BLENDOP_SOURCE; } else { return BLENDOP_SUB; } } else { if(destBlendFactor() == BLEND_ZERO) { return BLENDOP_SOURCE; } else { return BLENDOP_SUB; } } case BLENDOP_INVSUB: if(sourceBlendFactor() == BLEND_ZERO) { if(destBlendFactor() == BLEND_ZERO) { return BLENDOP_NULL; } else { return BLENDOP_DEST; } } else if(sourceBlendFactor() == BLEND_ONE) { if(destBlendFactor() == BLEND_ZERO) { return BLENDOP_NULL; // Negative, clamped to zero } else { return BLENDOP_INVSUB; } } else { if(destBlendFactor() == BLEND_ZERO) { return BLENDOP_NULL; // Negative, clamped to zero } else { return BLENDOP_INVSUB; } } case BLENDOP_MIN: return BLENDOP_MIN; case BLENDOP_MAX: return BLENDOP_MAX; default: ASSERT(false); } return blendOperationState; } BlendFactor Context::sourceBlendFactorAlpha() { if(!separateAlphaBlendEnable) { return sourceBlendFactor(); } else { switch(blendOperationStateAlpha) { case BLENDOP_ADD: case BLENDOP_SUB: case BLENDOP_INVSUB: return sourceBlendFactorStateAlpha; case BLENDOP_MIN: return BLEND_ONE; case BLENDOP_MAX: return BLEND_ONE; default: ASSERT(false); } return sourceBlendFactorStateAlpha; } } BlendFactor Context::destBlendFactorAlpha() { if(!separateAlphaBlendEnable) { return destBlendFactor(); } else { switch(blendOperationStateAlpha) { case BLENDOP_ADD: case BLENDOP_SUB: case BLENDOP_INVSUB: return destBlendFactorStateAlpha; case BLENDOP_MIN: return BLEND_ONE; case BLENDOP_MAX: return BLEND_ONE; default: ASSERT(false); } return destBlendFactorStateAlpha; } } BlendOperation Context::blendOperationAlpha() { if(!separateAlphaBlendEnable) { return blendOperation(); } else { switch(blendOperationStateAlpha) { case BLENDOP_ADD: if(sourceBlendFactorAlpha() == BLEND_ZERO) { if(destBlendFactorAlpha() == BLEND_ZERO) { return BLENDOP_NULL; } else { return BLENDOP_DEST; } } else if(sourceBlendFactorAlpha() == BLEND_ONE) { if(destBlendFactorAlpha() == BLEND_ZERO) { return BLENDOP_SOURCE; } else { return BLENDOP_ADD; } } else { if(destBlendFactorAlpha() == BLEND_ZERO) { return BLENDOP_SOURCE; } else { return BLENDOP_ADD; } } case BLENDOP_SUB: if(sourceBlendFactorAlpha() == BLEND_ZERO) { return BLENDOP_NULL; // Negative, clamped to zero } else if(sourceBlendFactorAlpha() == BLEND_ONE) { if(destBlendFactorAlpha() == BLEND_ZERO) { return BLENDOP_SOURCE; } else { return BLENDOP_SUB; } } else { if(destBlendFactorAlpha() == BLEND_ZERO) { return BLENDOP_SOURCE; } else { return BLENDOP_SUB; } } case BLENDOP_INVSUB: if(sourceBlendFactorAlpha() == BLEND_ZERO) { if(destBlendFactorAlpha() == BLEND_ZERO) { return BLENDOP_NULL; } else { return BLENDOP_DEST; } } else if(sourceBlendFactorAlpha() == BLEND_ONE) { if(destBlendFactorAlpha() == BLEND_ZERO) { return BLENDOP_NULL; // Negative, clamped to zero } else { return BLENDOP_INVSUB; } } else { if(destBlendFactorAlpha() == BLEND_ZERO) { return BLENDOP_NULL; // Negative, clamped to zero } else { return BLENDOP_INVSUB; } } case BLENDOP_MIN: return BLENDOP_MIN; case BLENDOP_MAX: return BLENDOP_MAX; default: ASSERT(false); } return blendOperationStateAlpha; } } bool Context::indexedVertexBlendActive() { if(vertexShader) { return false; } return indexedVertexBlendEnable; } int Context::vertexBlendMatrixCountActive() { if(vertexShader) { return 0; } return vertexBlendMatrixCount; } bool Context::localViewerActive() { if(vertexShader) { return false; } return localViewer; } bool Context::normalizeNormalsActive() { if(vertexShader) { return false; } return normalizeNormals; } FogMode Context::vertexFogModeActive() { if(vertexShader || !fogActive()) { return FOG_NONE; } return vertexFogMode; } bool Context::rangeFogActive() { if(vertexShader || !fogActive()) { return false; } return rangeFogEnable; } TexGen Context::texGenActive(int stage) { if(vertexShader || !texCoordActive(stage)) { return TEXGEN_PASSTHRU; } return texGen[stage]; } int Context::textureTransformCountActive(int stage) { if(vertexShader || !texCoordActive(stage)) { return 0; } return textureTransformCount[stage]; } int Context::texCoordIndexActive(int stage) { if(vertexShader || !texCoordActive(stage)) { return stage; } return textureStage[stage].texCoordIndex; } bool Context::perspectiveActive() { if(!colorUsed()) { return false; } if(!perspectiveCorrection) { return false; } if(isDrawPoint(true)) { return false; } return true; } bool Context::diffuseUsed() { return diffuseUsed(0) || diffuseUsed(1) || diffuseUsed(2) || diffuseUsed(3); } bool Context::diffuseUsed(int component) { if(!colorUsed()) { return false; } if(pixelShader) { return pixelShader->usesDiffuse(component); } // Directly using the diffuse input color for(int i = 0; i < 8; i++) { if(textureStage[i].isStageDisabled()) { break; } if(textureStage[i].usesDiffuse()) { return true; } } // Using the current color (initialized to diffuse) before it's overwritten for(int i = 0; i < 8; i++) { if(textureStage[i].usesCurrent() || textureStage[i].isStageDisabled()) // Current color contains diffuse before being overwritten { return true; } if(textureStage[i].writesCurrent()) { return false; } } return true; } bool Context::diffuseActive() { return diffuseActive(0) || diffuseActive(1) || diffuseActive(2) || diffuseActive(3); } bool Context::diffuseActive(int component) { if(!colorUsed()) { return false; } // Vertex processor provides diffuse component bool vertexDiffuse; if(vertexShader) { vertexDiffuse = vertexShader->getOutput(C0, component).active(); } else if(!preTransformed) { vertexDiffuse = input[Color0] || lightingEnable; } else { vertexDiffuse = input[Color0]; } // Pixel processor requires diffuse component bool pixelDiffuse = diffuseUsed(component); return vertexDiffuse && pixelDiffuse; } bool Context::specularUsed() { return Context::specularUsed(0) || Context::specularUsed(1) || Context::specularUsed(2) || Context::specularUsed(3); } bool Context::specularUsed(int component) { if(!colorUsed()) { return false; } if(pixelShader) { return pixelShader->usesSpecular(component); } bool pixelSpecular = specularEnable; for(int i = 0; i < 8; i++) { if(textureStage[i].isStageDisabled()) break; pixelSpecular = pixelSpecular || textureStage[i].usesSpecular(); } return pixelSpecular; } bool Context::specularActive() { return specularActive(0) || specularActive(1) || specularActive(2) || specularActive(3); } bool Context::specularActive(int component) { if(!colorUsed()) { return false; } // Vertex processor provides specular component bool vertexSpecular; if(!vertexShader) { vertexSpecular = input[Color1] || (lightingEnable && specularEnable); } else { vertexSpecular = vertexShader->getOutput(C1, component).active(); } // Pixel processor requires specular component bool pixelSpecular = specularUsed(component); return vertexSpecular && pixelSpecular; } bool Context::colorActive(int color, int component) { if(color == 0) { return diffuseActive(component); } else { return specularActive(component); } } bool Context::textureActive() { for(int i = 0; i < 8; i++) { if(textureActive(i)) { return true; } } return false; } bool Context::textureActive(int coordinate) { return textureActive(coordinate, 0) || textureActive(coordinate, 1) || textureActive(coordinate, 2) || textureActive(coordinate, 3); } bool Context::textureActive(int coordinate, int component) { if(!colorUsed()) { return false; } if(!texCoordActive(coordinate, component)) { return false; } if(textureTransformProject[coordinate] && pixelShaderModel() <= 0x0103) { if(textureTransformCount[coordinate] == 2) { if(component == 1) return true; } else if(textureTransformCount[coordinate] == 3) { if(component == 2) return true; } else if(textureTransformCount[coordinate] == 4 || textureTransformCount[coordinate] == 0) { if(component == 3) return true; } } if(!pixelShader) { bool texture = textureStage[coordinate].usesTexture(); bool cube = sampler[coordinate].hasCubeTexture(); bool volume = sampler[coordinate].hasVolumeTexture(); if(texture) { for(int i = coordinate; i >= 0; i--) { if(textureStage[i].stageOperation == TextureStage::STAGE_DISABLE) { return false; } } } switch(component) { case 0: return texture; case 1: return texture; case 2: return (texture && (cube || volume)); case 3: return false; } } else { return pixelShader->usesTexture(coordinate, component); } return false; } unsigned short Context::pixelShaderModel() const { return pixelShader ? pixelShader->getShaderModel() : 0x0000; } unsigned short Context::vertexShaderModel() const { return vertexShader ? vertexShader->getShaderModel() : 0x0000; } int Context::getMultiSampleCount() const { return renderTarget[0] ? renderTarget[0]->getMultiSampleCount() : 1; } int Context::getSuperSampleCount() const { return renderTarget[0] ? renderTarget[0]->getSuperSampleCount() : 1; } Format Context::renderTargetInternalFormat(int index) { if(renderTarget[index]) { return renderTarget[index]->getInternalFormat(); } else { return FORMAT_NULL; } } int Context::colorWriteActive() { return colorWriteActive(0) | colorWriteActive(1) | colorWriteActive(2) | colorWriteActive(3); } int Context::colorWriteActive(int index) { if(!renderTarget[index] || renderTarget[index]->getInternalFormat() == FORMAT_NULL) { return 0; } if(blendOperation() == BLENDOP_DEST && destBlendFactor() == BLEND_ONE && (!separateAlphaBlendEnable || (blendOperationAlpha() == BLENDOP_DEST && destBlendFactorAlpha() == BLEND_ONE))) { return 0; } return colorWriteMask[index]; } bool Context::colorUsed() { return colorWriteActive() || alphaTestActive() || (pixelShader && pixelShader->containsKill()); } }