// 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 <string.h>
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;
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());
}
}