2021-12-02 18:23:59 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "GPUMessaging.h"
# include "RenderResource.h"
# include "RenderGraphUtils.h"
# include "RHIGPUReadback.h"
# include "GlobalShader.h"
DEFINE_LOG_CATEGORY_STATIC ( LogGPUMessaging , Log , All ) ;
RDG_REGISTER_BLACKBOARD_STRUCT ( GPUMessage : : FParameters )
class FClearMessageBufferCS : public FGlobalShader
{
public :
DECLARE_GLOBAL_SHADER ( FClearMessageBufferCS ) ;
SHADER_USE_PARAMETER_STRUCT ( FClearMessageBufferCS , FGlobalShader )
BEGIN_SHADER_PARAMETER_STRUCT ( FParameters , )
2021-12-03 16:08:27 -05:00
SHADER_PARAMETER_RDG_BUFFER_UAV ( RWStructuredBuffer < uint > , GPUMessageDataBuffer )
2021-12-02 18:23:59 -05:00
END_SHADER_PARAMETER_STRUCT ( )
} ;
IMPLEMENT_GLOBAL_SHADER ( FClearMessageBufferCS , " /Engine/Private/GPUMessaging.usf " , " ClearMessageBuffer " , SF_Compute ) ;
namespace GPUMessage
{
int32 GLogAllMessages = 0 ;
static FAutoConsoleVariableRef CVarLogAllMessages (
TEXT ( " r.GPUMessage.LogAllMessages " ) ,
GLogAllMessages ,
TEXT ( " Log all messages to the console. \n " )
TEXT ( " 0: Disabled \n " )
TEXT ( " 1: Enabled \n " ) ,
ECVF_RenderThreadSafe
) ;
int32 GMaxBufferSize = 64 ;
static FAutoConsoleVariableRef CVarMaxBufferSize (
TEXT ( " r.GPUMessage.MaxBufferSize " ) ,
GMaxBufferSize ,
TEXT ( " Specifies the maximum size of the GPU message buffer, in KiB. \n " )
TEXT ( " default: 64 \n " ) ,
ECVF_RenderThreadSafe | ECVF_ReadOnly
) ;
class FSystem : public FRenderResource
{
public :
2021-12-03 16:08:27 -05:00
// Need to guard access because it may happen during FScene initialization (Game Thread) while another scene is being rendered.
FCriticalSection AccessCS ;
2021-12-02 18:23:59 -05:00
FSocket RegisterHandler ( const TSharedPtr < FHandler > & Handler )
{
2021-12-03 16:08:27 -05:00
FScopeLock Lock ( & AccessCS ) ;
2021-12-02 18:23:59 -05:00
const FMessageId MessageId ( NextMessageId + + ) ;
check ( ! MessageHandlers . Contains ( MessageId ) ) ;
MessageHandlers . Add ( MessageId , Handler ) ;
return FSocket ( MessageId ) ;
}
void RemoveHandler ( FMessageId MessageId )
{
2021-12-03 16:08:27 -05:00
FScopeLock Lock ( & AccessCS ) ;
2021-12-02 18:23:59 -05:00
check ( MessageId . IsValid ( ) ) ;
check ( MessageHandlers . Contains ( MessageId ) ) ;
MessageHandlers . Remove ( MessageId ) ;
}
void BeginMessageScope ( FRDGBuilder & GraphBuilder )
{
check ( MessageBuffer = = nullptr ) ;
2021-12-03 16:08:27 -05:00
FScopeLock Lock ( & AccessCS ) ;
2021-12-02 18:23:59 -05:00
PollMessages ( ) ;
if ( MessageHandlers . IsEmpty ( ) )
{
return ;
}
2023-04-04 12:41:14 -04:00
const int32 MaxBufferSize = GetMaxBufferSize ( ) ;
2021-12-02 18:23:59 -05:00
FRDGBufferDesc Desc = FRDGBufferDesc : : CreateStructuredDesc ( sizeof ( uint32 ) , MaxBufferSize ) ;
2022-05-06 15:44:23 -04:00
Desc . Usage | = EBufferUsageFlags : : SourceCopy ;
2021-12-02 18:23:59 -05:00
MessageBuffer = GraphBuilder . CreateBuffer ( Desc , TEXT ( " GPUMessageManager.MessageBuffer " ) ) ;
FParameters & Parameters = GraphBuilder . Blackboard . Create < FParameters > ( ) ;
Parameters . GPUMessageDataBuffer = GraphBuilder . CreateUAV ( MessageBuffer ) ;
Parameters . GPUMessageDataBufferSize = MaxBufferSize ;
TShaderMapRef < FClearMessageBufferCS > ComputeShader ( GetGlobalShaderMap ( GMaxRHIFeatureLevel ) ) ;
FClearMessageBufferCS : : FParameters * PassParameters = GraphBuilder . AllocParameters < FClearMessageBufferCS : : FParameters > ( ) ;
PassParameters - > GPUMessageDataBuffer = Parameters . GPUMessageDataBuffer ;
FComputeShaderUtils : : AddPass ( GraphBuilder , RDG_EVENT_NAME ( " ClearGPUMessageBuffer " ) , ComputeShader , PassParameters , FIntVector ( 1 , 1 , 1 ) ) ;
}
void EndMessageScope ( FRDGBuilder & GraphBuilder )
{
2021-12-03 16:08:27 -05:00
FScopeLock Lock ( & AccessCS ) ;
2021-12-02 18:23:59 -05:00
if ( MessageHandlers . IsEmpty ( ) )
{
check ( MessageBuffer = = nullptr ) ;
return ;
}
check ( MessageBuffer ! = nullptr ) ;
FRHIGPUBufferReadback * GPUBufferReadback = nullptr ;
if ( ! MessageReadbackBuffersReady . IsEmpty ( ) )
{
GPUBufferReadback = MessageReadbackBuffersReady . Pop ( ) ;
}
else
{
GPUBufferReadback = new FRHIGPUBufferReadback ( TEXT ( " GPUMessageManager.Readback " ) ) ;
}
AddEnqueueCopyPass ( GraphBuilder , GPUBufferReadback , MessageBuffer , 0u ) ;
MessageReadbackBuffersInUse . Add ( GPUBufferReadback ) ;
MessageBuffer = nullptr ;
}
private :
void PollMessages ( )
{
for ( int32 Index = 0 ; Index < MessageReadbackBuffersInUse . Num ( ) ; )
{
FRHIGPUBufferReadback * GPUBufferReadback = MessageReadbackBuffersInUse [ Index ] ;
if ( GPUBufferReadback - > IsReady ( ) )
{
2023-04-04 12:41:14 -04:00
const int32 MaxBufferSize = GetMaxBufferSize ( ) ;
2021-12-02 18:23:59 -05:00
const uint32 * BufferPtr = ( const uint32 * ) GPUBufferReadback - > Lock ( MaxBufferSize * sizeof ( uint32 ) ) ;
2023-04-04 12:41:14 -04:00
int32 ValidRangeEnd = ( int32 ) BufferPtr [ 0 ] + 1 ;
2021-12-02 18:23:59 -05:00
2023-04-04 12:41:14 -04:00
const int32 OriginalValidRangeEnd = ValidRangeEnd ;
2021-12-02 18:23:59 -05:00
ValidRangeEnd = FMath : : Min ( ValidRangeEnd , MaxBufferSize ) ;
2023-04-04 12:41:14 -04:00
ensureMsgf ( ValidRangeEnd = = OriginalValidRangeEnd , TEXT ( " GPU messages size %d exceeded maximum size %d. Results have been truncated. " ) , OriginalValidRangeEnd , MaxBufferSize ) ;
2021-12-02 18:23:59 -05:00
if ( GLogAllMessages ! = 0 )
{
2023-04-04 12:41:14 -04:00
FString LogMessage ;
LogMessage . Appendf ( TEXT ( " \n Message Buffer[0] = %u; \n " ) , BufferPtr [ 0 ] ) ;
2021-12-02 18:23:59 -05:00
if ( ValidRangeEnd ! = OriginalValidRangeEnd )
{
2023-04-04 12:41:14 -04:00
LogMessage . Appendf ( TEXT ( " GPU messages size %d exceeded maximum size %d. Results have been truncated. \n " ) , OriginalValidRangeEnd , MaxBufferSize ) ;
2021-12-02 18:23:59 -05:00
}
2023-04-04 12:41:14 -04:00
// Subtract 1 from ValidRangeEnd because we expect to have at least 2 ints available (message ID and payload size).
for ( int32 ReadOffset = 1 ; ReadOffset < ValidRangeEnd - 1 ; )
2021-12-02 18:23:59 -05:00
{
const FMessageId MessageId ( BufferPtr [ ReadOffset ] ) ;
2023-04-04 12:41:14 -04:00
int32 PayloadNumUints = ( int32 ) BufferPtr [ ReadOffset + 1 ] ;
2021-12-02 18:23:59 -05:00
2023-04-04 12:41:14 -04:00
const int32 OriginalPayloadNumUints = PayloadNumUints ;
PayloadNumUints = FMath : : Min ( PayloadNumUints , ValidRangeEnd - ReadOffset - 2 ) ;
LogMessage . Appendf ( TEXT ( " Message[Offset: %d, ID: %d, PayloadSize: %d, " ) , ReadOffset , MessageId . GetIndexUnchecked ( ) , OriginalPayloadNumUints ) ;
if ( PayloadNumUints ! = OriginalPayloadNumUints )
{
LogMessage . Appendf ( TEXT ( " !payload size %d exceeds buffer size (ReadOffset=%d, ValidRangeEnd=%d), truncated to %d!, " ) , OriginalPayloadNumUints , ReadOffset , ValidRangeEnd , PayloadNumUints ) ;
}
2021-12-02 18:23:59 -05:00
if ( auto Handler = MessageHandlers . Find ( MessageId ) )
{
LogMessage . Appendf ( TEXT ( " Name: %s] " ) , ( * Handler ) - > GetName ( ) ) ;
}
else
{
LogMessage . Append ( TEXT ( " <Unknown>] " ) ) ;
}
if ( PayloadNumUints > 0 )
{
LogMessage . Append ( TEXT ( " Payload: { " ) ) ;
2023-04-04 12:41:14 -04:00
for ( int32 PayloadIndex = 0 ; PayloadIndex < PayloadNumUints ; + + PayloadIndex )
2021-12-02 18:23:59 -05:00
{
2023-04-04 12:41:14 -04:00
LogMessage . Appendf ( TEXT ( " %u%s " ) , BufferPtr [ ReadOffset + 2 + PayloadIndex ] , PayloadIndex + 1 = = PayloadNumUints ? TEXT ( " " ) : TEXT ( " , " ) ) ;
2021-12-02 18:23:59 -05:00
}
LogMessage . Append ( TEXT ( " } " ) ) ;
}
LogMessage . Append ( TEXT ( " \n " ) ) ;
// Step past the message to the next
2023-04-04 12:41:14 -04:00
ReadOffset + = PayloadNumUints + 2 ;
2021-12-02 18:23:59 -05:00
}
UE_LOG ( LogGPUMessaging , Log , TEXT ( " %s " ) , * LogMessage ) ;
}
2023-04-04 12:41:14 -04:00
int32 ReadOffset ;
for ( ReadOffset = 1 ; ReadOffset < ValidRangeEnd - 1 ; )
2021-12-02 18:23:59 -05:00
{
const FMessageId MessageId ( BufferPtr [ ReadOffset ] ) ;
2023-04-04 12:41:14 -04:00
int32 PayloadNumUints = ( int32 ) BufferPtr [ ReadOffset + 1 ] ;
const int32 OriginalPayloadNumUints = PayloadNumUints ;
PayloadNumUints = FMath : : Min ( PayloadNumUints , ValidRangeEnd - ReadOffset - 2 ) ;
ensureMsgf ( PayloadNumUints = = OriginalPayloadNumUints , TEXT ( " GPU message payload size %d exceeds buffer size (ReadOffset=%d, ValidRangeEnd=%d), truncated to %d. " ) , OriginalPayloadNumUints , ReadOffset , ValidRangeEnd , PayloadNumUints ) ;
2021-12-02 18:23:59 -05:00
if ( auto Handler = MessageHandlers . Find ( MessageId ) )
{
2023-04-04 12:41:14 -04:00
( * Handler ) - > Execute ( FReader ( MessageId , PayloadNumUints , & BufferPtr [ ReadOffset + 2 ] ) ) ;
2021-12-02 18:23:59 -05:00
}
// Step past the message to the next
2023-04-04 12:41:14 -04:00
ReadOffset + = PayloadNumUints + 2 ;
2021-12-02 18:23:59 -05:00
}
2023-04-04 12:41:14 -04:00
ensureMsgf ( ReadOffset = = ValidRangeEnd , TEXT ( " Garbage at the end of the GPU message buffer, ReadOffset=%d, ValidRangeEnd=%d " ) , ReadOffset , ValidRangeEnd ) ;
2021-12-02 18:23:59 -05:00
GPUBufferReadback - > Unlock ( ) ;
MessageReadbackBuffersInUse . RemoveAt ( Index ) ;
MessageReadbackBuffersReady . Push ( GPUBufferReadback ) ;
}
else
{
+ + Index ;
}
}
}
2023-06-09 12:58:33 -04:00
void ReleaseRHI ( ) override
2021-12-02 18:23:59 -05:00
{
2021-12-03 16:08:27 -05:00
FScopeLock Lock ( & AccessCS ) ;
2021-12-02 18:23:59 -05:00
check ( MessageBuffer = = nullptr ) ;
for ( FRHIGPUBufferReadback * ReadbackBuffer : MessageReadbackBuffersInUse )
{
delete ReadbackBuffer ;
}
MessageReadbackBuffersInUse . Empty ( ) ;
for ( FRHIGPUBufferReadback * ReadbackBuffer : MessageReadbackBuffersReady )
{
delete ReadbackBuffer ;
}
MessageReadbackBuffersReady . Empty ( ) ;
}
2023-04-04 12:41:14 -04:00
int32 GetMaxBufferSize ( ) const { return GMaxBufferSize * 1024 ; }
2021-12-02 18:23:59 -05:00
TMap < FMessageId , TSharedPtr < FHandler > > MessageHandlers ;
TArray < FRHIGPUBufferReadback * , TInlineAllocator < 8 > > MessageReadbackBuffersInUse ;
TArray < FRHIGPUBufferReadback * , TInlineAllocator < 8 > > MessageReadbackBuffersReady ;
FRDGBuffer * MessageBuffer { } ;
uint32 NextMessageId = 0 ;
} ;
static TGlobalResource < FSystem > GSystem ;
void FSocket : : Reset ( )
{
if ( MessageId . IsValid ( ) )
{
GSystem . RemoveHandler ( MessageId ) ;
}
MessageId = FMessageId : : Null ;
}
bool FScope : : bRecursionCheck = false ;
FScope : : FScope ( FRDGBuilder & InGraphBuilder )
: GraphBuilder ( InGraphBuilder )
{
checkf ( ! bRecursionCheck , TEXT ( " GPUMessage::FScope (GPU_MESSAGE_SCOPE) has already been pushed. Only one scope can be active at a time. " ) ) ;
bRecursionCheck = true ;
GSystem . BeginMessageScope ( InGraphBuilder ) ;
}
FScope : : ~ FScope ( )
{
GSystem . EndMessageScope ( GraphBuilder ) ;
bRecursionCheck = false ;
}
FSocket RegisterHandler ( const TSharedPtr < FHandler > & Handler )
{
return GSystem . RegisterHandler ( Handler ) ;
}
FParameters GetShaderParameters ( FRDGBuilder & GraphBuilder )
{
const FParameters * Parameters = GraphBuilder . Blackboard . Get < FParameters > ( ) ;
checkf ( Parameters , TEXT ( " Shader parameters not initialized. GPUMessage::GetShaderParameters may only be called within a GPU_MESSAGE_SCOPE. " ) ) ;
return * Parameters ;
}
} // GPUMessage