// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= OpenGLState.h: OpenGL state definitions. =============================================================================*/ #pragma once #include "CoreTypes.h" #include "Misc/AssertionMacros.h" #include "HAL/UnrealMemory.h" #include "Containers/Array.h" #include "Math/Color.h" #include "Math/IntRect.h" #include "RHIDefinitions.h" #include "Containers/StaticArray.h" #include "RHI.h" #include "OpenGLResources.h" #define ZERO_FILLED_DUMMY_UNIFORM_BUFFER_SIZE 65536 class FRenderTarget; struct FOpenGLSamplerStateData { // These enum is just used to count the number of members in this struct enum EGLSamplerData { EGLSamplerData_WrapS, EGLSamplerData_WrapT, EGLSamplerData_WrapR, EGLSamplerData_LODBias, EGLSamplerData_MagFilter, EGLSamplerData_MinFilter, EGLSamplerData_MaxAniso, EGLSamplerData_CompareMode, EGLSamplerData_CompareFunc, EGLSamplerData_Num, }; GLint WrapS; GLint WrapT; GLint WrapR; GLint LODBias; GLint MagFilter; GLint MinFilter; GLint MaxAnisotropy; GLint CompareMode; GLint CompareFunc; FOpenGLSamplerStateData() : WrapS(GL_REPEAT) , WrapT(GL_REPEAT) , WrapR(GL_REPEAT) , LODBias(0) , MagFilter(GL_NEAREST) , MinFilter(GL_NEAREST) , MaxAnisotropy(1) , CompareMode(GL_NONE) , CompareFunc(GL_ALWAYS) { } }; class FOpenGLSamplerState : public FRHISamplerState { public: GLuint Resource; FOpenGLSamplerStateData Data; ~FOpenGLSamplerState(); }; struct FOpenGLRasterizerStateData { GLenum FillMode = GL_FILL; GLenum CullMode = GL_NONE; float DepthBias = 0.0f; float SlopeScaleDepthBias = 0.0f; ERasterizerDepthClipMode DepthClipMode = ERasterizerDepthClipMode::DepthClip; }; class FOpenGLRasterizerState : public FRHIRasterizerState { public: virtual bool GetInitializer(FRasterizerStateInitializerRHI& Init) override final; FOpenGLRasterizerStateData Data; }; struct FOpenGLDepthStencilStateData { bool bZEnable; bool bZWriteEnable; GLenum ZFunc; bool bStencilEnable; bool bTwoSidedStencilMode; GLenum StencilFunc; GLenum StencilFail; GLenum StencilZFail; GLenum StencilPass; GLenum CCWStencilFunc; GLenum CCWStencilFail; GLenum CCWStencilZFail; GLenum CCWStencilPass; uint32 StencilReadMask; uint32 StencilWriteMask; FOpenGLDepthStencilStateData() : bZEnable(false) , bZWriteEnable(true) , ZFunc(GL_LESS) , bStencilEnable(false) , bTwoSidedStencilMode(false) , StencilFunc(GL_ALWAYS) , StencilFail(GL_KEEP) , StencilZFail(GL_KEEP) , StencilPass(GL_KEEP) , CCWStencilFunc(GL_ALWAYS) , CCWStencilFail(GL_KEEP) , CCWStencilZFail(GL_KEEP) , CCWStencilPass(GL_KEEP) , StencilReadMask(0xFFFFFFFF) , StencilWriteMask(0xFFFFFFFF) { } }; class FOpenGLDepthStencilState : public FRHIDepthStencilState { public: virtual bool GetInitializer(FDepthStencilStateInitializerRHI& Init) override final; FOpenGLDepthStencilStateData Data; }; struct FOpenGLBlendStateData { struct FRenderTarget { bool bAlphaBlendEnable; GLenum ColorBlendOperation; GLenum ColorSourceBlendFactor; GLenum ColorDestBlendFactor; bool bSeparateAlphaBlendEnable; GLenum AlphaBlendOperation; GLenum AlphaSourceBlendFactor; GLenum AlphaDestBlendFactor; uint32 ColorWriteMaskR : 1; uint32 ColorWriteMaskG : 1; uint32 ColorWriteMaskB : 1; uint32 ColorWriteMaskA : 1; }; TStaticArray RenderTargets; bool bUseAlphaToCoverage; FOpenGLBlendStateData() { bUseAlphaToCoverage = false; for (int32 i = 0; i < MaxSimultaneousRenderTargets; ++i) { FRenderTarget& Target = RenderTargets[i]; Target.bAlphaBlendEnable = false; Target.ColorBlendOperation = GL_NONE; Target.ColorSourceBlendFactor = GL_NONE; Target.ColorDestBlendFactor = GL_NONE; Target.bSeparateAlphaBlendEnable = false; Target.AlphaBlendOperation = GL_NONE; Target.AlphaSourceBlendFactor = GL_NONE; Target.AlphaDestBlendFactor = GL_NONE; Target.ColorWriteMaskR = false; Target.ColorWriteMaskG = false; Target.ColorWriteMaskB = false; Target.ColorWriteMaskA = false; } } }; class FOpenGLBlendState : public FRHIBlendState { FBlendStateInitializerRHI RHIInitializer; public: FOpenGLBlendState(const FBlendStateInitializerRHI& Initializer) : RHIInitializer(Initializer) {} virtual bool GetInitializer(FBlendStateInitializerRHI& Init) override final { Init = RHIInitializer; return true; } FOpenGLBlendStateData Data; }; struct FTextureStage { class FOpenGLTexture* Texture; class FOpenGLShaderResourceView* SRV; GLenum Target; GLuint Resource; int32 LimitMip; bool bHasMips; int32 NumMips; FTextureStage() : Texture(NULL) , SRV(NULL) , Target(GL_NONE) , Resource(0) , LimitMip(-1) , bHasMips(false) , NumMips(0) { } }; struct FUAVStage { GLenum Format; GLuint Resource; GLenum Access; GLint Layer; bool bLayered; GLint Level; FUAVStage() : Format(GL_NONE) , Resource(0) , Access(GL_READ_WRITE) , Layer(0) , bLayered(false) , Level(0) { } }; #define FOpenGLCachedAttr_Invalid (void*)(UPTRINT)0xFFFFFFFF #define FOpenGLCachedAttr_SingleVertex (void*)(UPTRINT)0xFFFFFFFE struct FOpenGLCachedAttr { GLuint Size; GLenum Type; GLuint StreamOffset; GLuint StreamIndex; GLboolean bNormalized; GLboolean bShouldConvertToFloat; FOpenGLCachedAttr() : Size(), Type(0), StreamOffset(), StreamIndex(0xFFFFFFFF), bNormalized(), bShouldConvertToFloat() { } }; struct FOpenGLStream { GLuint VertexBufferResource; uint32 Stride; uint32 Offset; uint32 Divisor; FOpenGLStream() : VertexBufferResource(0) , Stride(0) , Offset(0) , Divisor(0) {} }; #define NUM_OPENGL_VERTEX_STREAMS 16 struct FOpenGLCommonState { TArray Textures; TArray SamplerStates; TArray UAVs; FOpenGLCommonState() {} virtual ~FOpenGLCommonState() { FOpenGLCommonState::CleanupResources(); } // NumCombinedTextures must be greater than or equal to FOpenGL::GetMaxCombinedTextureImageUnits() // NumCombinedUAVUnits must be greater than or equal to FOpenGL::GetMaxCombinedUAVUnits() virtual void InitializeResources(int32 NumCombinedTextures, int32 NumCombinedUAVUnits) { check(NumCombinedTextures >= FOpenGL::GetMaxCombinedTextureImageUnits()); check(NumCombinedUAVUnits >= FOpenGL::GetMaxCombinedUAVUnits()); check(Textures.IsEmpty() && SamplerStates.IsEmpty() && UAVs.Num() == 0); Textures.SetNum(NumCombinedTextures); SamplerStates.SetNumZeroed(NumCombinedTextures); UAVs.Reserve(NumCombinedUAVUnits); UAVs.AddDefaulted(NumCombinedUAVUnits); } virtual void CleanupResources() { SamplerStates.Empty(); Textures.Empty(); UAVs.Empty(); } }; struct FOpenGLContextState final : public FOpenGLCommonState { FOpenGLRasterizerStateData RasterizerState; FOpenGLDepthStencilStateData DepthStencilState; uint32 StencilRef; FOpenGLBlendStateData BlendState; GLuint Framebuffer; uint32 RenderTargetWidth; uint32 RenderTargetHeight; GLuint OcclusionQuery; GLuint Program; GLuint UniformBuffers[CrossCompiler::NUM_SHADER_STAGES*OGL_MAX_UNIFORM_BUFFER_BINDINGS]; GLuint UniformBufferOffsets[CrossCompiler::NUM_SHADER_STAGES*OGL_MAX_UNIFORM_BUFFER_BINDINGS]; TArray CachedSamplerStates; GLenum ActiveTexture; bool bScissorEnabled; FIntRect Scissor; FIntRect Viewport; float DepthMinZ; float DepthMaxZ; GLuint ArrayBufferBound; GLuint ElementArrayBufferBound; GLuint StorageBufferBound; GLuint PixelUnpackBufferBound; GLuint UniformBufferBound; FLinearColor ClearColor; uint16 ClearStencil; float ClearDepth; int32 FirstNonzeroRenderTarget; bool bAlphaToCoverageEnabled; bool bNonCoherentFramebufferFetchEnabled; FOpenGLVertexDeclaration* VertexDecl; FOpenGLCachedAttr VertexAttrs[NUM_OPENGL_VERTEX_STREAMS]; FOpenGLStream VertexStreams[NUM_OPENGL_VERTEX_STREAMS]; uint32 ActiveStreamMask; uint32 VertexAttrs_EnabledBits; FORCEINLINE bool GetVertexAttrEnabled(int32 Index) const { static_assert(NUM_OPENGL_VERTEX_STREAMS <= sizeof(VertexAttrs_EnabledBits) * 8, "Not enough bits in VertexAttrs_EnabledBits to store NUM_OPENGL_VERTEX_STREAMS"); return !!(VertexAttrs_EnabledBits & (1 << Index)); } FORCEINLINE void SetVertexAttrEnabled(int32 Index, bool bEnable) { if (bEnable) { VertexAttrs_EnabledBits |= (1 << Index); } else { VertexAttrs_EnabledBits &= ~(1 << Index); } } uint32 ActiveUAVMask; FOpenGLContextState() : StencilRef(0) , Framebuffer(0) , Program(0) , ActiveTexture(GL_TEXTURE0) , bScissorEnabled(false) , DepthMinZ(0.0f) , DepthMaxZ(1.0f) , ArrayBufferBound(0) , ElementArrayBufferBound(0) , StorageBufferBound(0) , PixelUnpackBufferBound(0) , UniformBufferBound(0) , ClearColor(-1, -1, -1, -1) , ClearStencil(0xFFFF) , ClearDepth(-1.0f) , FirstNonzeroRenderTarget(0) , bAlphaToCoverageEnabled(false) , bNonCoherentFramebufferFetchEnabled(false) , VertexDecl(0) , VertexAttrs() , VertexStreams() , ActiveStreamMask(0) , VertexAttrs_EnabledBits(0) , ActiveUAVMask(0) { Scissor.Min.X = Scissor.Min.Y = Scissor.Max.X = Scissor.Max.Y = 0; Viewport.Min.X = Viewport.Min.Y = Viewport.Max.X = Viewport.Max.Y = 0; FMemory::Memzero(UniformBuffers, sizeof(UniformBuffers)); FMemory::Memzero(UniformBufferOffsets, sizeof(UniformBufferOffsets)); } virtual void InitializeResources(int32 NumCombinedTextures, int32 NumCombinedUAVUnits) override { FOpenGLCommonState::InitializeResources(NumCombinedTextures, NumCombinedUAVUnits); CachedSamplerStates.Empty(NumCombinedTextures); CachedSamplerStates.AddZeroed(NumCombinedTextures); checkf(NumCombinedUAVUnits <= sizeof(ActiveUAVMask) * 8, TEXT("Not enough bits in ActiveUAVMask to store %d UAV units"), NumCombinedUAVUnits); } virtual void CleanupResources() override { CachedSamplerStates.Empty(); FOpenGLCommonState::CleanupResources(); } }; struct FOpenGLRHIState final : public FOpenGLCommonState { FOpenGLRasterizerStateData RasterizerState; FOpenGLDepthStencilStateData DepthStencilState; uint32 StencilRef; FOpenGLBlendStateData BlendState; GLuint Framebuffer; bool bScissorEnabled; FIntRect Scissor; FIntRect Viewport; float DepthMinZ; float DepthMaxZ; GLuint ZeroFilledDummyUniformBuffer; uint32 RenderTargetWidth; uint32 RenderTargetHeight; bool bAlphaToCoverageEnabled; // Pending framebuffer setup int32 NumRenderingSamples;// Only used with GL_EXT_multisampled_render_to_texture int32 FirstNonzeroRenderTarget; FOpenGLTexture* RenderTargets[MaxSimultaneousRenderTargets]; uint32 RenderTargetMipmapLevels[MaxSimultaneousRenderTargets]; uint32 RenderTargetArrayIndex[MaxSimultaneousRenderTargets]; FOpenGLTexture* DepthStencil; ERenderTargetStoreAction StencilStoreAction; uint32 DepthTargetWidth; uint32 DepthTargetHeight; bool bFramebufferSetupInvalid; // Information about pending BeginDraw[Indexed]PrimitiveUP calls. FOpenGLStream DynamicVertexStream; uint32 NumVertices; uint32 PrimitiveType; uint32 NumPrimitives; uint32 MinVertexIndex; uint32 IndexDataStride; FOpenGLStream Streams[NUM_OPENGL_VERTEX_STREAMS]; // we null this when the we dirty PackedGlobalUniformDirty. Thus we can skip all of CommitNonComputeShaderConstants if it matches the current program FOpenGLLinkedProgram* LinkedProgramAndDirtyFlag; FOpenGLShaderParameterCache* ShaderParameters; TRefCountPtr BoundShaderState; TRefCountPtr CurrentComputeShader; /** The RHI does not allow more than 14 constant buffers per shader stage due to D3D11 limits. */ enum { MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE = 14 }; /** Track the currently bound uniform buffers. */ FRHIUniformBuffer* BoundUniformBuffers[SF_NumStandardFrequencies][MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE]; uint32 BoundUniformBuffersDynamicOffset[SF_NumStandardFrequencies][MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE]; /** Array to track if any real (not emulated) uniform buffers have been bound since the last draw call */ bool bAnyDirtyRealUniformBuffers[SF_NumStandardFrequencies]; /** Bit array to track which uniform buffers have changed since the last draw call. */ bool bAnyDirtyGraphicsUniformBuffers; uint16 DirtyUniformBuffers[SF_NumStandardFrequencies]; // Used for if(!FOpenGL::SupportsFastBufferData()) uint32 UpVertexBufferBytes; uint32 UpIndexBufferBytes; uint32 UpStride; void* UpVertexBuffer; void* UpIndexBuffer; FOpenGLRHIState() : StencilRef(0) , Framebuffer(0) , bScissorEnabled(false) , DepthMinZ(0.0f) , DepthMaxZ(1.0f) , ZeroFilledDummyUniformBuffer(0) , RenderTargetWidth(0) , RenderTargetHeight(0) , bAlphaToCoverageEnabled(false) , NumRenderingSamples(1) , FirstNonzeroRenderTarget(-1) , DepthStencil(0) , StencilStoreAction(ERenderTargetStoreAction::ENoAction) , DepthTargetWidth(0) , DepthTargetHeight(0) , bFramebufferSetupInvalid(true) , NumVertices(0) , PrimitiveType(0) , NumPrimitives(0) , MinVertexIndex(0) , IndexDataStride(0) , LinkedProgramAndDirtyFlag(nullptr) , ShaderParameters(NULL) , BoundShaderState(NULL) , CurrentComputeShader(NULL) , UpVertexBufferBytes(0) , UpIndexBufferBytes(0) , UpVertexBuffer(0) , UpIndexBuffer(0) { Scissor.Min.X = Scissor.Min.Y = Scissor.Max.X = Scissor.Max.Y = 0; Viewport.Min.X = Viewport.Min.Y = Viewport.Max.X = Viewport.Max.Y = 0; FMemory::Memset( RenderTargets, 0, sizeof(RenderTargets) ); // setting all to 0 at start FMemory::Memset( RenderTargetMipmapLevels, 0, sizeof(RenderTargetMipmapLevels) ); // setting all to 0 at start FMemory::Memset( RenderTargetArrayIndex, 0, sizeof(RenderTargetArrayIndex) ); // setting all to 0 at start FMemory::Memset(BoundUniformBuffers, 0, sizeof(BoundUniformBuffers)); FMemory::Memset(BoundUniformBuffersDynamicOffset, 0u, sizeof(BoundUniformBuffersDynamicOffset)); } ~FOpenGLRHIState() { CleanupResources(); } virtual void InitializeResources(int32 NumCombinedTextures, int32 NumComputeUAVUnits) override; virtual void CleanupResources() override { delete [] ShaderParameters; ShaderParameters = NULL; FMemory::Memset(BoundUniformBuffers, 0, sizeof(BoundUniformBuffers)); FMemory::Memset(BoundUniformBuffersDynamicOffset, 0u, sizeof(BoundUniformBuffersDynamicOffset)); FOpenGLCommonState::CleanupResources(); } }; template<> struct TOpenGLResourceTraits { typedef FOpenGLSamplerState TConcreteType; }; template<> struct TOpenGLResourceTraits { typedef FOpenGLRasterizerState TConcreteType; }; template<> struct TOpenGLResourceTraits { typedef FOpenGLDepthStencilState TConcreteType; }; template<> struct TOpenGLResourceTraits { typedef FOpenGLBlendState TConcreteType; };