Problem
I use ReloadPackages
to reload texture A from new pak files at runtime. If a material is using texture A, then an occasional problem may happen:
FRHIResource::Release() RHIResources.h:54
TArray::~TArray<…>() Array.h:621
FD3D11UniformBuffer::~FD3D11UniformBuffer() D3D11UniformBuffer.cpp:381
<lambda_84e30748...>::operator()(TArray<…> &) RHI.cpp:599
FRHIResource::FlushPendingDeletes(bool) RHI.cpp:629
FlushPendingDeleteRHIResources_RenderThread() RenderingThread.cpp:1303
<lambda_83aa676a...>::operator()(FRHICommandListImmediate &) SceneRendering.cpp:4008
TEnqueueUniqueRenderCommandType<`FRendererModule::BeginRenderingViewFamily'::`43'::FDrawSceneCommandName,<lambda_83aa676af25f62e876425d334fdfc6e4> >::DoTask(Type,const TRefCountPtr<FGraphEvent> &) RenderingThread.h:183
TGraphTask<TEnqueueUniqueRenderCommandType<`FRendererModule::BeginRenderingViewFamily'::`43'::FDrawSceneCommandName,<lambda_83aa676af25f62e876425d334fdfc6e4> > >::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32> > &,Type) TaskGraphInterfaces.h:886
FNamedTaskThread::ProcessTasksNamedThread(int, bool) TaskGraph.cpp:710
FNamedTaskThread::ProcessTasksUntilQuit(int) TaskGraph.cpp:601
FTaskGraphImplementation::ProcessThreadUntilRequestReturn(Type) TaskGraph.cpp:1480
RenderingThreadMain(FEvent *) RenderingThread.cpp:372
FRenderingThread::Run() RenderingThread.cpp:526
FRunnableThreadWin::Run() WindowsRunnableThread.cpp:84
FRunnableThreadWin::GuardedRun() WindowsRunnableThread.cpp:27
FRunnableThreadWin::_ThreadProc(void *) WindowsRunnableThread.h:37
The reason for this error is that in FD3D11UniformBuffer
, there is a table that stores resources.
/** Resource table containing RHI references. */
TArray<TRefCountPtr<FRHIResource> > ResourceTable;
When FD3D11UniformBuffer
is destroyed, this TArray is also destroyed
When TArray is destroyed, the destructor is called for each member first
Here, a TRefCountPtr<FRHIResource>
is already a wild pointer
This array view:
Then an error is reported at 0x417014e3e0000000
This is the call to the destructor of FD3D11UniformBuffer
in the delete queue, so it still depends on who initiated the destructor of FD3D11UniformBuffer
Because the release method is to put the RHI resource into the delete queue, and class FD3D11UniformBuffer : public FRHIUniformBuffer
, class FRHIUniformBuffer : public FRHIResource
, so someone should have called it Release()
of FRHIUniformBuffer
My try
I added Log to FRHIUniformBuffer::Release
and disabled optimization
PRAGMA_DISABLE_OPTIMIZATION
FORCEINLINE_DEBUGGABLE uint32 Release() const
{
const FRHIUniformBufferLayout* LocalLayout = Layout;
#if VALIDATE_UNIFORM_BUFFER_LIFETIME
int32 LocalNumMeshCommandReferencesForDebugging = NumMeshCommandReferencesForDebugging;
#endif
UE_LOG(LogTemp, Warning, TEXT("Someone call FRHIUniformBuffer::Release()!"));
After recompiling, I found that FRHIUniformBuffer::Release()
is called very frequently
This is from the command FRHICommandSetShaderUniformBuffer
<lambda_fd12fd3e...>::operator()<…>(const FLogCategoryLogTemp &, const wchar_t (&)[43]) RHIResources.h:777
FRHIUniformBuffer::Release() RHIResources.h:777
TRefCountPtr::operator=(FRHIUniformBuffer *) RefCounting.h:205
FD3D11DynamicRHI::RHISetShaderUniformBuffer(FRHIComputeShader *, unsigned int, FRHIUniformBuffer *) D3D11Commands.cpp:701
FRHICommandSetShaderUniformBuffer::Execute(FRHICommandListBase &) RHICommandListCommandExecutes.inl:142
FRHICommand::ExecuteAndDestruct(FRHICommandListBase &, FRHICommandListDebugContext &) RHICommandList.h:765
FRHICommandListExecutor::ExecuteInner_DoExecute(FRHICommandListBase &) RHICommandList.cpp:372
FRHICommandListExecutor::ExecuteInner(FRHICommandListBase &) RHICommandList.cpp:657
FRHICommandListExecutor::ExecuteList(FRHICommandListImmediate &) RHICommandList.cpp:708
FRHICommandListImmediate::ImmediateFlush(Type) RHICommandList.inl:98
FRHICommandList::EndScene() RHICommandList.cpp:1574
TRDGLambdaPass::ExecuteImpl(FRHIComputeCommandList &) RenderGraphPass.h:424
FRDGPass::Execute(FRHIComputeCommandList &) RenderGraphPass.cpp:305
FRDGBuilder::ExecutePass(FRDGPass *) RenderGraphBuilder.cpp:1638
FRDGBuilder::Execute() RenderGraphBuilder.cpp:1308
FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate &) DeferredShadingRenderer.cpp:2709
RenderViewFamily_RenderThread(FRHICommandListImmediate &, FSceneRenderer *) SceneRendering.cpp:3739
<lambda_83aa676a...>::operator()(FRHICommandListImmediate &) SceneRendering.cpp:4007
TEnqueueUniqueRenderCommandType<`FRendererModule::BeginRenderingViewFamily'::`43'::FDrawSceneCommandName,<lambda_83aa676af25f62e876425d334fdfc6e4> >::DoTask(Type,const TRefCountPtr<FGraphEvent> &) RenderingThread.h:183
TGraphTask<TEnqueueUniqueRenderCommandType<`FRendererModule::BeginRenderingViewFamily'::`43'::FDrawSceneCommandName,<lambda_83aa676af25f62e876425d334fdfc6e4> > >::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32> > &,Type) TaskGraphInterfaces.h:886
FNamedTaskThread::ProcessTasksNamedThread(int, bool) TaskGraph.cpp:710
FNamedTaskThread::ProcessTasksUntilQuit(int) TaskGraph.cpp:601
FTaskGraphImplementation::ProcessThreadUntilRequestReturn(Type) TaskGraph.cpp:1480
RenderingThreadMain(FEvent *) RenderingThread.cpp:372
FRenderingThread::Run() RenderingThread.cpp:526
FRunnableThreadWin::Run() WindowsRunnableThread.cpp:84
FRunnableThreadWin::GuardedRun() WindowsRunnableThread.cpp:27
FRunnableThreadWin::_ThreadProc(void *) WindowsRunnableThread.h:37
This is from the command FRHICommandSetGraphicsPipelineState
FRHIUniformBuffer::Release() RHIResources.h:777
TRefCountPtr::operator=(FRHIUniformBuffer *) RefCounting.h:205
FD3D11DynamicRHI::RHISetBoundShaderState(FRHIBoundShaderState *) D3D11Commands.cpp:361
IRHICommandContextPSOFallback::RHISetGraphicsPipelineState(FRHIGraphicsPipelineState *, bool) RHIContext.h:789
FD3D11DynamicRHI::RHISetGraphicsPipelineState(FRHIGraphicsPipelineState *, bool) D3D11Commands.cpp:193
FRHICommandSetGraphicsPipelineState::Execute(FRHICommandListBase &) RHICommandListCommandExecutes.inl:296
FRHICommand::ExecuteAndDestruct(FRHICommandListBase &, FRHICommandListDebugContext &) RHICommandList.h:765
FRHICommandListExecutor::ExecuteInner_DoExecute(FRHICommandListBase &) RHICommandList.cpp:372
FRHICommandListExecutor::ExecuteInner(FRHICommandListBase &) RHICommandList.cpp:657
FRHICommandListExecutor::ExecuteList(FRHICommandListImmediate &) RHICommandList.cpp:708
FRHICommandListImmediate::ImmediateFlush(Type) RHICommandList.inl:98
FRHICommandList::EndScene() RHICommandList.cpp:1574
TRDGLambdaPass::ExecuteImpl(FRHIComputeCommandList &) RenderGraphPass.h:424
FRDGPass::Execute(FRHIComputeCommandList &) RenderGraphPass.cpp:305
FRDGBuilder::ExecutePass(FRDGPass *) RenderGraphBuilder.cpp:1638
FRDGBuilder::Execute() RenderGraphBuilder.cpp:1308
FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate &) DeferredShadingRenderer.cpp:2709
RenderViewFamily_RenderThread(FRHICommandListImmediate &, FSceneRenderer *) SceneRendering.cpp:3739
<lambda_83aa676a...>::operator()(FRHICommandListImmediate &) SceneRendering.cpp:4007
TEnqueueUniqueRenderCommandType<`FRendererModule::BeginRenderingViewFamily'::`43'::FDrawSceneCommandName,<lambda_83aa676af25f62e876425d334fdfc6e4> >::DoTask(Type,const TRefCountPtr<FGraphEvent> &) RenderingThread.h:183
TGraphTask<TEnqueueUniqueRenderCommandType<`FRendererModule::BeginRenderingViewFamily'::`43'::FDrawSceneCommandName,<lambda_83aa676af25f62e876425d334fdfc6e4> > >::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32> > &,Type) TaskGraphInterfaces.h:886
FNamedTaskThread::ProcessTasksNamedThread(int, bool) TaskGraph.cpp:710
FNamedTaskThread::ProcessTasksUntilQuit(int) TaskGraph.cpp:601
FTaskGraphImplementation::ProcessThreadUntilRequestReturn(Type) TaskGraph.cpp:1480
RenderingThreadMain(FEvent *) RenderingThread.cpp:372
FRenderingThread::Run() RenderingThread.cpp:526
FRunnableThreadWin::Run() WindowsRunnableThread.cpp:84
FRunnableThreadWin::GuardedRun() WindowsRunnableThread.cpp:27
FRunnableThreadWin::_ThreadProc(void *) WindowsRunnableThread.h:37
They are called every frame.
Now I don’t know why element of TArray<TRefCountPtr<FRHIResource> > ResourceTable;
becomes wild pointer? How does it relevent with texture reloading? Anyone has idea? Thank you!
Env
Debug Config, Game Target
UE 4.27.2
Windows build