Hello,
Between 5.5 and 5.6, FD3D12ContextCommon::CloseCommandList() changed the order of internal events causing FlushResourceBarriers() to be called prior to a check statement which ensured the commandlist was open.
Internally, FlushResourceBarriers() appears to open the commandlist if it’s currently closed, this breaks the order of events that would occur during opening/closure of an a derived commandlist. FD3D12CommandContext inherits from FD3D12ContextCommon and will perform the following operations if CloseCommandList() is called on an unopened list, with pending barriers:
1. FD3D12CommandContext::CloseCommandList()
a. FD3D12DescriptorCache::CloseCommandList() is called
b. FD3D12ContextCommon::CloseCommandList() is called
2. FD3D12ContextCommon::CloseCommandList() calls FlushResourceBarriers() which will then try to open the commandlist
3. FD3D12CommandContext::OpenCommandList()
a. FD3D12ContextCommon::OpenCommandList() is called
b. FD3D12DescriptorCache::OpenCommandList() is called
4. FD3D12ContextCommon::CloseCommandList() completes
The result is that the descriptor cache and any subsequent dependencies are left in an ‘open’ state while the underlying commandlist is closed.
We’ve spotted this in our version of the engine as we have dependent code that is reliant on Open/Close occuring in the correct order. We’ve also applied a fix locally, though are open to better options:
void FD3D12ContextCommon::CloseCommandList()
{
checkf(IsPendingCommands(), TEXT("The command list is empty."));
// <#CCP_MOD>
// CommandList has pending resource barriers still
// Open/Close here so that inherited closures occur in order.
if (IsPendingCommands() && !IsOpen())
{
OpenCommandList();
CloseCommandList();
return;
}
// </#CCP_MOD>
// Do this before we insert the final timestamp to ensure we're timing all the work on the command list.
// If the command list only has barrier work to do, this will open the command list for the first time
FlushResourceBarriers();
...
Thanks,
Jared