首发于Amazing UE4

UE4.24源码分析 - ENQUEUE_RENDER_COMMAND

  • 在UE 4.22版本之前,Game线程中往渲染线程中 (Rendering Thread)压入渲染命令任务,以前使用的是ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER。

关于该宏的解释,请参考:

UE4 ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER 宏blog.csdn.net

其中代码使用例子为:

ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
        BeginDrawingCommand,
        FViewport*,Viewport,this,
    {
        Viewport->BeginRenderFrame(RHICmdList);
    });
  • 在UE 4.22及其之后,该命令修改为ENQUEUE_RENDER_COMMAND,本质上就是对以前的宏进行重新整理,使用起来更加方便点,使用Lamda来传递Code。

上面的例子代码修正为:

ENQUEUE_RENDER_COMMAND(BeginDrawingCommand)([Viewport](FRHICommandListImmediate& RHICmdList){
	Viewport->BeginRenderFrame(RHICmdList)
});

如果在里面需要修改Viewport值,就需要在Lamda表达式后面加上mutable关键词,表示该值在lamda中可修改。

ENQUEUE_RENDER_COMMAND(BeginDrawingCommand)([Viewport](FRHICommandListImmediate& RHICmdList) mutable {
	Viewport->BeginRenderFrame(RHICmdList);
                Viewport = nullptr;
});

其中,ENQUEUE_RENDER_COMMAND定义如下

#define ENQUEUE_RENDER_COMMAND(Type) \
	struct Type##Name \
	{  \
		static const char* CStr() { return #Type; } \
		static const TCHAR* TStr() { return TEXT(#Type); } \
	}; \
	EnqueueUniqueRenderCommand<Type##Name>

将ENQUEUE_RENDER_COMMAND(BeginDrawingCommand)展开就为

EnqueueUniqueRenderCommand<BeginDrawingCommandName>

其中,EnqueueUniqueRenderCommand定义为

template<typename TSTR, typename LAMBDA>
FORCEINLINE_DEBUGGABLE void EnqueueUniqueRenderCommand(LAMBDA&& Lambda)
{
	typedef TEnqueueUniqueRenderCommandType<TSTR, LAMBDA> EURCType;

#if 0 // UE_SERVER && UE_BUILD_DEBUG
	UE_LOG(LogRHI, Warning, TEXT("Render command '%s' is being executed on a dedicated server."), TSTR::TStr())
#endif      
               // 如果是在Rendering线程中
	if (IsInRenderingThread())
	{
		FRHICommandListImmediate& RHICmdList = GetImmediateCommandList_ForRenderCommand();
		Lambda(RHICmdList);   //执行Lamda表达式
	}
	else
	{
		if (ShouldExecuteOnRenderThread())
		{               
                                                // 异步执行
			CheckNotBlockedOnRenderThread();
			TGraphTask<EURCType>::CreateTask().ConstructAndDispatchWhenReady(Forward<LAMBDA>(Lambda));
		}
		else
		{
			EURCType TempCommand(Forward<LAMBDA>(Lambda));
			FScopeCycleCounter EURCMacro_Scope(TempCommand.GetStatId());
			TempCommand.DoTask(ENamedThreads::GameThread, FGraphEventRef());
		}
	}
}

按上面,进行展开,就为

EnqueueUniqueRenderCommand<BeginDrawingCommandName>(LAMBDA&& Lambda)

其中LAMBDA为

[Viewport](FRHICommandListImmediate& RHICmdList) mutable {
	Viewport->BeginRenderFrame(RHICmdList);


  • 使用ENQUEUE_RENDER_COMMAND,需要注意几点是:
  1. ENQUEUE_RENDER_COMMAND()执行在Rendering线程,调用ENQUEUE_RENDER_COMMAND()一般都在主线程,也就是Game线程。注意资源同步的事情,建议在Game线程中谨慎删除Rendering线程中使用到的资源,否则会出现野指针
  2. mutable关键字,表示可以修改按值传入的变量的副本(不是值本身),类似于不带const关键字的形参。使用mutable关键字后对按值传入的变量进行的修改,不会将改变传递到Lambda表达式之外。

编辑于 06-22

文章被以下专栏收录