Files
UnrealEngine/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLState.cpp
2025-05-18 13:04:45 +08:00

456 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
OpenGLState.cpp: OpenGL state implementation.
=============================================================================*/
#include "OpenGLState.h"
#include "Serialization/MemoryWriter.h"
#include "RHI.h"
#include "RHIUtilities.h"
#include "OpenGLDrv.h"
GLint GMaxOpenGLTextureFilterAnisotropic = 1;
// Hash of sampler states, used for caching sampler states and texture objects
static TMap<FSamplerStateInitializerRHI, FOpenGLSamplerState*> GSamplerStateCache;
namespace OpenGLConsoleVariables
{
extern int32 GOpenGLForceBilinear;
};
void EmptyGLSamplerStateCache()
{
for (auto Iter = GSamplerStateCache.CreateIterator(); Iter; ++Iter )
{
auto* State = Iter.Value();
// Manually release
State->Release();
}
GSamplerStateCache.Empty();
}
static GLenum TranslateAddressMode(ESamplerAddressMode AddressMode)
{
switch(AddressMode)
{
case AM_Clamp: return GL_CLAMP_TO_EDGE;
case AM_Mirror: return GL_MIRRORED_REPEAT;
case AM_Border: return UGL_CLAMP_TO_BORDER;
default: return GL_REPEAT;
};
}
static GLenum TranslateCullMode(ERasterizerCullMode CullMode)
{
switch(CullMode)
{
case CM_CW: return FOpenGL::SupportsClipControl() ? GL_BACK : GL_FRONT;
case CM_CCW: return FOpenGL::SupportsClipControl() ? GL_FRONT : GL_BACK;
default: return GL_NONE;
};
}
static GLenum TranslateFillMode(ERasterizerFillMode FillMode)
{
if ( FOpenGL::SupportsPolygonMode() )
{
switch(FillMode)
{
case FM_Point: return GL_POINT;
case FM_Wireframe: return GL_LINE;
default: break;
};
}
return GL_FILL;
}
static ERasterizerCullMode TranslateCullMode(GLenum CullMode)
{
if (FOpenGL::SupportsClipControl())
{
switch(CullMode)
{
case GL_BACK: return CM_CW;
case GL_FRONT: return CM_CCW;
default: return CM_None;
}
}
else
{
switch(CullMode)
{
case GL_FRONT: return CM_CW;
case GL_BACK: return CM_CCW;
default: return CM_None;
}
}
}
static ERasterizerFillMode TranslateFillMode(GLenum FillMode)
{
switch(FillMode)
{
case GL_POINT: return FM_Point;
case GL_LINE: return FM_Wireframe;
default: return FM_Solid;
}
}
static GLenum TranslateCompareFunction(ECompareFunction CompareFunction)
{
switch(CompareFunction)
{
case CF_Less: return GL_LESS;
case CF_LessEqual: return GL_LEQUAL;
case CF_Greater: return GL_GREATER;
case CF_GreaterEqual: return GL_GEQUAL;
case CF_Equal: return GL_EQUAL;
case CF_NotEqual: return GL_NOTEQUAL;
case CF_Never: return GL_NEVER;
default: return GL_ALWAYS;
};
}
static GLenum TranslateStencilOp(EStencilOp StencilOp)
{
switch(StencilOp)
{
case SO_Zero: return GL_ZERO;
case SO_Replace: return GL_REPLACE;
case SO_SaturatedIncrement: return GL_INCR;
case SO_SaturatedDecrement: return GL_DECR;
case SO_Invert: return GL_INVERT;
case SO_Increment: return GL_INCR_WRAP;
case SO_Decrement: return GL_DECR_WRAP;
default: return GL_KEEP;
};
}
static ECompareFunction TranslateCompareFunction(GLenum CompareFunction)
{
switch(CompareFunction)
{
case GL_LESS: return CF_Less;
case GL_LEQUAL: return CF_LessEqual;
case GL_GREATER: return CF_Greater;
case GL_GEQUAL: return CF_GreaterEqual;
case GL_EQUAL: return CF_Equal;
case GL_NOTEQUAL: return CF_NotEqual;
case GL_NEVER: return CF_Never;
default: return CF_Always;
};
}
static EStencilOp TranslateStencilOp(GLenum StencilOp)
{
switch(StencilOp)
{
case GL_ZERO: return SO_Zero;
case GL_REPLACE: return SO_Replace;
case GL_INCR: return SO_SaturatedIncrement;
case GL_DECR: return SO_SaturatedDecrement;
case GL_INVERT: return SO_Invert;
case GL_INCR_WRAP: return SO_Increment;
case GL_DECR_WRAP: return SO_Decrement;
default: return SO_Keep;
};
}
static GLenum TranslateBlendOp(EBlendOperation BlendOp)
{
switch(BlendOp)
{
case BO_Subtract: return GL_FUNC_SUBTRACT;
case BO_Min: return GL_MIN;
case BO_Max: return GL_MAX;
case BO_ReverseSubtract: return GL_FUNC_REVERSE_SUBTRACT;
default: return GL_FUNC_ADD;
};
}
static GLenum TranslateBlendFactor(EBlendFactor BlendFactor)
{
switch(BlendFactor)
{
case BF_One: return GL_ONE;
case BF_SourceColor: return GL_SRC_COLOR;
case BF_InverseSourceColor: return GL_ONE_MINUS_SRC_COLOR;
case BF_SourceAlpha: return GL_SRC_ALPHA;
case BF_InverseSourceAlpha: return GL_ONE_MINUS_SRC_ALPHA;
case BF_DestAlpha: return GL_DST_ALPHA;
case BF_InverseDestAlpha: return GL_ONE_MINUS_DST_ALPHA;
case BF_DestColor: return GL_DST_COLOR;
case BF_InverseDestColor: return GL_ONE_MINUS_DST_COLOR;
case BF_ConstantBlendFactor: return GL_CONSTANT_COLOR;
case BF_InverseConstantBlendFactor: return GL_ONE_MINUS_CONSTANT_COLOR;
default: return GL_ZERO;
};
}
static EBlendOperation TranslateBlendOp(GLenum BlendOp)
{
switch(BlendOp)
{
case GL_FUNC_SUBTRACT: return BO_Subtract;
case GL_MIN: return BO_Min;
case GL_MAX: return BO_Max;
case GL_FUNC_REVERSE_SUBTRACT: return BO_ReverseSubtract;
default: return BO_Add;
};
}
static EBlendFactor TranslateBlendFactor(GLenum BlendFactor)
{
switch(BlendFactor)
{
case GL_ONE: return BF_One;
case GL_SRC_COLOR: return BF_SourceColor;
case GL_ONE_MINUS_SRC_COLOR: return BF_InverseSourceColor;
case GL_SRC_ALPHA: return BF_SourceAlpha;
case GL_ONE_MINUS_SRC_ALPHA: return BF_InverseSourceAlpha;
case GL_DST_ALPHA: return BF_DestAlpha;
case GL_ONE_MINUS_DST_ALPHA: return BF_InverseDestAlpha;
case GL_DST_COLOR: return BF_DestColor;
case GL_ONE_MINUS_DST_COLOR: return BF_InverseDestColor;
case GL_CONSTANT_COLOR: return BF_ConstantBlendFactor;
case GL_ONE_MINUS_CONSTANT_COLOR: return BF_InverseConstantBlendFactor;
default: return BF_Zero;
};
}
FOpenGLSamplerState::~FOpenGLSamplerState()
{
VERIFY_GL_SCOPE();
FOpenGL::DeleteSamplers(1,&Resource);
}
FSamplerStateRHIRef FOpenGLDynamicRHI::RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer)
{
// Try to find an existing cached state
FOpenGLSamplerState** Found = GSamplerStateCache.Find(Initializer);
if (Found)
{
return *Found;
}
FOpenGLSamplerState* SamplerState = new FOpenGLSamplerState;
SamplerState->Data.WrapS = TranslateAddressMode( Initializer.AddressU );
SamplerState->Data.WrapT = TranslateAddressMode( Initializer.AddressV );
SamplerState->Data.WrapR = TranslateAddressMode( Initializer.AddressW );
SamplerState->Data.LODBias = Initializer.MipBias;
SamplerState->Data.MaxAnisotropy = 1;
const bool bComparisonEnabled = (Initializer.SamplerComparisonFunction != SCF_Never);
switch(Initializer.Filter)
{
case SF_AnisotropicPoint:
// This is set up like this in D3D11, so following suit.
// Otherwise we're getting QA reports about weird artifacting, because QA scenes are set up in
// D3D11 and AnisotropicPoint when Linear would be proper goes unnoticed there.
// Once someone decides to fix things in D3D11, I assume they'll look here to fix things up too. The code below is waiting.
// MagFilter = GL_NEAREST;
// MinFilter = bComparisonEnabled ? GL_NEAREST : GL_NEAREST_MIPMAP_NEAREST;
// break;
// PASS-THROUGH to AnisotropicLinear!
case SF_AnisotropicLinear:
SamplerState->Data.MagFilter = GL_LINEAR;
SamplerState->Data.MinFilter = bComparisonEnabled ? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR;
SamplerState->Data.MaxAnisotropy = FMath::Min<uint32>(ComputeAnisotropyRT(Initializer.MaxAnisotropy), GMaxOpenGLTextureFilterAnisotropic);
break;
case SF_Trilinear:
SamplerState->Data.MagFilter = GL_LINEAR;
SamplerState->Data.MinFilter = bComparisonEnabled ? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR;
break;
case SF_Bilinear:
SamplerState->Data.MagFilter = GL_LINEAR;
SamplerState->Data.MinFilter = GL_LINEAR_MIPMAP_NEAREST;
break;
default:
case SF_Point:
SamplerState->Data.MagFilter = GL_NEAREST;
SamplerState->Data.MinFilter = GL_NEAREST_MIPMAP_NEAREST;
break;
}
if( bComparisonEnabled )
{
check(Initializer.SamplerComparisonFunction == SCF_Less);
SamplerState->Data.CompareMode = GL_COMPARE_REF_TO_TEXTURE;
SamplerState->Data.CompareFunc = GL_LESS;
}
else
{
SamplerState->Data.CompareMode = GL_NONE;
}
if (OpenGLConsoleVariables::GOpenGLForceBilinear && (SamplerState->Data.MinFilter == GL_LINEAR_MIPMAP_LINEAR))
{
SamplerState->Data.MinFilter = GL_LINEAR_MIPMAP_NEAREST;
}
SamplerState->Resource = 0;
FRHICommandListImmediate::Get().EnqueueLambda([SamplerState](FRHICommandListImmediate&)
{
VERIFY_GL_SCOPE();
FOpenGL::GenSamplers( 1, &SamplerState->Resource);
FOpenGL::SetSamplerParameter(SamplerState->Resource, GL_TEXTURE_WRAP_S, SamplerState->Data.WrapS);
FOpenGL::SetSamplerParameter(SamplerState->Resource, GL_TEXTURE_WRAP_T, SamplerState->Data.WrapT);
if (FOpenGL::SupportsTexture3D())
{
FOpenGL::SetSamplerParameter(SamplerState->Resource, GL_TEXTURE_WRAP_R, SamplerState->Data.WrapR);
}
if (FOpenGL::SupportsTextureLODBias())
{
FOpenGL::SetSamplerParameter(SamplerState->Resource, GL_TEXTURE_LOD_BIAS, SamplerState->Data.LODBias);
}
FOpenGL::SetSamplerParameter(SamplerState->Resource, GL_TEXTURE_MIN_FILTER, SamplerState->Data.MinFilter);
FOpenGL::SetSamplerParameter(SamplerState->Resource, GL_TEXTURE_MAG_FILTER, SamplerState->Data.MagFilter);
if (FOpenGL::SupportsTextureFilterAnisotropic())
{
FOpenGL::SetSamplerParameter(SamplerState->Resource, GL_TEXTURE_MAX_ANISOTROPY_EXT, SamplerState->Data.MaxAnisotropy);
}
if (FOpenGL::SupportsTextureCompare())
{
FOpenGL::SetSamplerParameter(SamplerState->Resource, GL_TEXTURE_COMPARE_MODE, SamplerState->Data.CompareMode);
FOpenGL::SetSamplerParameter(SamplerState->Resource, GL_TEXTURE_COMPARE_FUNC, SamplerState->Data.CompareFunc);
}
});
// Manually add reference as we control the creation/destructions
SamplerState->AddRef();
GSamplerStateCache.Add(Initializer, SamplerState);
return SamplerState;
}
FRasterizerStateRHIRef FOpenGLDynamicRHI::RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer)
{
FOpenGLRasterizerState* RasterizerState = new FOpenGLRasterizerState;
RasterizerState->Data.CullMode = TranslateCullMode(Initializer.CullMode);
RasterizerState->Data.FillMode = TranslateFillMode(Initializer.FillMode);
RasterizerState->Data.DepthBias = Initializer.DepthBias;
RasterizerState->Data.SlopeScaleDepthBias = Initializer.SlopeScaleDepthBias;
RasterizerState->Data.DepthClipMode = Initializer.DepthClipMode;
return RasterizerState;
}
bool FOpenGLRasterizerState::GetInitializer(FRasterizerStateInitializerRHI& Init)
{
FMemory::Memzero(Init);
Init.CullMode = TranslateCullMode(Data.CullMode);
Init.FillMode = TranslateFillMode(Data.FillMode);
Init.DepthBias = Data.DepthBias;
Init.SlopeScaleDepthBias = Data.SlopeScaleDepthBias;
Init.DepthClipMode = Data.DepthClipMode;
return true;
}
FDepthStencilStateRHIRef FOpenGLDynamicRHI::RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer)
{
FOpenGLDepthStencilState* DepthStencilState = new FOpenGLDepthStencilState;
DepthStencilState->Data.bZEnable = Initializer.DepthTest != CF_Always || Initializer.bEnableDepthWrite;
DepthStencilState->Data.bZWriteEnable = Initializer.bEnableDepthWrite;
DepthStencilState->Data.ZFunc = TranslateCompareFunction(Initializer.DepthTest);
DepthStencilState->Data.bStencilEnable = Initializer.bEnableFrontFaceStencil || Initializer.bEnableBackFaceStencil;
DepthStencilState->Data.bTwoSidedStencilMode = Initializer.bEnableBackFaceStencil;
DepthStencilState->Data.StencilFunc = TranslateCompareFunction(Initializer.FrontFaceStencilTest);
DepthStencilState->Data.StencilFail = TranslateStencilOp(Initializer.FrontFaceStencilFailStencilOp);
DepthStencilState->Data.StencilZFail = TranslateStencilOp(Initializer.FrontFaceDepthFailStencilOp);
DepthStencilState->Data.StencilPass = TranslateStencilOp(Initializer.FrontFacePassStencilOp);
DepthStencilState->Data.CCWStencilFunc = TranslateCompareFunction(Initializer.BackFaceStencilTest);
DepthStencilState->Data.CCWStencilFail = TranslateStencilOp(Initializer.BackFaceStencilFailStencilOp);
DepthStencilState->Data.CCWStencilZFail = TranslateStencilOp(Initializer.BackFaceDepthFailStencilOp);
DepthStencilState->Data.CCWStencilPass = TranslateStencilOp(Initializer.BackFacePassStencilOp);
DepthStencilState->Data.StencilReadMask = Initializer.StencilReadMask;
DepthStencilState->Data.StencilWriteMask = Initializer.StencilWriteMask;
return DepthStencilState;
}
bool FOpenGLDepthStencilState::GetInitializer(FDepthStencilStateInitializerRHI& Init)
{
Init.bEnableBackFaceStencil = Data.bTwoSidedStencilMode;
Init.bEnableFrontFaceStencil = Data.bStencilEnable;
Init.bEnableDepthWrite = Data.bZWriteEnable;
Init.StencilReadMask = Data.StencilReadMask;
Init.StencilWriteMask = Data.StencilWriteMask;
Init.DepthTest = TranslateCompareFunction(Data.ZFunc);
Init.FrontFaceStencilTest = TranslateCompareFunction(Data.StencilFunc);
Init.FrontFaceStencilFailStencilOp = TranslateStencilOp(Data.StencilFail);
Init.FrontFaceDepthFailStencilOp = TranslateStencilOp(Data.StencilZFail);
Init.FrontFacePassStencilOp = TranslateStencilOp(Data.StencilPass);
Init.BackFaceStencilTest = TranslateCompareFunction(Data.CCWStencilFunc);
Init.BackFaceStencilFailStencilOp = TranslateStencilOp(Data.CCWStencilFail);
Init.BackFaceDepthFailStencilOp = TranslateStencilOp(Data.CCWStencilZFail);
Init.BackFacePassStencilOp = TranslateStencilOp(Data.CCWStencilPass);
return true;
}
FBlendStateRHIRef FOpenGLDynamicRHI::RHICreateBlendState(const FBlendStateInitializerRHI& Initializer)
{
FOpenGLBlendState* BlendState = new FOpenGLBlendState(Initializer);
BlendState->Data.bUseAlphaToCoverage = Initializer.bUseAlphaToCoverage;
for(uint32 RenderTargetIndex = 0;RenderTargetIndex < MaxSimultaneousRenderTargets;++RenderTargetIndex)
{
const FBlendStateInitializerRHI::FRenderTarget& RenderTargetInitializer =
Initializer.bUseIndependentRenderTargetBlendStates
? Initializer.RenderTargets[RenderTargetIndex]
: Initializer.RenderTargets[0];
FOpenGLBlendStateData::FRenderTarget& RenderTarget = BlendState->Data.RenderTargets[RenderTargetIndex];
RenderTarget.bAlphaBlendEnable =
RenderTargetInitializer.ColorBlendOp != BO_Add || RenderTargetInitializer.ColorDestBlend != BF_Zero || RenderTargetInitializer.ColorSrcBlend != BF_One ||
RenderTargetInitializer.AlphaBlendOp != BO_Add || RenderTargetInitializer.AlphaDestBlend != BF_Zero || RenderTargetInitializer.AlphaSrcBlend != BF_One;
RenderTarget.ColorBlendOperation = TranslateBlendOp(RenderTargetInitializer.ColorBlendOp);
RenderTarget.ColorSourceBlendFactor = TranslateBlendFactor(RenderTargetInitializer.ColorSrcBlend);
RenderTarget.ColorDestBlendFactor = TranslateBlendFactor(RenderTargetInitializer.ColorDestBlend);
RenderTarget.bSeparateAlphaBlendEnable =
RenderTargetInitializer.AlphaDestBlend != RenderTargetInitializer.ColorDestBlend ||
RenderTargetInitializer.AlphaSrcBlend != RenderTargetInitializer.ColorSrcBlend;
RenderTarget.AlphaBlendOperation = TranslateBlendOp(RenderTargetInitializer.AlphaBlendOp);
RenderTarget.AlphaSourceBlendFactor = TranslateBlendFactor(RenderTargetInitializer.AlphaSrcBlend);
RenderTarget.AlphaDestBlendFactor = TranslateBlendFactor(RenderTargetInitializer.AlphaDestBlend);
RenderTarget.ColorWriteMaskR = (RenderTargetInitializer.ColorWriteMask & CW_RED) != 0;
RenderTarget.ColorWriteMaskG = (RenderTargetInitializer.ColorWriteMask & CW_GREEN) != 0;
RenderTarget.ColorWriteMaskB = (RenderTargetInitializer.ColorWriteMask & CW_BLUE) != 0;
RenderTarget.ColorWriteMaskA = (RenderTargetInitializer.ColorWriteMask & CW_ALPHA) != 0;
}
return BlendState;
}
//!AB: moved from the header, since it was causing linker error when the header is included externally
void FOpenGLRHIState::InitializeResources(int32 NumCombinedTextures, int32 NumComputeUAVUnits)
{
check(!ShaderParameters);
FOpenGLCommonState::InitializeResources(NumCombinedTextures, NumComputeUAVUnits);
ShaderParameters = new FOpenGLShaderParameterCache[CrossCompiler::NUM_SHADER_STAGES];
ShaderParameters[CrossCompiler::SHADER_STAGE_VERTEX].InitializeResources(FOpenGL::GetMaxVertexUniformComponents() * 4 * sizeof(float));
ShaderParameters[CrossCompiler::SHADER_STAGE_PIXEL].InitializeResources(FOpenGL::GetMaxPixelUniformComponents() * 4 * sizeof(float));
ShaderParameters[CrossCompiler::SHADER_STAGE_GEOMETRY].InitializeResources(FOpenGL::GetMaxGeometryUniformComponents() * 4 * sizeof(float));
LinkedProgramAndDirtyFlag = nullptr;
ShaderParameters[CrossCompiler::SHADER_STAGE_COMPUTE].InitializeResources(FOpenGL::GetMaxComputeUniformComponents() * 4 * sizeof(float));
for (int32 Frequency = 0; Frequency < SF_NumStandardFrequencies; ++Frequency)
{
DirtyUniformBuffers[Frequency] = MAX_uint16;
bAnyDirtyRealUniformBuffers[Frequency] = true;
}
bAnyDirtyGraphicsUniformBuffers = true;
}