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/DXMETAL/Implementation/GLState.cpp

529 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 "MetalDevice.hpp"
namespace NCryMetal
{
// Confetti BEGIN: Igor Lobanchikov
bool GetComparisonFunc(MTLCompareFunction& eMTLCompareFunc, D3D11_COMPARISON_FUNC eD3DCompareFunc)
{
switch (eD3DCompareFunc)
{
case D3D11_COMPARISON_NEVER:
eMTLCompareFunc = MTLCompareFunctionNever;
break;
case D3D11_COMPARISON_LESS:
eMTLCompareFunc = MTLCompareFunctionLess;
break;
case D3D11_COMPARISON_EQUAL:
eMTLCompareFunc = MTLCompareFunctionEqual;
break;
case D3D11_COMPARISON_LESS_EQUAL:
eMTLCompareFunc = MTLCompareFunctionLessEqual;
break;
case D3D11_COMPARISON_GREATER:
eMTLCompareFunc = MTLCompareFunctionGreater;
break;
case D3D11_COMPARISON_NOT_EQUAL:
eMTLCompareFunc = MTLCompareFunctionNotEqual;
break;
case D3D11_COMPARISON_GREATER_EQUAL:
eMTLCompareFunc = MTLCompareFunctionGreaterEqual;
break;
case D3D11_COMPARISON_ALWAYS:
eMTLCompareFunc = MTLCompareFunctionAlways;
break;
default:
return false;
}
return true;
}
bool GetStencilOperation(MTLStencilOperation& eMTLStencilOp, D3D11_STENCIL_OP eD3DStencilOp)
{
switch (eD3DStencilOp)
{
case D3D11_STENCIL_OP_KEEP:
eMTLStencilOp = MTLStencilOperationKeep;
break;
case D3D11_STENCIL_OP_ZERO:
eMTLStencilOp = MTLStencilOperationZero;
break;
case D3D11_STENCIL_OP_REPLACE:
eMTLStencilOp = MTLStencilOperationReplace;
break;
case D3D11_STENCIL_OP_INCR_SAT:
eMTLStencilOp = MTLStencilOperationIncrementClamp;
break;
case D3D11_STENCIL_OP_DECR_SAT:
eMTLStencilOp = MTLStencilOperationDecrementClamp;
break;
case D3D11_STENCIL_OP_INVERT:
eMTLStencilOp = MTLStencilOperationInvert;
break;
case D3D11_STENCIL_OP_INCR:
eMTLStencilOp = MTLStencilOperationIncrementWrap;
break;
case D3D11_STENCIL_OP_DECR:
eMTLStencilOp = MTLStencilOperationDecrementWrap;
break;
default:
return false;
}
return true;
}
// Confetti End: Igor Lobanchikov
// Confetti BEGIN: Igor Lobanchikov
MTLColorWriteMask DX11ToMetalColorMask(UINT8 RenderTargetWriteMask)
{
uint32 res = ((RenderTargetWriteMask & D3D11_COLOR_WRITE_ENABLE_RED) ? MTLColorWriteMaskRed : 0)
| ((RenderTargetWriteMask & D3D11_COLOR_WRITE_ENABLE_GREEN) ? MTLColorWriteMaskGreen : 0)
| ((RenderTargetWriteMask & D3D11_COLOR_WRITE_ENABLE_BLUE) ? MTLColorWriteMaskBlue : 0)
| ((RenderTargetWriteMask & D3D11_COLOR_WRITE_ENABLE_ALPHA) ? MTLColorWriteMaskAlpha : 0);
return (MTLColorWriteMask)res;
}
bool DX11ToMetalBlendOperation(D3D11_BLEND_OP BlendOp, MTLBlendOperation& res)
{
switch (BlendOp)
{
case D3D11_BLEND_OP_ADD:
res = MTLBlendOperationAdd;
break;
case D3D11_BLEND_OP_SUBTRACT:
res = MTLBlendOperationSubtract;
break;
case D3D11_BLEND_OP_REV_SUBTRACT:
res = MTLBlendOperationReverseSubtract;
break;
case D3D11_BLEND_OP_MIN:
res = MTLBlendOperationMin;
break;
case D3D11_BLEND_OP_MAX:
res = MTLBlendOperationMax;
break;
default:
DXGL_ERROR("Invalid blend operator");
return false;
}
;
return true;
}
bool DX11ToMetalBlendFactor(D3D11_BLEND factor, bool bAlpha, MTLBlendFactor& res)
{
switch (factor)
{
case D3D11_BLEND_ZERO:
res = MTLBlendFactorZero;
break;
case D3D11_BLEND_ONE:
res = MTLBlendFactorOne;
break;
case D3D11_BLEND_SRC_COLOR:
res = MTLBlendFactorSourceColor;
break;
case D3D11_BLEND_INV_SRC_COLOR:
res = MTLBlendFactorOneMinusSourceColor;
break;
case D3D11_BLEND_SRC_ALPHA:
res = MTLBlendFactorSourceAlpha;
break;
case D3D11_BLEND_INV_SRC_ALPHA:
res = MTLBlendFactorOneMinusSourceAlpha;
break;
case D3D11_BLEND_DEST_ALPHA:
res = MTLBlendFactorDestinationAlpha;
break;
case D3D11_BLEND_INV_DEST_ALPHA:
res = MTLBlendFactorOneMinusDestinationAlpha;
break;
case D3D11_BLEND_DEST_COLOR:
res = MTLBlendFactorDestinationColor;
break;
case D3D11_BLEND_INV_DEST_COLOR:
res = MTLBlendFactorOneMinusDestinationColor;
break;
case D3D11_BLEND_SRC_ALPHA_SAT:
res = MTLBlendFactorSourceAlphaSaturated;
break;
case D3D11_BLEND_BLEND_FACTOR:
res = bAlpha ? MTLBlendFactorBlendAlpha : MTLBlendFactorBlendColor;
break;
case D3D11_BLEND_INV_BLEND_FACTOR:
res = bAlpha ? MTLBlendFactorOneMinusBlendAlpha : MTLBlendFactorOneMinusBlendColor;
break;
default:
DXGL_ERROR("Invalid blend factor");
return false;
}
return true;
}
// Confetti End: Igor Lobanchikov
bool InitializeBlendState(const D3D11_BLEND_DESC& kDesc, SBlendState& kState, CDevice*)
{
// Confetti BEGIN: Igor Lobanchikov
assert(!kDesc.AlphaToCoverageEnable && "Not Implemented yet");
{
assert(DXGL_ARRAY_SIZE(kDesc.RenderTarget) == DXGL_ARRAY_SIZE(kState.colorAttachements));
for (uint32 uTarget = 0; uTarget < DXGL_ARRAY_SIZE(kDesc.RenderTarget); ++uTarget)
{
if (uTarget && !kDesc.IndependentBlendEnable)
{
kState.colorAttachements[uTarget] = kState.colorAttachements[0];
}
else
{
const D3D11_RENDER_TARGET_BLEND_DESC& kRTDesc(kDesc.RenderTarget[uTarget]);
SMetalBlendState& kRTState(kState.colorAttachements[uTarget]);
// Igor: warning: ResetToDefault resets all members. Please, be careful and set other members after reset.
kRTState.blendingEnabled = kRTDesc.BlendEnable == TRUE;
if (kRTState.blendingEnabled)
{
if (!DX11ToMetalBlendOperation(kRTDesc.BlendOp, kRTState.rgbBlendOperation))
{
return false;
}
if (!DX11ToMetalBlendOperation(kRTDesc.BlendOpAlpha, kRTState.alphaBlendOperation))
{
return false;
}
if (!DX11ToMetalBlendFactor(kRTDesc.SrcBlend, false, kRTState.sourceRGBBlendFactor))
{
return false;
}
if (!DX11ToMetalBlendFactor(kRTDesc.DestBlend, false, kRTState.destinationRGBBlendFactor))
{
return false;
}
if (!DX11ToMetalBlendFactor(kRTDesc.SrcBlendAlpha, true, kRTState.sourceAlphaBlendFactor))
{
return false;
}
if (!DX11ToMetalBlendFactor(kRTDesc.DestBlendAlpha, true, kRTState.destinationAlphaBlendFactor))
{
return false;
}
}
else
{
kRTState.ResetToDefault();
}
kRTState.writeMask = DX11ToMetalColorMask(kRTDesc.RenderTargetWriteMask);
}
}
}
return true;
// Confetti End: Igor Lobanchikov
}
// Confetti BEGIN: Igor Lobanchikov
bool InitializeStencilFace(MTLStencilDescriptor* kStencilFace, const D3D11_DEPTH_STENCILOP_DESC& kDesc, UINT8 uWriteMask, UINT8 uReadMask)
{
kStencilFace.readMask = uReadMask;
kStencilFace.writeMask = uWriteMask;
MTLCompareFunction cmpFunc;
if (!GetComparisonFunc(cmpFunc, kDesc.StencilFunc))
{
DXGL_ERROR("Invalid stencil comparison function");
return false;
}
kStencilFace.stencilCompareFunction = cmpFunc;
MTLStencilOperation eStencilFailOperation;
MTLStencilOperation eDepthFailOperation;
MTLStencilOperation eDepthPassOperation;
if (!GetStencilOperation(eStencilFailOperation, kDesc.StencilFailOp) ||
!GetStencilOperation(eDepthFailOperation, kDesc.StencilDepthFailOp) ||
!GetStencilOperation(eDepthPassOperation, kDesc.StencilPassOp))
{
DXGL_ERROR("Invalid stencil operation");
return false;
}
kStencilFace.stencilFailureOperation = eStencilFailOperation;
kStencilFace.depthFailureOperation = eDepthFailOperation;
kStencilFace.depthStencilPassOperation = eDepthPassOperation;
return true;
}
bool InitializeDepthStencilState(const D3D11_DEPTH_STENCIL_DESC& kDesc, id<MTLDepthStencilState>& kState, CDevice* pDevice)
{
MTLDepthStencilDescriptor* desc = [[MTLDepthStencilDescriptor alloc] init];
desc.depthWriteEnabled = (kDesc.DepthWriteMask == D3D11_DEPTH_WRITE_MASK_ALL);
if (kDesc.DepthEnable == TRUE)
{
MTLCompareFunction cmpFunc;
if (!GetComparisonFunc(cmpFunc, kDesc.DepthFunc))
{
[desc release];
DXGL_ERROR("Invalid depth comparison function");
return false;
}
desc.depthCompareFunction = cmpFunc;
}
else
{
desc.depthCompareFunction = MTLCompareFunctionAlways;
}
if (kDesc.StencilEnable == TRUE)
{
if (!InitializeStencilFace(desc.frontFaceStencil, kDesc.FrontFace, kDesc.StencilWriteMask, kDesc.StencilReadMask) ||
!InitializeStencilFace(desc.backFaceStencil, kDesc.BackFace, kDesc.StencilWriteMask, kDesc.StencilReadMask))
{
[desc release];
return false;
}
}
id<MTLDevice> mtlDevice = pDevice->GetMetalDevice();
kState = [mtlDevice newDepthStencilStateWithDescriptor:desc];
[desc release];
return true;
}
// Confetti End: Igor Lobanchikov
bool InitializeRasterizerState(const D3D11_RASTERIZER_DESC& kDesc, SRasterizerState& kState, CDevice*)
{
// Confetti BEGIN: Igor Lobanchikov
switch (kDesc.FillMode)
{
case D3D11_FILL_SOLID:
kState.triangleFillMode = MTLTriangleFillModeFill;
break;
case D3D11_FILL_WIREFRAME:
kState.triangleFillMode = MTLTriangleFillModeLines;
break;
}
switch (kDesc.CullMode)
{
case D3D11_CULL_NONE:
kState.cullMode = MTLCullModeNone;
break;
case D3D11_CULL_FRONT:
kState.cullMode = MTLCullModeFront;
break;
case D3D11_CULL_BACK:
kState.cullMode = MTLCullModeBack;
break;
}
kState.frontFaceWinding = kDesc.FrontCounterClockwise ? MTLWindingCounterClockwise : MTLWindingClockwise;
kState.depthBias = (float)kDesc.DepthBias;
kState.depthBiasClamp = kDesc.DepthBiasClamp;
kState.depthSlopeScale = kDesc.SlopeScaledDepthBias;
if (!kDesc.DepthClipEnable)
{
kState.depthBias = 0;
kState.depthBiasClamp = 0;
kState.depthSlopeScale = 0;
}
kState.depthClipMode = kDesc.DepthClipEnable ? MTLDepthClipModeClip : MTLDepthClipModeClamp;
kState.scissorEnable = kDesc.ScissorEnable;
if (kDesc.AntialiasedLineEnable == TRUE)
{
DXGL_WARNING("Smooth line rasterization is not supported on Metal. This setting will be ignored.");
}
if (kDesc.MultisampleEnable == TRUE)
{
DXGL_WARNING("Specifying the multisampling mode globally is not supported on Metal. This setting will be ignored.");
}
// Confetti End: Igor Lobanchikov
return true;
}
// Confetti BEGIN: Igor Lobanchikov
bool DX11ToMetalTextureAddressMode(D3D11_TEXTURE_ADDRESS_MODE mode, MTLSamplerAddressMode& res)
{
switch (mode)
{
case D3D11_TEXTURE_ADDRESS_WRAP:
res = MTLSamplerAddressModeRepeat;
break;
case D3D11_TEXTURE_ADDRESS_MIRROR:
res = MTLSamplerAddressModeMirrorRepeat;
break;
case D3D11_TEXTURE_ADDRESS_CLAMP:
res = MTLSamplerAddressModeClampToEdge;
break;
case D3D11_TEXTURE_ADDRESS_BORDER:
DXGL_WARNING("Sampler with texture border clamping is not supported. Border Zero is used");
res = MTLSamplerAddressModeClampToZero;
break;
return true;
default:
DXGL_WARNING("Unsupported sampler address mode");
return false;
}
return true;
}
bool DX11ToMetalFilterMode(D3D11_FILTER FilterFunc, MTLSamplerMinMagFilter& minFilter, MTLSamplerMinMagFilter& maxFilter, MTLSamplerMipFilter& mipFilter, bool& bAnizotropic)
{
DXMETAL_TODO("Consider getting rid of the comparison samplers for Metal on the engine side.");
switch (FilterFunc)
{
#define _FILTER_CASE(_D3D11FilterID, _MinFilter, _MagFilter, _MipFilter, _Anisotropic, _Comparison) \
case _D3D11FilterID: \
minFilter = MTLSamplerMinMagFilter##_MinFilter; \
maxFilter = MTLSamplerMinMagFilter##_MagFilter; \
mipFilter = MTLSamplerMipFilter##_MipFilter; \
bAnizotropic = _Anisotropic; \
assert(!_Comparison || _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 UpdateMtlSamplerDescriptor(const D3D11_SAMPLER_DESC& kDesc, MTLSamplerDescriptor* desc)
{
MTLSamplerAddressMode addressMode;
if (!DX11ToMetalTextureAddressMode(kDesc.AddressU, addressMode))
{
return false;
}
desc.sAddressMode = addressMode;
if (!DX11ToMetalTextureAddressMode(kDesc.AddressV, addressMode))
{
return false;
}
desc.tAddressMode = addressMode;
if (!DX11ToMetalTextureAddressMode(kDesc.AddressW, addressMode))
{
return false;
}
desc.rAddressMode = addressMode;
if (kDesc.MipLODBias)
{
DXGL_WARNING("Metal sampler: MipLODBias is not supported.");
}
desc.maxAnisotropy = kDesc.MaxAnisotropy;
desc.lodMinClamp = kDesc.MinLOD;
desc.lodMaxClamp = kDesc.MaxLOD;
MTLSamplerMinMagFilter minFilter;
MTLSamplerMinMagFilter maxFilter;
MTLSamplerMipFilter mipFilter;
bool bAnizotropic;
if (!DX11ToMetalFilterMode(kDesc.Filter, minFilter, maxFilter, mipFilter, bAnizotropic))
{
DXGL_WARNING("Metal sampler: filter mode is not supported.");
return false;
}
if (!bAnizotropic)
{
desc.maxAnisotropy = 1;
}
desc.minFilter = minFilter;
desc.magFilter = maxFilter;
desc.mipFilter = mipFilter;
//D3D11_COMPARISON_FUNC ComparisonFunc;
DXMETAL_TODO("Consider getting rid of the comparison samplers for Metal on the engine side.");
// Igor: Motivation: Metal supports comparison sampler states but they must be declared in the
// shader body. This is due to the fact that while newer iOS GPUs support comparison sampling
// in hardware while the older ones emulate comparison sampler behaviour using shader maths.
// This leads to the fact that the shader compiler has to know comparison sampler configuration
// at compile time.
// At the moment we still allow to create the sampler object (which can't be configured to be
// comparison one) because the engine expects to have one. This might lead to confusion but at the
// moment it seems to be the best solution.
return true;
}
bool InitializeSamplerState(const D3D11_SAMPLER_DESC& kDesc, id<MTLSamplerState>& kState,
MTLSamplerDescriptor* mtlSamplerDesc, CDevice* pDevice)
{
bool result = UpdateMtlSamplerDescriptor(kDesc, mtlSamplerDesc);
if(result)
{
kState = [pDevice->GetMetalDevice() newSamplerStateWithDescriptor:mtlSamplerDesc];
}
return result;
}
void SetLodMinClamp(id<MTLSamplerState>& mtlSamplerState, MTLSamplerDescriptor* mtlSamplerDesc, const float lodMinClamp, CDevice* pDevice)
{
//once mtlSamplerState is created the behavior of a sampler state object is fixed and cannot be changed.
//Hence we create a new object that has the updated samplerdescriptor.
mtlSamplerDesc.lodMinClamp = lodMinClamp;
mtlSamplerState = [pDevice->GetMetalDevice() newSamplerStateWithDescriptor:mtlSamplerDesc];
}
}