/* * 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 #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(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) || defined(APPLETV)) // 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) || defined(APPLETV)) // 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 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; } }