Browse Source

Avoid locking the mutex and blocking the main/render thread on destroy at the same time, which could cause deadlocks under some circumstances.

master
Nico de Poel 11 months ago
parent
commit
bfcfa4f627
  1. 32
      FSR3UnityPlugin.cpp

32
FSR3UnityPlugin.cpp

@ -292,18 +292,27 @@ extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_InitApi()
static void DestroyFeature(uint32_t featureSlot)
{
uint64_t dispatchFrameValue = 0;
{
// We need to lock the features list while we're accessing it, but we also can't hold the lock while blocking the main/render thread at the same time.
// Otherwise we're very likely to create a deadlock. So instead we only lock for as long as is necessary to grab the data we need from the feature.
std::lock_guard<std::mutex> lock(s_FeatureMutex);
auto& feature = s_Features[featureSlot];
if (feature.upscalingContext == nullptr && !feature.isValid)
return;
dispatchFrameValue = feature.dispatchFrameValue;
}
if (s_GraphicsD3D12 != nullptr)
{
// If there's still an upscale dispatch executing on the current frame, wait until rendering is finished before destroying its context.
// Thanks to https://alextardif.com/D3D11To12P1.html for explaining how fences work and how to make the CPU wait for a command queue to complete.
ID3D12Fence* frameFence = s_GraphicsD3D12->GetFrameFence();
if (feature.dispatchFrameValue > frameFence->GetCompletedValue())
if (dispatchFrameValue > frameFence->GetCompletedValue())
{
frameFence->SetEventOnCompletion(feature.dispatchFrameValue, s_FrameFenceEventHandle);
frameFence->SetEventOnCompletion(dispatchFrameValue, s_FrameFenceEventHandle);
WaitForSingleObjectEx(s_FrameFenceEventHandle, INFINITE, false);
}
}
@ -311,13 +320,16 @@ static void DestroyFeature(uint32_t featureSlot)
{
UnityVulkanRecordingState state;
s_GraphicsVulkan->CommandRecordingState(&state, kUnityVulkanGraphicsQueueAccess_DontCare);
if (feature.dispatchFrameValue > state.safeFrameNumber)
if (dispatchFrameValue > state.safeFrameNumber)
{
UnityVulkanInstance instance = s_GraphicsVulkan->Instance();
vkQueueWaitIdle(instance.graphicsQueue);
}
}
std::lock_guard<std::mutex> lock(s_FeatureMutex);
auto& feature = s_Features[featureSlot];
if (feature.upscalingContext != nullptr)
{
s_ffxFunctions.DestroyContext(&feature.upscalingContext, nullptr);
@ -331,14 +343,20 @@ static void DestroyFeature(uint32_t featureSlot)
}
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_ShutdownApi()
{
size_t numFeatures = 0;
{
std::lock_guard<std::mutex> lock(s_FeatureMutex);
numFeatures = s_Features.size();
}
for (uint32_t slot = 0; slot < s_Features.size(); ++slot)
for (uint32_t slot = 0; slot < numFeatures; ++slot)
{
DestroyFeature(slot);
}
std::lock_guard<std::mutex> lock(s_FeatureMutex);
if (s_ffxModule != nullptr)
{
FreeLibrary(s_ffxModule);
@ -442,8 +460,6 @@ static FfxApiResource GetVulkanTextureResource(UnityVulkanInstance& instance, FS
// Plugin function to handle a specific rendering event
static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data)
{
std::lock_guard<std::mutex> lock(s_FeatureMutex);
if (s_DX11Device == nullptr && s_DX12BackendDesc.device == nullptr && s_VulkanBackendDesc.vkDevice == nullptr)
return;
@ -461,6 +477,8 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data)
}
case BaseEventId + FSR3PluginEvent::eExecute:
{
std::lock_guard<std::mutex> lock(s_FeatureMutex);
auto* params = (FSR3CommandExecutionData*)data;
if (params->featureSlot < 0 || params->featureSlot >= s_Features.size())
return;
@ -598,6 +616,8 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data)
}
case BaseEventId + FSR3PluginEvent::eInit:
{
std::lock_guard<std::mutex> lock(s_FeatureMutex);
auto* params = (FSR3CommandInitializationData*)data;
if (params->featureSlot < 0 || params->featureSlot >= s_Features.size())
return;

Loading…
Cancel
Save