2019-12-26 14:45:42 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
|
|
|
OpenGLUniformBuffer.cpp: OpenGL Uniform buffer RHI implementation.
|
|
|
|
|
=============================================================================*/
|
|
|
|
|
|
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
|
|
|
#include "CoreMinimal.h"
|
|
|
|
|
#include "Stats/Stats.h"
|
|
|
|
|
#include "HAL/IConsoleManager.h"
|
2018-02-22 11:25:06 -05:00
|
|
|
#include "HAL/LowLevelMemTracker.h"
|
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
|
|
|
#include "RHI.h"
|
|
|
|
|
#include "OpenGLDrv.h"
|
2014-03-14 14:13:41 -04:00
|
|
|
#include "OpenGLDrvPrivate.h"
|
2019-03-11 11:09:10 -04:00
|
|
|
#include "Misc/ScopeLock.h"
|
2020-09-24 00:43:27 -04:00
|
|
|
#include "ShaderParameterStruct.h"
|
2014-04-30 23:51:07 -04:00
|
|
|
|
|
|
|
|
namespace OpenGLConsoleVariables
|
|
|
|
|
{
|
2020-02-12 13:27:19 -05:00
|
|
|
#if (PLATFORM_WINDOWS)
|
2014-04-30 23:51:07 -04:00
|
|
|
int32 RequestedUBOPoolSize = 1024*1024*16;
|
|
|
|
|
#else
|
|
|
|
|
int32 RequestedUBOPoolSize = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static FAutoConsoleVariableRef CVarUBOPoolSize(
|
|
|
|
|
TEXT("OpenGL.UBOPoolSize"),
|
|
|
|
|
RequestedUBOPoolSize,
|
|
|
|
|
TEXT("Size of the UBO pool, 0 disables UBO Pool"),
|
|
|
|
|
ECVF_ReadOnly
|
|
|
|
|
);
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
#if PLATFORM_ANDROID
|
|
|
|
|
int32 bUBODirectWrite = 0;
|
|
|
|
|
#else
|
|
|
|
|
int32 bUBODirectWrite = 1;
|
|
|
|
|
#endif
|
2014-04-30 23:51:07 -04:00
|
|
|
|
|
|
|
|
static FAutoConsoleVariableRef CVarUBODirectWrite(
|
|
|
|
|
TEXT("OpenGL.UBODirectWrite"),
|
|
|
|
|
bUBODirectWrite,
|
|
|
|
|
TEXT("Enables direct writes to the UBO via Buffer Storage"),
|
|
|
|
|
ECVF_ReadOnly
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
#define NUM_POOL_BUCKETS 45
|
|
|
|
|
|
|
|
|
|
#define NUM_SAFE_FRAMES 3
|
|
|
|
|
|
2014-06-17 18:27:26 -04:00
|
|
|
static const uint32 RequestedUniformBufferSizeBuckets[NUM_POOL_BUCKETS] = {
|
2014-03-14 14:13:41 -04:00
|
|
|
16,32,48,64,80,96,112,128, // 16-byte increments
|
|
|
|
|
160,192,224,256, // 32-byte increments
|
|
|
|
|
320,384,448,512, // 64-byte increments
|
|
|
|
|
640,768,896,1024, // 128-byte increments
|
|
|
|
|
1280,1536,1792,2048, // 256-byte increments
|
|
|
|
|
2560,3072,3584,4096, // 512-byte increments
|
|
|
|
|
5120,6144,7168,8192, // 1024-byte increments
|
|
|
|
|
10240,12288,14336,16384, // 2048-byte increments
|
|
|
|
|
20480,24576,28672,32768, // 4096-byte increments
|
|
|
|
|
40960,49152,57344,65536, // 8192-byte increments
|
|
|
|
|
|
|
|
|
|
// 65536 is current max uniform buffer size for Mac OS X.
|
|
|
|
|
|
2014-04-30 23:51:07 -04:00
|
|
|
0xFFFF0000 // Not max uint32 to allow rounding
|
2014-03-14 14:13:41 -04:00
|
|
|
};
|
|
|
|
|
|
2014-04-30 23:51:07 -04:00
|
|
|
// Maps desired size buckets to aligment actually
|
2014-06-17 18:27:26 -04:00
|
|
|
static TArray<uint32> UniformBufferSizeBuckets;
|
2014-04-30 23:51:07 -04:00
|
|
|
|
2018-02-22 11:25:06 -05:00
|
|
|
static FCriticalSection GGLUniformBufferPoolCS;
|
|
|
|
|
|
2014-04-30 23:51:07 -04:00
|
|
|
|
|
|
|
|
static inline bool IsSuballocatingUBOs()
|
|
|
|
|
{
|
|
|
|
|
#if SUBALLOCATED_CONSTANT_BUFFER
|
2014-08-20 08:27:17 -04:00
|
|
|
if (!GUseEmulatedUniformBuffers)
|
2014-04-30 23:51:07 -04:00
|
|
|
{
|
|
|
|
|
return OpenGLConsoleVariables::RequestedUBOPoolSize != 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline uint32 GetUBOPoolSize()
|
|
|
|
|
{
|
|
|
|
|
static uint32 UBOPoolSize = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
|
|
if ( UBOPoolSize == 0xFFFFFFFF )
|
|
|
|
|
{
|
|
|
|
|
GLint Alignment;
|
|
|
|
|
glGetIntegerv( GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &Alignment);
|
|
|
|
|
|
|
|
|
|
UBOPoolSize = (( OpenGLConsoleVariables::RequestedUBOPoolSize + Alignment - 1) / Alignment ) * Alignment;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return UBOPoolSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert bucket sizes to cbe compatible with present device
|
|
|
|
|
static void RemapBuckets()
|
|
|
|
|
{
|
|
|
|
|
if (!IsSuballocatingUBOs())
|
|
|
|
|
{
|
|
|
|
|
for (int32 Count = 0; Count < NUM_POOL_BUCKETS; Count++)
|
|
|
|
|
{
|
|
|
|
|
UniformBufferSizeBuckets.Push(RequestedUniformBufferSizeBuckets[Count]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GLint Alignment;
|
|
|
|
|
glGetIntegerv( GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &Alignment);
|
|
|
|
|
|
|
|
|
|
for (int32 Count = 0; Count < NUM_POOL_BUCKETS; Count++)
|
|
|
|
|
{
|
|
|
|
|
uint32 AlignedSize = ((RequestedUniformBufferSizeBuckets[Count] + Alignment - 1) / Alignment ) * Alignment;
|
|
|
|
|
if (!UniformBufferSizeBuckets.Contains(AlignedSize))
|
|
|
|
|
{
|
|
|
|
|
UniformBufferSizeBuckets.Push(AlignedSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
UE_LOG(LogRHI,Log,TEXT("Configured UBO bucket pool to %d buckets based on alignment of %d bytes"), UniformBufferSizeBuckets.Num(), Alignment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
static uint32 GetPoolBucketIndex(uint32 NumBytes)
|
|
|
|
|
{
|
2014-04-30 23:51:07 -04:00
|
|
|
if (UniformBufferSizeBuckets.Num() == 0)
|
|
|
|
|
{
|
2018-02-22 11:25:06 -05:00
|
|
|
check(IsInRenderingThread()); // this better be set up before there is any concurrency.
|
|
|
|
|
FScopeLock Lock(&GGLUniformBufferPoolCS);
|
2014-04-30 23:51:07 -04:00
|
|
|
RemapBuckets();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
check( UniformBufferSizeBuckets.Num() > 0);
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
unsigned long lower = 0;
|
2014-04-30 23:51:07 -04:00
|
|
|
unsigned long upper = UniformBufferSizeBuckets.Num();
|
2014-03-14 14:13:41 -04:00
|
|
|
unsigned long middle;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
middle = ( upper + lower ) >> 1;
|
|
|
|
|
if( NumBytes <= UniformBufferSizeBuckets[middle-1] )
|
|
|
|
|
{
|
|
|
|
|
upper = middle;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
lower = middle;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while( upper - lower > 1 );
|
|
|
|
|
|
|
|
|
|
check( NumBytes <= UniformBufferSizeBuckets[lower] );
|
|
|
|
|
check( (lower == 0 ) || ( NumBytes > UniformBufferSizeBuckets[lower-1] ) );
|
|
|
|
|
|
|
|
|
|
return lower;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline uint32 GetPoolBucketSize(uint32 NumBytes)
|
|
|
|
|
{
|
|
|
|
|
return UniformBufferSizeBuckets[GetPoolBucketIndex(NumBytes)];
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-18 11:56:25 -04:00
|
|
|
static FCriticalSection GGLEmulatedUniformBufferDataFactoryCS;
|
2014-03-14 14:13:41 -04:00
|
|
|
struct FUniformBufferDataFactory
|
|
|
|
|
{
|
|
|
|
|
FOpenGLEUniformBufferDataRef Create(uint32 Size, GLuint& OutResource)
|
|
|
|
|
{
|
2018-06-18 11:56:25 -04:00
|
|
|
FScopeLock Lock(&GGLEmulatedUniformBufferDataFactoryCS);
|
2014-03-14 14:13:41 -04:00
|
|
|
static GLuint TempCounter = 0;
|
|
|
|
|
OutResource = ++TempCounter;
|
|
|
|
|
|
|
|
|
|
FOpenGLEUniformBufferDataRef Buffer = new FOpenGLEUniformBufferData(Size);
|
|
|
|
|
Entries.Add(OutResource, Buffer);
|
|
|
|
|
return Buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FOpenGLEUniformBufferDataRef Get(GLuint Resource)
|
|
|
|
|
{
|
2018-06-18 11:56:25 -04:00
|
|
|
FScopeLock Lock(&GGLEmulatedUniformBufferDataFactoryCS);
|
2014-03-14 14:13:41 -04:00
|
|
|
FOpenGLEUniformBufferDataRef* Buffer = Entries.Find(Resource);
|
|
|
|
|
check(Buffer);
|
|
|
|
|
return *Buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Destroy(GLuint Resource)
|
|
|
|
|
{
|
2018-06-18 11:56:25 -04:00
|
|
|
FScopeLock Lock(&GGLEmulatedUniformBufferDataFactoryCS);
|
2014-03-14 14:13:41 -04:00
|
|
|
Entries.Remove(Resource);
|
|
|
|
|
}
|
2018-06-18 11:56:25 -04:00
|
|
|
private:
|
|
|
|
|
TMap<GLuint, FOpenGLEUniformBufferDataRef> Entries;
|
2014-03-14 14:13:41 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static FUniformBufferDataFactory UniformBufferDataFactory;
|
|
|
|
|
|
|
|
|
|
// Describes a uniform buffer in the free pool.
|
|
|
|
|
struct FPooledGLUniformBuffer
|
|
|
|
|
{
|
|
|
|
|
GLuint Buffer;
|
|
|
|
|
uint32 CreatedSize;
|
2014-04-30 23:51:07 -04:00
|
|
|
uint32 Offset;
|
2014-03-14 14:13:41 -04:00
|
|
|
uint32 FrameFreed;
|
2014-06-05 16:38:54 -04:00
|
|
|
uint8* PersistentlyMappedBuffer;
|
2014-03-14 14:13:41 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Pool of free uniform buffers, indexed by bucket for constant size search time.
|
2014-06-05 16:38:54 -04:00
|
|
|
static TArray<FPooledGLUniformBuffer> GLUniformBufferPool[NUM_POOL_BUCKETS][2];
|
2022-04-12 08:41:13 -04:00
|
|
|
static TArray<FPooledGLUniformBuffer> GLEmulatedUniformBufferPool[NUM_POOL_BUCKETS][2];
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// Uniform buffers that have been freed more recently than NumSafeFrames ago.
|
2014-06-05 16:38:54 -04:00
|
|
|
static TArray<FPooledGLUniformBuffer> SafeGLUniformBufferPools[NUM_SAFE_FRAMES][NUM_POOL_BUCKETS][2];
|
2022-04-12 08:41:13 -04:00
|
|
|
static TArray<FPooledGLUniformBuffer> SafeGLEmulatedUniformBufferPools[NUM_SAFE_FRAMES][NUM_POOL_BUCKETS][2];
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2018-02-22 11:25:06 -05:00
|
|
|
// Delete the uniform buffer's GL resource
|
|
|
|
|
static void ReleaseUniformBuffer(bool bEmulatedBufferData, GLuint Resource, uint32 AllocatedSize)
|
|
|
|
|
{
|
|
|
|
|
if (bEmulatedBufferData)
|
|
|
|
|
{
|
|
|
|
|
UniformBufferDataFactory.Destroy(Resource);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
check(Resource);
|
|
|
|
|
auto DeleteGLBuffer = [=]()
|
|
|
|
|
{
|
|
|
|
|
VERIFY_GL_SCOPE();
|
|
|
|
|
FOpenGL::DeleteBuffers(1, &Resource);
|
|
|
|
|
check(Resource != 0);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
RunOnGLRenderContextThread(MoveTemp(DeleteGLBuffer));
|
|
|
|
|
}
|
2021-02-16 00:46:28 -04:00
|
|
|
DecrementBufferMemory(GL_UNIFORM_BUFFER, AllocatedSize);
|
2018-02-22 11:25:06 -05:00
|
|
|
}
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
// Does per-frame global updating for the uniform buffer pool.
|
|
|
|
|
void BeginFrame_UniformBufferPoolCleanup()
|
|
|
|
|
{
|
2018-02-22 11:25:06 -05:00
|
|
|
FScopeLock Lock(&GGLUniformBufferPoolCS);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
int32 NumToCleanThisFrame = 10;
|
|
|
|
|
|
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_OpenGLUniformBufferCleanupTime);
|
|
|
|
|
|
2014-04-30 23:51:07 -04:00
|
|
|
if (!IsSuballocatingUBOs())
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2022-04-12 08:41:13 -04:00
|
|
|
// Clean a limited number of old entries to reduce hitching when leaving a large level
|
|
|
|
|
for( int32 StreamedIndex = 0; StreamedIndex < 2; ++StreamedIndex)
|
2014-06-05 16:38:54 -04:00
|
|
|
{
|
2022-04-12 08:41:13 -04:00
|
|
|
for (int32 BucketIndex = 0; BucketIndex < UniformBufferSizeBuckets.Num(); BucketIndex++)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2022-04-12 08:41:13 -04:00
|
|
|
for (int32 EntryIndex = GLUniformBufferPool[BucketIndex][StreamedIndex].Num() - 1; EntryIndex >= 0; EntryIndex--)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2022-04-12 08:41:13 -04:00
|
|
|
FPooledGLUniformBuffer& PoolEntry = GLUniformBufferPool[BucketIndex][StreamedIndex][EntryIndex];
|
2014-04-30 23:51:07 -04:00
|
|
|
|
2022-04-12 08:41:13 -04:00
|
|
|
check(PoolEntry.Buffer);
|
|
|
|
|
|
|
|
|
|
// Clean entries that are unlikely to be reused
|
|
|
|
|
if (GFrameNumberRenderThread - PoolEntry.FrameFreed > 30)
|
2014-06-05 16:38:54 -04:00
|
|
|
{
|
2022-04-12 08:41:13 -04:00
|
|
|
DEC_DWORD_STAT(STAT_OpenGLNumFreeUniformBuffers);
|
|
|
|
|
DEC_MEMORY_STAT_BY(STAT_OpenGLFreeUniformBufferMemory, PoolEntry.CreatedSize);
|
|
|
|
|
ReleaseUniformBuffer(false, PoolEntry.Buffer, PoolEntry.CreatedSize);
|
|
|
|
|
GLUniformBufferPool[BucketIndex][StreamedIndex].RemoveAtSwap(EntryIndex);
|
|
|
|
|
|
|
|
|
|
--NumToCleanThisFrame;
|
|
|
|
|
if (NumToCleanThisFrame == 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-06-05 16:38:54 -04:00
|
|
|
}
|
2014-04-30 23:51:07 -04:00
|
|
|
}
|
2022-04-12 08:41:13 -04:00
|
|
|
|
|
|
|
|
if (GUseEmulatedUniformBuffers && NumToCleanThisFrame != 0)
|
|
|
|
|
{
|
|
|
|
|
for (int32 EntryIndex = GLEmulatedUniformBufferPool[BucketIndex][StreamedIndex].Num() - 1; EntryIndex >= 0; EntryIndex--)
|
|
|
|
|
{
|
|
|
|
|
FPooledGLUniformBuffer& PoolEntry = GLEmulatedUniformBufferPool[BucketIndex][StreamedIndex][EntryIndex];
|
|
|
|
|
|
|
|
|
|
check(PoolEntry.Buffer);
|
|
|
|
|
|
|
|
|
|
// Clean entries that are unlikely to be reused
|
|
|
|
|
if (GFrameNumberRenderThread - PoolEntry.FrameFreed > 30)
|
|
|
|
|
{
|
|
|
|
|
DEC_DWORD_STAT(STAT_OpenGLNumFreeUniformBuffers);
|
|
|
|
|
DEC_MEMORY_STAT_BY(STAT_OpenGLFreeUniformBufferMemory, PoolEntry.CreatedSize);
|
|
|
|
|
ReleaseUniformBuffer(true, PoolEntry.Buffer, PoolEntry.CreatedSize);
|
|
|
|
|
GLEmulatedUniformBufferPool[BucketIndex][StreamedIndex].RemoveAtSwap(EntryIndex);
|
|
|
|
|
|
|
|
|
|
--NumToCleanThisFrame;
|
|
|
|
|
if (NumToCleanThisFrame == 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumToCleanThisFrame == 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumToCleanThisFrame == 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Index of the bucket that is now old enough to be reused
|
|
|
|
|
const int32 SafeFrameIndex = GFrameNumberRenderThread % NUM_SAFE_FRAMES;
|
|
|
|
|
|
|
|
|
|
// Merge the bucket into the free pool array
|
|
|
|
|
for( int32 StreamedIndex = 0; StreamedIndex < 2; ++StreamedIndex)
|
|
|
|
|
{
|
2014-04-30 23:51:07 -04:00
|
|
|
for (int32 BucketIndex = 0; BucketIndex < UniformBufferSizeBuckets.Num(); BucketIndex++)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
GLUniformBufferPool[BucketIndex][StreamedIndex].Append(SafeGLUniformBufferPools[SafeFrameIndex][BucketIndex][StreamedIndex]);
|
|
|
|
|
SafeGLUniformBufferPools[SafeFrameIndex][BucketIndex][StreamedIndex].Reset();
|
2022-04-12 08:41:13 -04:00
|
|
|
|
|
|
|
|
if (GUseEmulatedUniformBuffers)
|
|
|
|
|
{
|
|
|
|
|
GLEmulatedUniformBufferPool[BucketIndex][StreamedIndex].Append(SafeGLEmulatedUniformBufferPools[SafeFrameIndex][BucketIndex][StreamedIndex]);
|
|
|
|
|
SafeGLEmulatedUniformBufferPools[SafeFrameIndex][BucketIndex][StreamedIndex].Reset();
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool IsPoolingEnabled()
|
|
|
|
|
{
|
|
|
|
|
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.UniformBufferPooling"));
|
2018-06-29 15:49:49 -04:00
|
|
|
int32 CVarValue = IsInParallelRenderingThread() ? CVar->GetValueOnRenderThread() : CVar->GetValueOnGameThread();
|
2014-03-14 14:13:41 -04:00
|
|
|
return CVarValue != 0;
|
|
|
|
|
};
|
|
|
|
|
|
2014-04-30 23:51:07 -04:00
|
|
|
struct TUBOPoolBuffer
|
|
|
|
|
{
|
|
|
|
|
GLuint Resource;
|
|
|
|
|
uint32 ConsumedSpace;
|
|
|
|
|
uint32 AllocatedSpace;
|
|
|
|
|
uint8* Pointer;
|
|
|
|
|
};
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-04-30 23:51:07 -04:00
|
|
|
TArray<TUBOPoolBuffer> UBOPool;
|
|
|
|
|
|
|
|
|
|
static void SuballocateUBO( uint32 Size, GLuint& Resource, uint32& Offset, uint8*& Pointer)
|
|
|
|
|
{
|
2018-02-22 11:25:06 -05:00
|
|
|
VERIFY_GL_SCOPE();
|
|
|
|
|
|
2014-04-30 23:51:07 -04:00
|
|
|
check( Size <= GetUBOPoolSize());
|
|
|
|
|
// Find space in previously allocated pool buffers
|
|
|
|
|
for ( int32 Buffer = 0; Buffer < UBOPool.Num(); Buffer++)
|
|
|
|
|
{
|
|
|
|
|
TUBOPoolBuffer &Pool = UBOPool[Buffer];
|
|
|
|
|
if ( Size < (Pool.AllocatedSpace - Pool.ConsumedSpace))
|
|
|
|
|
{
|
|
|
|
|
Resource = Pool.Resource;
|
|
|
|
|
Offset = Pool.ConsumedSpace;
|
|
|
|
|
Pointer = Pool.Pointer ? Pool.Pointer + Offset : 0;
|
|
|
|
|
Pool.ConsumedSpace += Size;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No space was found to use, create a new Pool buffer
|
|
|
|
|
TUBOPoolBuffer Pool;
|
|
|
|
|
|
|
|
|
|
FOpenGL::GenBuffers( 1, &Pool.Resource);
|
|
|
|
|
|
|
|
|
|
CachedBindUniformBuffer(Pool.Resource);
|
|
|
|
|
|
|
|
|
|
if (FOpenGL::SupportsBufferStorage() && OpenGLConsoleVariables::bUBODirectWrite)
|
|
|
|
|
{
|
|
|
|
|
FOpenGL::BufferStorage( GL_UNIFORM_BUFFER, GetUBOPoolSize(), NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT );
|
2019-07-24 09:57:26 -04:00
|
|
|
Pool.Pointer = (uint8*)FOpenGL::MapBufferRange(GL_UNIFORM_BUFFER, 0, GetUBOPoolSize(), FOpenGL::EResourceLockMode::RLM_WriteOnlyPersistent);
|
2014-04-30 23:51:07 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glBufferData( GL_UNIFORM_BUFFER, GetUBOPoolSize(), 0, GL_DYNAMIC_DRAW);
|
|
|
|
|
Pool.Pointer = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Pointer = Pool.Pointer;
|
|
|
|
|
|
|
|
|
|
INC_MEMORY_STAT_BY(STAT_OpenGLFreeUniformBufferMemory, GetUBOPoolSize());
|
|
|
|
|
|
|
|
|
|
Pool.ConsumedSpace = Size;
|
|
|
|
|
Pool.AllocatedSpace = GetUBOPoolSize();
|
|
|
|
|
|
|
|
|
|
Resource = Pool.Resource;
|
|
|
|
|
Offset = 0;
|
|
|
|
|
|
|
|
|
|
UBOPool.Push(Pool);
|
|
|
|
|
|
|
|
|
|
UE_LOG(LogRHI,Log,TEXT("Allocated new buffer for uniform Pool %d buffers with %d bytes"),UBOPool.Num(), UBOPool.Num()*GetUBOPoolSize());
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-11 22:25:04 -05:00
|
|
|
static uint32 UniqueUniformBufferID()
|
|
|
|
|
{
|
|
|
|
|
// make it atomic?
|
|
|
|
|
static uint32 GUniqueUniformBufferID = 0;
|
|
|
|
|
return ++GUniqueUniformBufferID;
|
|
|
|
|
}
|
2014-06-05 16:38:54 -04:00
|
|
|
|
2021-08-19 23:53:29 -04:00
|
|
|
FOpenGLUniformBuffer::FOpenGLUniformBuffer(const FRHIUniformBufferLayout* InLayout)
|
2014-06-05 16:38:54 -04:00
|
|
|
: FRHIUniformBuffer(InLayout)
|
2018-02-22 11:25:06 -05:00
|
|
|
, Resource(0)
|
|
|
|
|
, Offset(0)
|
|
|
|
|
, PersistentlyMappedBuffer(nullptr)
|
2018-12-11 22:25:04 -05:00
|
|
|
, UniqueID(UniqueUniformBufferID())
|
2018-02-22 11:25:06 -05:00
|
|
|
, AllocatedSize(0)
|
|
|
|
|
, bStreamDraw(false)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2022-04-12 08:41:13 -04:00
|
|
|
bIsEmulatedUniformBuffer = GUseEmulatedUniformBuffers && !InLayout->bNoEmulatedUniformBuffer;
|
2014-06-05 16:38:54 -04:00
|
|
|
}
|
|
|
|
|
|
2018-02-22 11:25:06 -05:00
|
|
|
void FOpenGLUniformBuffer::SetGLUniformBufferParams(GLuint InResource, uint32 InOffset, uint8* InPersistentlyMappedBuffer, uint32 InAllocatedSize, FOpenGLEUniformBufferDataRef InEmulatedBuffer, bool bInStreamDraw)
|
|
|
|
|
{
|
|
|
|
|
Resource = InResource;
|
|
|
|
|
Offset = InOffset;
|
|
|
|
|
PersistentlyMappedBuffer = InPersistentlyMappedBuffer;
|
|
|
|
|
EmulatedBufferData = InEmulatedBuffer;
|
|
|
|
|
AllocatedSize = InAllocatedSize;
|
|
|
|
|
bStreamDraw = bInStreamDraw;
|
|
|
|
|
|
2019-03-29 13:42:02 -04:00
|
|
|
LLM(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Default, ((uint8*)this)+1, InAllocatedSize)); //+1 because ptr must be unique for LLM
|
2018-02-22 11:25:06 -05:00
|
|
|
}
|
|
|
|
|
|
2014-06-05 16:38:54 -04:00
|
|
|
FOpenGLUniformBuffer::~FOpenGLUniformBuffer()
|
|
|
|
|
{
|
2021-05-18 13:24:16 -04:00
|
|
|
AccessFence.WaitFenceRenderThreadOnly();
|
|
|
|
|
CopyFence.WaitFenceRenderThreadOnly();
|
2018-02-22 11:25:06 -05:00
|
|
|
|
2014-06-05 16:38:54 -04:00
|
|
|
if (Resource != 0)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-06-05 16:38:54 -04:00
|
|
|
if (IsPoolingEnabled())
|
|
|
|
|
{
|
|
|
|
|
FPooledGLUniformBuffer NewEntry;
|
|
|
|
|
NewEntry.Buffer = Resource;
|
|
|
|
|
NewEntry.Offset = Offset;
|
|
|
|
|
NewEntry.FrameFreed = GFrameNumberRenderThread;
|
|
|
|
|
NewEntry.CreatedSize = AllocatedSize;
|
|
|
|
|
NewEntry.PersistentlyMappedBuffer = PersistentlyMappedBuffer;
|
|
|
|
|
|
|
|
|
|
int StreamedIndex = bStreamDraw ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
// Add to this frame's array of free uniform buffers
|
|
|
|
|
const int32 SafeFrameIndex = GFrameNumberRenderThread % NUM_SAFE_FRAMES;
|
|
|
|
|
const uint32 BucketIndex = GetPoolBucketIndex(AllocatedSize);
|
|
|
|
|
|
|
|
|
|
check(AllocatedSize == UniformBufferSizeBuckets[BucketIndex]);
|
|
|
|
|
// this might fail with sizes > 65536; handle it then by extending the range? sizes > 65536 are presently unsupported on Mac OS X.
|
|
|
|
|
|
2018-02-22 11:25:06 -05:00
|
|
|
FScopeLock Lock(&GGLUniformBufferPoolCS);
|
2022-04-12 08:41:13 -04:00
|
|
|
|
|
|
|
|
if (GUseEmulatedUniformBuffers && !GetLayout().bNoEmulatedUniformBuffer)
|
|
|
|
|
{
|
|
|
|
|
SafeGLEmulatedUniformBufferPools[SafeFrameIndex][BucketIndex][StreamedIndex].Add(NewEntry);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SafeGLUniformBufferPools[SafeFrameIndex][BucketIndex][StreamedIndex].Add(NewEntry);
|
|
|
|
|
}
|
2014-06-05 16:38:54 -04:00
|
|
|
INC_DWORD_STAT(STAT_OpenGLNumFreeUniformBuffers);
|
|
|
|
|
INC_MEMORY_STAT_BY(STAT_OpenGLFreeUniformBufferMemory, AllocatedSize);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-02-22 11:25:06 -05:00
|
|
|
ReleaseUniformBuffer(IsValidRef(EmulatedBufferData), Resource, AllocatedSize);
|
|
|
|
|
Resource = 0;
|
2014-06-05 16:38:54 -04:00
|
|
|
}
|
2019-03-29 13:42:02 -04:00
|
|
|
|
|
|
|
|
LLM(FLowLevelMemTracker::Get().OnLowLevelFree(ELLMTracker::Default, ((uint8*)this)+1)); //+1 because ptr must be unique for LLM
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 11:25:06 -05:00
|
|
|
|
2021-08-19 23:53:29 -04:00
|
|
|
static void SetLayoutTable(FOpenGLUniformBuffer* NewUniformBuffer, const void* Contents, const FRHIUniformBufferLayout* Layout, EUniformBufferValidation Validation)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2021-08-19 23:53:29 -04:00
|
|
|
if (Layout->Resources.Num())
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2021-08-19 23:53:29 -04:00
|
|
|
int32 NumResources = Layout->Resources.Num();
|
2014-06-05 16:38:54 -04:00
|
|
|
NewUniformBuffer->ResourceTable.Empty(NumResources);
|
|
|
|
|
NewUniformBuffer->ResourceTable.AddZeroed(NumResources);
|
2020-09-24 00:43:27 -04:00
|
|
|
|
|
|
|
|
for (int32 Index = 0; Index < NumResources; ++Index)
|
2014-06-05 16:38:54 -04:00
|
|
|
{
|
2021-08-19 23:53:29 -04:00
|
|
|
NewUniformBuffer->ResourceTable[Index] = GetShaderParameterResourceRHI(Contents, Layout->Resources[Index].MemberOffset, Layout->Resources[Index].MemberType);
|
2014-06-05 16:38:54 -04:00
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
2018-02-22 11:25:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CopyDataToUniformBuffer(const bool bCanRunOnThisThread, FOpenGLUniformBuffer* NewUniformBuffer, const void* Contents, uint32 ContentSize)
|
|
|
|
|
{
|
|
|
|
|
FOpenGLEUniformBufferDataRef EmulatedUniformDataRef = NewUniformBuffer->EmulatedBufferData;
|
|
|
|
|
uint8* PersistentlyMappedBuffer = NewUniformBuffer->PersistentlyMappedBuffer;
|
|
|
|
|
// Copy the contents of the uniform buffer.
|
|
|
|
|
if (IsValidRef(EmulatedUniformDataRef))
|
|
|
|
|
{
|
|
|
|
|
FMemory::Memcpy(EmulatedUniformDataRef->Data.GetData(), Contents, ContentSize);
|
|
|
|
|
}
|
|
|
|
|
else if (PersistentlyMappedBuffer)
|
|
|
|
|
{
|
|
|
|
|
FMemory::Memcpy(PersistentlyMappedBuffer, Contents, ContentSize);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (bCanRunOnThisThread)
|
|
|
|
|
{
|
|
|
|
|
VERIFY_GL_SCOPE();
|
|
|
|
|
FOpenGL::BufferSubData(GL_UNIFORM_BUFFER, 0, ContentSize, Contents);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NewUniformBuffer->CopyFence.Reset();
|
|
|
|
|
// running on RHI thread take a copy of the incoming data.
|
|
|
|
|
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
|
|
|
|
|
void* ConstantBufferCopy = RHICmdList.Alloc(ContentSize, 16);
|
|
|
|
|
FMemory::Memcpy(ConstantBufferCopy, Contents, ContentSize);
|
|
|
|
|
|
2018-11-13 07:18:15 -05:00
|
|
|
ALLOC_COMMAND_CL(RHICmdList, FRHICommandGLCommand)(
|
2018-02-22 11:25:06 -05:00
|
|
|
[=]()
|
|
|
|
|
{
|
|
|
|
|
VERIFY_GL_SCOPE();
|
|
|
|
|
FOpenGL::BufferSubData(GL_UNIFORM_BUFFER, 0, ContentSize, ConstantBufferCopy);
|
|
|
|
|
NewUniformBuffer->CopyFence.WriteAssertFence();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
NewUniformBuffer->CopyFence.SetRHIThreadFence();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-19 23:53:29 -04:00
|
|
|
static FUniformBufferRHIRef CreateUniformBuffer(const void* Contents, const FRHIUniformBufferLayout* Layout, EUniformBufferUsage Usage, EUniformBufferValidation Validation)
|
2018-02-22 11:25:06 -05:00
|
|
|
{
|
|
|
|
|
// This should really be synchronized, if there's a chance it'll be used from more than one buffer. Luckily, uniform buffers
|
|
|
|
|
// are only used for drawing/shader usage, not for loading resources or framebuffer blitting, so no synchronization primitives for now.
|
|
|
|
|
|
|
|
|
|
// Explicitly check that the size is nonzero before allowing CreateBuffer to opaquely fail.
|
2021-08-19 23:53:29 -04:00
|
|
|
check(Layout->Resources.Num() > 0 || Layout->ConstantBufferSize > 0);
|
2018-02-22 11:25:06 -05:00
|
|
|
|
|
|
|
|
FOpenGLUniformBuffer* NewUniformBuffer = new FOpenGLUniformBuffer(Layout);
|
|
|
|
|
|
|
|
|
|
TFunction<void(void)> GLCreationFunc;
|
|
|
|
|
|
2021-08-19 23:53:29 -04:00
|
|
|
const uint32 BucketIndex = GetPoolBucketIndex(Layout->ConstantBufferSize);
|
2018-02-22 11:25:06 -05:00
|
|
|
const uint32 SizeOfBufferToAllocate = UniformBufferSizeBuckets[BucketIndex];
|
2021-08-19 23:53:29 -04:00
|
|
|
const uint32 AllocatedSize = (SizeOfBufferToAllocate > 0) ? SizeOfBufferToAllocate : Layout->ConstantBufferSize;;
|
2018-02-22 11:25:06 -05:00
|
|
|
|
|
|
|
|
// EmulatedUniformDataRef will not be initialized on RHI thread. safe to use on RT thread.
|
|
|
|
|
FOpenGLEUniformBufferDataRef EmulatedUniformDataRef;
|
|
|
|
|
|
|
|
|
|
// PersistentlyMappedBuffer initializes via IsSuballocatingUBOs path which will flush RHI commands. safe to use on RT thread.
|
|
|
|
|
uint8* PersistentlyMappedBuffer = NULL;
|
|
|
|
|
|
2022-04-12 08:41:13 -04:00
|
|
|
bool bUseEmulatedUBs = GUseEmulatedUniformBuffers && !Layout->bNoEmulatedUniformBuffer;
|
|
|
|
|
|
2018-02-22 11:25:06 -05:00
|
|
|
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
|
|
|
|
|
{
|
|
|
|
|
const bool bStreamDraw = (Usage == UniformBuffer_SingleDraw || Usage == UniformBuffer_SingleFrame);
|
|
|
|
|
|
|
|
|
|
// Nothing usable was found in the free pool, or we're not pooling, so create a new uniform buffer
|
2022-04-12 08:41:13 -04:00
|
|
|
if (bUseEmulatedUBs)
|
2018-02-22 11:25:06 -05:00
|
|
|
{
|
|
|
|
|
GLuint AllocatedResource = 0;
|
|
|
|
|
uint32 OffsetInBuffer = 0;
|
|
|
|
|
EmulatedUniformDataRef = UniformBufferDataFactory.Create(AllocatedSize, AllocatedResource);
|
|
|
|
|
NewUniformBuffer->SetGLUniformBufferParams(AllocatedResource, OffsetInBuffer, PersistentlyMappedBuffer, AllocatedSize, EmulatedUniformDataRef, bStreamDraw);
|
|
|
|
|
}
|
|
|
|
|
else if (IsSuballocatingUBOs())
|
|
|
|
|
{
|
|
|
|
|
GLCreationFunc = [NewUniformBuffer, AllocatedSize, &PersistentlyMappedBuffer, &EmulatedUniformDataRef, bStreamDraw]() {
|
|
|
|
|
GLuint AllocatedResource = 0;
|
|
|
|
|
uint32 OffsetInBuffer = 0;
|
|
|
|
|
|
|
|
|
|
SuballocateUBO(AllocatedSize, AllocatedResource, OffsetInBuffer, PersistentlyMappedBuffer);
|
|
|
|
|
NewUniformBuffer->SetGLUniformBufferParams(AllocatedResource, OffsetInBuffer, PersistentlyMappedBuffer, AllocatedSize, EmulatedUniformDataRef, bStreamDraw);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
check(PersistentlyMappedBuffer == nullptr);
|
|
|
|
|
GLCreationFunc = [NewUniformBuffer, AllocatedSize, PersistentlyMappedBuffer, EmulatedUniformDataRef, bStreamDraw]()
|
|
|
|
|
{
|
|
|
|
|
VERIFY_GL_SCOPE();
|
|
|
|
|
GLuint AllocatedResource = 0;
|
|
|
|
|
uint32 OffsetInBuffer = 0;
|
|
|
|
|
FOpenGL::GenBuffers(1, &AllocatedResource);
|
|
|
|
|
::CachedBindUniformBuffer(AllocatedResource);
|
|
|
|
|
glBufferData(GL_UNIFORM_BUFFER, AllocatedSize, NULL, bStreamDraw ? GL_STREAM_DRAW : GL_STATIC_DRAW);
|
|
|
|
|
NewUniformBuffer->SetGLUniformBufferParams(AllocatedResource, OffsetInBuffer, nullptr, AllocatedSize, EmulatedUniformDataRef, bStreamDraw);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool bCanCreateOnThisThread = RHICmdList.Bypass() || (!IsRunningRHIInSeparateThread() && IsInRenderingThread()) || IsInRHIThread();
|
2022-04-12 08:41:13 -04:00
|
|
|
if(!bUseEmulatedUBs)
|
2018-02-22 11:25:06 -05:00
|
|
|
{
|
|
|
|
|
if (bCanCreateOnThisThread)
|
|
|
|
|
{
|
|
|
|
|
GLCreationFunc();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NewUniformBuffer->AccessFence.Reset();
|
|
|
|
|
// Queue GL resource creation.
|
2018-11-13 07:18:15 -05:00
|
|
|
ALLOC_COMMAND_CL(RHICmdList, FRHICommandGLCommand)([=]() {GLCreationFunc(); NewUniformBuffer->AccessFence.WriteAssertFence(); });
|
2018-02-22 11:25:06 -05:00
|
|
|
NewUniformBuffer->AccessFence.SetRHIThreadFence();
|
|
|
|
|
|
|
|
|
|
// flush for the UBO case
|
|
|
|
|
// as this path interacts with UBOPool, this hasnt been addressed for the RHI thread case.
|
|
|
|
|
if (IsSuballocatingUBOs())
|
|
|
|
|
{
|
|
|
|
|
RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread);
|
|
|
|
|
RHITHREAD_GLTRACE_BLOCKING;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-16 00:46:28 -04:00
|
|
|
IncrementBufferMemory(GL_UNIFORM_BUFFER, AllocatedSize);
|
2018-02-22 11:25:06 -05:00
|
|
|
|
2022-04-12 08:41:13 -04:00
|
|
|
check(!bUseEmulatedUBs || (IsValidRef(EmulatedUniformDataRef) && (EmulatedUniformDataRef->Data.Num() * EmulatedUniformDataRef->Data.GetTypeSize() == AllocatedSize)));
|
2018-02-22 11:25:06 -05:00
|
|
|
|
2021-08-19 23:53:29 -04:00
|
|
|
CopyDataToUniformBuffer(bCanCreateOnThisThread, NewUniformBuffer,Contents, Layout->ConstantBufferSize);
|
2018-02-22 11:25:06 -05:00
|
|
|
|
|
|
|
|
// Initialize the resource table for this uniform buffer.
|
2018-12-11 22:25:04 -05:00
|
|
|
SetLayoutTable(NewUniformBuffer, Contents, Layout, Validation);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-06-05 16:38:54 -04:00
|
|
|
return NewUniformBuffer;
|
|
|
|
|
}
|
2018-02-22 11:25:06 -05:00
|
|
|
|
2021-08-19 23:53:29 -04:00
|
|
|
FUniformBufferRHIRef FOpenGLDynamicRHI::RHICreateUniformBuffer(const void* Contents, const FRHIUniformBufferLayout* Layout, EUniformBufferUsage Usage, EUniformBufferValidation Validation)
|
2018-02-22 11:25:06 -05:00
|
|
|
{
|
|
|
|
|
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
|
|
|
|
|
// This should really be synchronized, if there's a chance it'll be used from more than one buffer. Luckily, uniform buffers
|
|
|
|
|
// are only used for drawing/shader usage, not for loading resources or framebuffer blitting, so no synchronization primitives for now.
|
|
|
|
|
|
|
|
|
|
// Explicitly check that the size is nonzero before allowing CreateBuffer to opaquely fail.
|
2021-08-19 23:53:29 -04:00
|
|
|
check(Layout->Resources.Num() > 0 || Layout->ConstantBufferSize > 0);
|
2022-04-12 08:41:13 -04:00
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
if (Validation == EUniformBufferValidation::ValidateResources)
|
|
|
|
|
{
|
2021-08-19 23:53:29 -04:00
|
|
|
ValidateShaderParameterResourcesRHI(Contents, *Layout);
|
2020-09-24 00:43:27 -04:00
|
|
|
}
|
|
|
|
|
|
2022-04-12 08:41:13 -04:00
|
|
|
bool bUseEmulatedUBs = GUseEmulatedUniformBuffers && !Layout->bNoEmulatedUniformBuffer;
|
|
|
|
|
|
2018-02-22 11:25:06 -05:00
|
|
|
bool bStreamDraw = (Usage == UniformBuffer_SingleDraw || Usage == UniformBuffer_SingleFrame);
|
|
|
|
|
GLuint AllocatedResource = 0;
|
|
|
|
|
uint32 OffsetInBuffer = 0;
|
|
|
|
|
uint8* PersistentlyMappedBuffer = NULL;
|
|
|
|
|
uint32 AllocatedSize = 0;
|
|
|
|
|
FOpenGLEUniformBufferDataRef EmulatedUniformDataRef;
|
|
|
|
|
|
|
|
|
|
const bool bCanCreateOnThisThread = RHICmdList.Bypass() || (!IsRunningRHIInSeparateThread() && IsInRenderingThread()) || IsInRHIThread();
|
|
|
|
|
|
|
|
|
|
// If the uniform buffer contains constants, allocate a uniform buffer resource from GL.
|
2021-08-19 23:53:29 -04:00
|
|
|
if (Layout->ConstantBufferSize > 0)
|
2018-02-22 11:25:06 -05:00
|
|
|
{
|
|
|
|
|
uint32 SizeOfBufferToAllocate = 0;
|
|
|
|
|
if (IsPoolingEnabled())
|
|
|
|
|
{
|
|
|
|
|
// Find the appropriate bucket based on size
|
2021-08-19 23:53:29 -04:00
|
|
|
const uint32 BucketIndex = GetPoolBucketIndex(Layout->ConstantBufferSize);
|
2018-02-22 11:25:06 -05:00
|
|
|
int StreamedIndex = bStreamDraw ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
FPooledGLUniformBuffer FreeBufferEntry;
|
|
|
|
|
FreeBufferEntry.Buffer = 0;
|
|
|
|
|
FreeBufferEntry.CreatedSize = 0;
|
|
|
|
|
bool bHasEntry = false;
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock(&GGLUniformBufferPoolCS);
|
2022-04-12 08:41:13 -04:00
|
|
|
|
|
|
|
|
TArray<FPooledGLUniformBuffer>* PoolBucket;
|
|
|
|
|
|
|
|
|
|
if (bUseEmulatedUBs)
|
|
|
|
|
{
|
|
|
|
|
PoolBucket = &GLEmulatedUniformBufferPool[BucketIndex][StreamedIndex];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PoolBucket = &GLUniformBufferPool[BucketIndex][StreamedIndex];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PoolBucket->Num() > 0)
|
2018-02-22 11:25:06 -05:00
|
|
|
{
|
|
|
|
|
// Reuse the last entry in this size bucket
|
2022-04-12 08:41:13 -04:00
|
|
|
FreeBufferEntry = PoolBucket->Pop();
|
2018-02-22 11:25:06 -05:00
|
|
|
bHasEntry = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (bHasEntry)
|
|
|
|
|
{
|
|
|
|
|
DEC_DWORD_STAT(STAT_OpenGLNumFreeUniformBuffers);
|
|
|
|
|
DEC_MEMORY_STAT_BY(STAT_OpenGLFreeUniformBufferMemory, FreeBufferEntry.CreatedSize);
|
|
|
|
|
|
|
|
|
|
AllocatedResource = FreeBufferEntry.Buffer;
|
|
|
|
|
AllocatedSize = FreeBufferEntry.CreatedSize;
|
|
|
|
|
|
2022-04-12 08:41:13 -04:00
|
|
|
if (bUseEmulatedUBs)
|
2018-02-22 11:25:06 -05:00
|
|
|
{
|
|
|
|
|
EmulatedUniformDataRef = UniformBufferDataFactory.Get(AllocatedResource);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
auto CacheGLUniformBuffer = [AllocatedResource]()
|
|
|
|
|
{
|
|
|
|
|
VERIFY_GL_SCOPE();
|
|
|
|
|
::CachedBindUniformBuffer(AllocatedResource);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (bCanCreateOnThisThread)
|
|
|
|
|
{
|
|
|
|
|
CacheGLUniformBuffer();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-11-24 18:42:39 -04:00
|
|
|
ALLOC_COMMAND_CL(RHICmdList, FRHICommandGLCommand)(MoveTemp(CacheGLUniformBuffer));
|
2018-02-22 11:25:06 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SizeOfBufferToAllocate = UniformBufferSizeBuckets[BucketIndex];
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-12 08:41:13 -04:00
|
|
|
}
|
2018-02-22 11:25:06 -05:00
|
|
|
|
|
|
|
|
if (AllocatedSize == 0)
|
|
|
|
|
{
|
2018-12-11 22:25:04 -05:00
|
|
|
return CreateUniformBuffer(Contents, Layout, Usage, Validation);
|
2018-02-22 11:25:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FOpenGLUniformBuffer* NewUniformBuffer = new FOpenGLUniformBuffer(Layout);
|
|
|
|
|
NewUniformBuffer->SetGLUniformBufferParams(AllocatedResource, OffsetInBuffer, PersistentlyMappedBuffer, AllocatedSize, EmulatedUniformDataRef, bStreamDraw);
|
|
|
|
|
|
2022-04-12 08:41:13 -04:00
|
|
|
check(!bUseEmulatedUBs || (IsValidRef(EmulatedUniformDataRef) && (EmulatedUniformDataRef->Data.Num() * EmulatedUniformDataRef->Data.GetTypeSize() == AllocatedSize)));
|
2021-08-19 23:53:29 -04:00
|
|
|
CopyDataToUniformBuffer(bCanCreateOnThisThread, NewUniformBuffer, Contents, Layout->ConstantBufferSize);
|
2018-02-22 11:25:06 -05:00
|
|
|
|
|
|
|
|
// Initialize the resource table for this uniform buffer.
|
2018-12-11 22:25:04 -05:00
|
|
|
SetLayoutTable(NewUniformBuffer, Contents, Layout, Validation);
|
2018-02-22 11:25:06 -05:00
|
|
|
|
|
|
|
|
return NewUniformBuffer;
|
2018-12-11 22:25:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UpdateUniformBufferContents(FOpenGLUniformBuffer* UniformBuffer, const void* Contents, uint32 ConstantBufferSize)
|
|
|
|
|
{
|
|
|
|
|
if (ConstantBufferSize > 0)
|
|
|
|
|
{
|
|
|
|
|
FOpenGLEUniformBufferDataRef EmulatedUniformDataRef = UniformBuffer->EmulatedBufferData;
|
|
|
|
|
uint8* PersistentlyMappedBuffer = UniformBuffer->PersistentlyMappedBuffer;
|
|
|
|
|
|
|
|
|
|
if (IsValidRef(EmulatedUniformDataRef))
|
|
|
|
|
{
|
|
|
|
|
FMemory::Memcpy(EmulatedUniformDataRef->Data.GetData(), Contents, ConstantBufferSize);
|
|
|
|
|
}
|
|
|
|
|
else if (PersistentlyMappedBuffer)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogRHI, Fatal, TEXT("RHIUpdateUniformBuffer doesn't support PersistentlyMappedBuffer yet!"));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
::CachedBindUniformBuffer(UniformBuffer->Resource);
|
|
|
|
|
FOpenGL::BufferSubData(GL_UNIFORM_BUFFER, 0, ConstantBufferSize, Contents);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-17 13:39:07 -04:00
|
|
|
void FOpenGLDynamicRHI::RHIUpdateUniformBuffer(FRHIUniformBuffer* UniformBufferRHI, const void* Contents)
|
2018-12-11 22:25:04 -05:00
|
|
|
{
|
|
|
|
|
FOpenGLUniformBuffer* UniformBuffer = ResourceCast(UniformBufferRHI);
|
|
|
|
|
|
|
|
|
|
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
|
|
|
|
|
const FRHIUniformBufferLayout& Layout = UniformBufferRHI->GetLayout();
|
2020-09-24 00:43:27 -04:00
|
|
|
ValidateShaderParameterResourcesRHI(Contents, Layout);
|
2018-12-11 22:25:04 -05:00
|
|
|
|
|
|
|
|
const int32 ConstantBufferSize = Layout.ConstantBufferSize;
|
|
|
|
|
const int32 NumResources = Layout.Resources.Num();
|
|
|
|
|
|
|
|
|
|
check(UniformBuffer->ResourceTable.Num() == NumResources);
|
|
|
|
|
|
|
|
|
|
uint32 NextUniqueID = UniqueUniformBufferID();
|
|
|
|
|
|
|
|
|
|
if (RHICmdList.Bypass())
|
|
|
|
|
{
|
|
|
|
|
UpdateUniformBufferContents(UniformBuffer, Contents, ConstantBufferSize);
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
for (int32 Index = 0; Index < NumResources; ++Index)
|
2018-12-11 22:25:04 -05:00
|
|
|
{
|
2020-09-24 00:43:27 -04:00
|
|
|
UniformBuffer->ResourceTable[Index] = GetShaderParameterResourceRHI(Contents, Layout.Resources[Index].MemberOffset, Layout.Resources[Index].MemberType);
|
2018-12-11 22:25:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UniformBuffer->UniqueID = NextUniqueID;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FRHIResource** CmdListResources = nullptr;
|
|
|
|
|
void* CmdListConstantBufferData = nullptr;
|
|
|
|
|
|
|
|
|
|
if (NumResources > 0)
|
|
|
|
|
{
|
|
|
|
|
CmdListResources = (FRHIResource**)RHICmdList.Alloc(sizeof(FRHIResource*) * NumResources, alignof(FRHIResource*));
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
for (int32 Index = 0; Index < NumResources; ++Index)
|
2018-12-11 22:25:04 -05:00
|
|
|
{
|
2020-09-24 00:43:27 -04:00
|
|
|
const auto Parameter = Layout.Resources[Index];
|
|
|
|
|
CmdListResources[Index] = GetShaderParameterResourceRHI(Contents, Parameter.MemberOffset, Parameter.MemberType);
|
2018-12-11 22:25:04 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ConstantBufferSize > 0)
|
|
|
|
|
{
|
|
|
|
|
CmdListConstantBufferData = (void*)RHICmdList.Alloc(ConstantBufferSize, 16);
|
|
|
|
|
FMemory::Memcpy(CmdListConstantBufferData, Contents, ConstantBufferSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RHICmdList.EnqueueLambda([UniformBuffer, CmdListResources, NumResources, CmdListConstantBufferData, ConstantBufferSize, NextUniqueID](FRHICommandList&)
|
|
|
|
|
{
|
|
|
|
|
UpdateUniformBufferContents(UniformBuffer, CmdListConstantBufferData, ConstantBufferSize);
|
|
|
|
|
|
|
|
|
|
// Update resource table.
|
|
|
|
|
for (int32 ResourceIndex = 0; ResourceIndex < NumResources; ++ResourceIndex)
|
|
|
|
|
{
|
|
|
|
|
UniformBuffer->ResourceTable[ResourceIndex] = CmdListResources[ResourceIndex];
|
|
|
|
|
}
|
|
|
|
|
UniformBuffer->UniqueID = NextUniqueID;
|
|
|
|
|
});
|
|
|
|
|
RHICmdList.RHIThreadFence(true);
|
|
|
|
|
}
|
2019-07-24 09:57:26 -04:00
|
|
|
}
|