You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/RenderDll/XRenderD3D9/DXGL/Implementation/GLState.cpp

532 lines
20 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Implementation of the functions used to create states
// and apply them to an OpenGL device
#include "RenderDll_precompiled.h"
#include "GLState.hpp"
#include "GLDevice.hpp"
namespace NCryOpenGL
{
bool GetComparisonFunc(GLenum& eGLCompareFunc, D3D11_COMPARISON_FUNC eD3DCompareFunc)
{
switch (eD3DCompareFunc)
{
case D3D11_COMPARISON_NEVER:
eGLCompareFunc = GL_NEVER;
break;
case D3D11_COMPARISON_LESS:
eGLCompareFunc = GL_LESS;
break;
case D3D11_COMPARISON_EQUAL:
eGLCompareFunc = GL_EQUAL;
break;
case D3D11_COMPARISON_LESS_EQUAL:
eGLCompareFunc = GL_LEQUAL;
break;
case D3D11_COMPARISON_GREATER:
eGLCompareFunc = GL_GREATER;
break;
case D3D11_COMPARISON_NOT_EQUAL:
eGLCompareFunc = GL_NOTEQUAL;
break;
case D3D11_COMPARISON_GREATER_EQUAL:
eGLCompareFunc = GL_GEQUAL;
break;
case D3D11_COMPARISON_ALWAYS:
eGLCompareFunc = GL_ALWAYS;
break;
default:
return false;
}
return true;
}
bool GetStencilOperation(GLenum& eGLStencilOp, D3D11_STENCIL_OP eD3DStencilOp)
{
switch (eD3DStencilOp)
{
case D3D11_STENCIL_OP_KEEP:
eGLStencilOp = GL_KEEP;
break;
case D3D11_STENCIL_OP_ZERO:
eGLStencilOp = GL_ZERO;
break;
case D3D11_STENCIL_OP_REPLACE:
eGLStencilOp = GL_REPLACE;
break;
case D3D11_STENCIL_OP_INCR_SAT:
eGLStencilOp = GL_INCR;
break;
case D3D11_STENCIL_OP_DECR_SAT:
eGLStencilOp = GL_DECR;
break;
case D3D11_STENCIL_OP_INVERT:
eGLStencilOp = GL_INVERT;
break;
case D3D11_STENCIL_OP_INCR:
eGLStencilOp = GL_INCR_WRAP;
break;
case D3D11_STENCIL_OP_DECR:
eGLStencilOp = GL_DECR_WRAP;
break;
default:
return false;
}
return true;
}
bool GetBlendEquation(GLenum& eGLBlendEquation, D3D11_BLEND_OP eD3DBlendOp)
{
switch (eD3DBlendOp)
{
case D3D11_BLEND_OP_ADD:
eGLBlendEquation = GL_FUNC_ADD;
break;
case D3D11_BLEND_OP_SUBTRACT:
eGLBlendEquation = GL_FUNC_SUBTRACT;
break;
case D3D11_BLEND_OP_REV_SUBTRACT:
eGLBlendEquation = GL_FUNC_REVERSE_SUBTRACT;
break;
case D3D11_BLEND_OP_MIN:
eGLBlendEquation = GL_MIN;
break;
case D3D11_BLEND_OP_MAX:
eGLBlendEquation = GL_MAX;
break;
default:
return false;
}
return true;
}
bool GetBlendFunction(GLenum& eGLBlendFunction, D3D11_BLEND eD3DBlend)
{
switch (eD3DBlend)
{
case D3D11_BLEND_ZERO:
eGLBlendFunction = GL_ZERO;
break;
case D3D11_BLEND_ONE:
eGLBlendFunction = GL_ONE;
break;
case D3D11_BLEND_SRC_COLOR:
eGLBlendFunction = GL_SRC_COLOR;
break;
case D3D11_BLEND_INV_SRC_COLOR:
eGLBlendFunction = GL_ONE_MINUS_SRC_COLOR;
break;
case D3D11_BLEND_SRC_ALPHA:
eGLBlendFunction = GL_SRC_ALPHA;
break;
case D3D11_BLEND_INV_SRC_ALPHA:
eGLBlendFunction = GL_ONE_MINUS_SRC_ALPHA;
break;
case D3D11_BLEND_DEST_ALPHA:
eGLBlendFunction = GL_DST_ALPHA;
break;
case D3D11_BLEND_INV_DEST_ALPHA:
eGLBlendFunction = GL_ONE_MINUS_DST_ALPHA;
break;
case D3D11_BLEND_DEST_COLOR:
eGLBlendFunction = GL_DST_COLOR;
break;
case D3D11_BLEND_INV_DEST_COLOR:
eGLBlendFunction = GL_ONE_MINUS_DST_COLOR;
break;
case D3D11_BLEND_SRC_ALPHA_SAT:
eGLBlendFunction = GL_SRC_ALPHA_SATURATE;
break;
case D3D11_BLEND_BLEND_FACTOR:
eGLBlendFunction = GL_CONSTANT_COLOR;
break;
case D3D11_BLEND_INV_BLEND_FACTOR:
eGLBlendFunction = GL_ONE_MINUS_CONSTANT_COLOR;
break;
#if !DXGLES
case D3D11_BLEND_SRC1_COLOR:
eGLBlendFunction = GL_SRC1_COLOR;
break;
case D3D11_BLEND_INV_SRC1_COLOR:
eGLBlendFunction = GL_ONE_MINUS_SRC1_COLOR;
break;
case D3D11_BLEND_SRC1_ALPHA:
eGLBlendFunction = GL_SRC1_ALPHA;
break;
case D3D11_BLEND_INV_SRC1_ALPHA:
eGLBlendFunction = GL_ONE_MINUS_SRC1_ALPHA;
break;
#endif
default:
return false;
}
return true;
}
bool InitializeChannelBlendState(
D3D11_BLEND_OP eBlendOp, D3D11_BLEND eSrcBlend, D3D11_BLEND eDestBlend,
SChannelBlendState& kChannel)
{
if (!GetBlendEquation(kChannel.m_eEquation, eBlendOp))
{
DXGL_ERROR("Invalid blend operator");
return false;
}
if (!GetBlendFunction(kChannel.m_kFunction.m_eSrc, eSrcBlend) ||
!GetBlendFunction(kChannel.m_kFunction.m_eDst, eDestBlend))
{
DXGL_ERROR("Invalid blend factor");
return false;
}
return true;
}
bool InitializeBlendState(const D3D11_BLEND_DESC& kDesc, SBlendState& kState, CContext* context)
{
if (kDesc.IndependentBlendEnable && !context->GetDevice()->IsFeatureSupported(eF_IndependentBlending))
{
DXGL_ERROR("Independent blend state is not supported");
return false;
}
COMPILE_TIME_ASSERT(DXGL_ARRAY_SIZE(kDesc.RenderTarget) == DXGL_ARRAY_SIZE(kState.m_kTargets));
kState.m_bIndependentBlendEnable = kDesc.IndependentBlendEnable == TRUE;
kState.m_bAlphaToCoverageEnable = kDesc.AlphaToCoverageEnable == TRUE;
uint32 uTargetEnd(kState.m_bIndependentBlendEnable ? DXGL_ARRAY_SIZE(kDesc.RenderTarget) : 1);
for (uint32 uTarget = 0; uTarget < uTargetEnd; ++uTarget)
{
const D3D11_RENDER_TARGET_BLEND_DESC& kRTDesc(kDesc.RenderTarget[uTarget]);
STargetBlendState& kRTState(kState.m_kTargets[uTarget]);
kRTState.m_kWriteMask.m_abRGBA[0] = (kRTDesc.RenderTargetWriteMask & D3D11_COLOR_WRITE_ENABLE_RED) != 0 ? GL_TRUE : GL_FALSE;
kRTState.m_kWriteMask.m_abRGBA[1] = (kRTDesc.RenderTargetWriteMask & D3D11_COLOR_WRITE_ENABLE_GREEN) != 0 ? GL_TRUE : GL_FALSE;
kRTState.m_kWriteMask.m_abRGBA[2] = (kRTDesc.RenderTargetWriteMask & D3D11_COLOR_WRITE_ENABLE_BLUE) != 0 ? GL_TRUE : GL_FALSE;
kRTState.m_kWriteMask.m_abRGBA[3] = (kRTDesc.RenderTargetWriteMask & D3D11_COLOR_WRITE_ENABLE_ALPHA) != 0 ? GL_TRUE : GL_FALSE;
kRTState.m_bEnable = kRTDesc.BlendEnable == TRUE;
if (kRTState.m_bEnable)
{
kRTState.m_bSeparateAlpha =
(kRTDesc.BlendOp != kRTDesc.BlendOpAlpha) ||
(kRTDesc.SrcBlend != kRTDesc.SrcBlendAlpha) ||
(kRTDesc.DestBlend != kRTDesc.DestBlendAlpha);
if (!InitializeChannelBlendState(kRTDesc.BlendOp, kRTDesc.SrcBlend, kRTDesc.DestBlend, kRTState.m_kRGB))
{
return false;
}
if (kRTState.m_bSeparateAlpha &&
!InitializeChannelBlendState(kRTDesc.BlendOpAlpha, kRTDesc.SrcBlendAlpha, kRTDesc.DestBlendAlpha, kRTState.m_kAlpha))
{
return false;
}
}
}
return true;
}
bool InitializeStencilFace(SDepthStencilState::SFace& kStencilFace, const D3D11_DEPTH_STENCILOP_DESC& kDesc, UINT8 uWriteMask, UINT8 uReadMask)
{
kStencilFace.m_uStencilWriteMask = uWriteMask;
kStencilFace.m_uStencilReadMask = uReadMask;
if (!GetComparisonFunc(kStencilFace.m_eFunction, kDesc.StencilFunc))
{
DXGL_ERROR("Invalid stencil comparison function");
return false;
}
if (!GetStencilOperation(kStencilFace.m_eStencilFailOperation, kDesc.StencilFailOp) ||
!GetStencilOperation(kStencilFace.m_eDepthFailOperation, kDesc.StencilDepthFailOp) ||
!GetStencilOperation(kStencilFace.m_eDepthPassOperation, kDesc.StencilPassOp))
{
DXGL_ERROR("Invalid stencil operation");
return false;
}
return true;
}
bool InitializeDepthStencilState(const D3D11_DEPTH_STENCIL_DESC& kDesc, SDepthStencilState& kState, CContext*)
{
kState.m_bDepthTestingEnabled = kDesc.DepthEnable == TRUE;
switch (kDesc.DepthWriteMask)
{
case D3D11_DEPTH_WRITE_MASK_ALL:
kState.m_bDepthWriteMask = GL_TRUE;
break;
case D3D11_DEPTH_WRITE_MASK_ZERO:
kState.m_bDepthWriteMask = GL_FALSE;
break;
default:
DXGL_ERROR("Invalid depth write mask");
return false;
}
if (kState.m_bDepthTestingEnabled)
{
if (!GetComparisonFunc(kState.m_eDepthTestFunc, kDesc.DepthFunc))
{
DXGL_ERROR("Invalid depth comparison function");
return false;
}
}
kState.m_bStencilTestingEnabled = kDesc.StencilEnable == TRUE;
if (kState.m_bStencilTestingEnabled)
{
if (!InitializeStencilFace(kState.m_kStencilFrontFaces, kDesc.FrontFace, kDesc.StencilWriteMask, kDesc.StencilReadMask) ||
!InitializeStencilFace(kState.m_kStencilBackFaces, kDesc.BackFace, kDesc.StencilWriteMask, kDesc.StencilReadMask))
{
return false;
}
}
return true;
}
bool InitializeRasterizerState(const D3D11_RASTERIZER_DESC& kDesc, SRasterizerState& kState, CContext*)
{
#if !DXGLES
switch (kDesc.FillMode)
{
case D3D11_FILL_SOLID:
kState.m_ePolygonMode = GL_FILL;
break;
case D3D11_FILL_WIREFRAME:
kState.m_ePolygonMode = GL_LINE;
break;
}
#else
if (kDesc.FillMode != D3D11_FILL_SOLID)
{
DXGL_WARNING("Fill modes other than solid are not supported on OpenGL ES. The setting will be ignored");
}
#endif
switch (kDesc.CullMode)
{
case D3D11_CULL_NONE:
kState.m_bCullingEnabled = false;
break;
case D3D11_CULL_FRONT:
kState.m_eCullFaceMode = GL_FRONT;
kState.m_bCullingEnabled = true;
break;
case D3D11_CULL_BACK:
kState.m_eCullFaceMode = GL_BACK;
kState.m_bCullingEnabled = true;
break;
}
#if CRY_OPENGL_FLIP_Y
kState.m_eFrontFaceMode = kDesc.FrontCounterClockwise ? GL_CW : GL_CCW;
#else
kState.m_eFrontFaceMode = kDesc.FrontCounterClockwise ? GL_CCW : GL_CW;
#endif
kState.m_fPolygonOffsetUnits = static_cast<GLfloat>(kDesc.DepthBias); // Same behavior as D3D11 except it's input as float
kState.m_fPolygonOffsetFactor = kDesc.SlopeScaledDepthBias;
kState.m_bPolygonOffsetEnabled = kDesc.DepthBias != 0.0f || kDesc.SlopeScaledDepthBias != 0.0f;
if (kDesc.DepthBiasClamp > 0.0f)
{
DXGL_WARNING("Direct3D Depth bias clamping is not supported by OpenGL. This setting will be ignored.");
}
kState.m_bScissorEnabled = kDesc.ScissorEnable == TRUE;
kState.m_bDepthClipEnabled = kDesc.DepthClipEnable == TRUE;
#if !DXGLES
kState.m_bLineSmoothEnabled = kDesc.AntialiasedLineEnable == TRUE;
kState.m_bMultisampleEnabled = kDesc.MultisampleEnable == TRUE;
#else
if (kDesc.AntialiasedLineEnable == TRUE)
{
DXGL_WARNING("Smooth line rasterization is not supported on OpenGL ES. This setting will be ignored.");
}
if (kDesc.MultisampleEnable == TRUE)
{
DXGL_WARNING("Specifying the multisampling mode globally is not supported on OpenGL ES. This setting will be ignored.");
}
#endif
return true;
}
bool GetTexFilterParams(GLenum& eMinFilter, GLenum& eMagFilter, bool& bAnisotropic, bool& bComparison, D3D11_FILTER eD3DFilter, bool bMip)
{
switch (eD3DFilter)
{
#define _FILTER_CASE(_D3D11FilterID, _MinFilter, _MagFilter, _MipFilter, _Anisotropic, _Comparison) \
case _D3D11FilterID: \
eMinFilter = bMip ? GL_ ## _MinFilter ## _MIPMAP_ ## _MipFilter : GL_ ## _MinFilter; \
eMagFilter = GL_ ## _MagFilter; \
bAnisotropic = _Anisotropic; \
bComparison = _Comparison; \
break;
_FILTER_CASE(D3D11_FILTER_MIN_MAG_MIP_POINT, NEAREST, NEAREST, NEAREST, false, false);
_FILTER_CASE(D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR, NEAREST, NEAREST, LINEAR, false, false);
_FILTER_CASE(D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, NEAREST, LINEAR, NEAREST, false, false);
_FILTER_CASE(D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR, NEAREST, LINEAR, LINEAR, false, false);
_FILTER_CASE(D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT, LINEAR, NEAREST, NEAREST, false, false);
_FILTER_CASE(D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, LINEAR, NEAREST, LINEAR, false, false);
_FILTER_CASE(D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT, LINEAR, LINEAR, NEAREST, false, false);
_FILTER_CASE(D3D11_FILTER_MIN_MAG_MIP_LINEAR, LINEAR, LINEAR, LINEAR, false, false);
_FILTER_CASE(D3D11_FILTER_ANISOTROPIC, LINEAR, LINEAR, LINEAR, true, false);
_FILTER_CASE(D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT, NEAREST, NEAREST, NEAREST, false, true);
_FILTER_CASE(D3D11_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR, NEAREST, NEAREST, LINEAR, false, true);
_FILTER_CASE(D3D11_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT, NEAREST, LINEAR, NEAREST, false, true);
_FILTER_CASE(D3D11_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR, NEAREST, LINEAR, LINEAR, false, true);
_FILTER_CASE(D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT, LINEAR, NEAREST, NEAREST, false, true);
_FILTER_CASE(D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR, LINEAR, NEAREST, LINEAR, false, true);
_FILTER_CASE(D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT, LINEAR, LINEAR, NEAREST, false, true);
_FILTER_CASE(D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR, LINEAR, LINEAR, LINEAR, false, true);
_FILTER_CASE(D3D11_FILTER_COMPARISON_ANISOTROPIC, LINEAR, LINEAR, LINEAR, true, true);
#undef _FILTER_CASE
default:
return false;
}
return true;
}
bool GetTexWrapParams(GLint& iTexWrap, D3D11_TEXTURE_ADDRESS_MODE eD3DAddressMode, CContext* pContext)
{
switch (eD3DAddressMode)
{
case D3D11_TEXTURE_ADDRESS_WRAP:
iTexWrap = GL_REPEAT;
break;
case D3D11_TEXTURE_ADDRESS_MIRROR:
iTexWrap = GL_MIRRORED_REPEAT;
break;
case D3D11_TEXTURE_ADDRESS_CLAMP:
iTexWrap = GL_CLAMP_TO_EDGE;
break;
case D3D11_TEXTURE_ADDRESS_BORDER:
#if !defined(IOS) // iOS cannot compile the below gl code
if (!pContext->GetDevice()->IsFeatureSupported(eF_TextureBorderClamp))
{
DXGL_WARNING("Sampler with texture border clamping is not supported");
iTexWrap = GL_CLAMP_TO_EDGE;
return true;
}
iTexWrap = GL_CLAMP_TO_BORDER_EXT;
#else
DXGL_WARNING("Sampler with texture border clamping is not supported");
//LYTODO: Their is code that calls this function that is dependent on this succeeding, so lie for now
//set to clamp to edge and succeed.
iTexWrap = GL_CLAMP_TO_EDGE;
return true;
#endif
break;
default:
DXGL_WARNING("Unsupported sampler address mode");
return false;
}
return true;
}
bool CreateSamplerObject(const D3D11_SAMPLER_DESC& kDesc, GLuint* puSampler, bool bMip, CContext* pContext)
{
GLenum eComparisonFunc, eMinFilter, eMagFilter;
bool bAnisotropic, bComparison;
GLint iWrapS, iWrapT, iWrapR;
if (!GetTexFilterParams(eMinFilter, eMagFilter, bAnisotropic, bComparison, kDesc.Filter, bMip) ||
!GetTexWrapParams(iWrapS, kDesc.AddressU, pContext) ||
!GetTexWrapParams(iWrapT, kDesc.AddressV, pContext) ||
!GetTexWrapParams(iWrapR, kDesc.AddressW, pContext))
{
return false;
}
if (bComparison && !GetComparisonFunc(eComparisonFunc, kDesc.ComparisonFunc))
{
return false;
}
CDevice* pDevice = pContext->GetDevice();
glGenSamplers(1, puSampler);
if (bComparison)
{
glSamplerParameteri(*puSampler, GL_TEXTURE_COMPARE_FUNC, eComparisonFunc);
}
if (pDevice->IsFeatureSupported(eF_TextureAnisotropicFiltering))
{
glSamplerParameterf(*puSampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, bAnisotropic ? (float)kDesc.MaxAnisotropy : 1.0f);
}
glSamplerParameteri(*puSampler, GL_TEXTURE_COMPARE_MODE, bComparison ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE);
glSamplerParameterf(*puSampler, GL_TEXTURE_MAX_LOD, kDesc.MaxLOD);
glSamplerParameteri(*puSampler, GL_TEXTURE_MAG_FILTER, eMagFilter);
glSamplerParameteri(*puSampler, GL_TEXTURE_MIN_FILTER, eMinFilter);
glSamplerParameterf(*puSampler, GL_TEXTURE_MIN_LOD, kDesc.MinLOD);
glSamplerParameteri(*puSampler, GL_TEXTURE_WRAP_S, iWrapS);
glSamplerParameteri(*puSampler, GL_TEXTURE_WRAP_T, iWrapT);
glSamplerParameteri(*puSampler, GL_TEXTURE_WRAP_R, iWrapR);
#if !defined(IOS) // iOS cannot compile the below gl code
if (pDevice->IsFeatureSupported(eF_TextureBorderClamp))
{
glSamplerParameterfv(*puSampler, GL_TEXTURE_BORDER_COLOR_EXT, kDesc.BorderColor);
}
#endif
if (kDesc.MipLODBias != 0.0f)
{
#if DXGLES
DXGL_WARNING("MipLODBias not supported on GL ES");
#else
glSamplerParameterf(*puSampler, GL_TEXTURE_LOD_BIAS, kDesc.MipLODBias);
#endif
}
return true;
}
bool InitializeSamplerState(const D3D11_SAMPLER_DESC& kDesc, SSamplerState& kSamplerState, CContext* pContext)
{
return
CreateSamplerObject(kDesc, &kSamplerState.m_uSamplerObjectMip, true, pContext) &&
CreateSamplerObject(kDesc, &kSamplerState.m_uSamplerObjectNoMip, false, pContext);
}
void ResetSamplerState(SSamplerState& kSamplerState)
{
AZStd::vector<GLuint> samplers;
if (kSamplerState.m_uSamplerObjectMip)
{
samplers.push_back(kSamplerState.m_uSamplerObjectMip);
}
if (kSamplerState.m_uSamplerObjectNoMip)
{
samplers.push_back(kSamplerState.m_uSamplerObjectNoMip);
}
if (!samplers.empty())
{
glDeleteSamplers(samplers.size(), samplers.data());
}
kSamplerState.m_uSamplerObjectMip = 0;
kSamplerState.m_uSamplerObjectNoMip = 0;
}
}