// 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;
|
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;
|
}
|
}
|
|
bool Context::colorWriteActive()
|
{
|
for (int i = 0; i < RENDERTARGETS; i++)
|
{
|
if (colorWriteActive(i))
|
{
|
return true;
|
}
|
}
|
|
return false;
|
}
|
|
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());
|
}
|
}
|