Browse Source

Await end of frame on the CPU before destroying a context, if the GPU has a dispatch queued on the current frame. Fixes remaining crashes in D3D12 when enabling/disabling the plugin too rapidly.

master
Nico de Poel 11 months ago
parent
commit
49fa7d69a2
  1. 30
      FSR3UnityPlugin.cpp

30
FSR3UnityPlugin.cpp

@ -50,6 +50,8 @@ struct FSR3Feature
uint32_t upscaleSizeHeight;
uint32_t flags;
uint64_t dispatchFrameValue;
FSR3TextureTable textureTable;
};
@ -65,6 +67,8 @@ static ffx::CreateBackendVKDesc s_VulkanBackendDesc{};
static FfxDevice s_DX11Device = nullptr;
static FfxFsr2Interface s_Fsr2Interface;
static HANDLE s_FrameFenceEventHandle = nullptr;
// Unity plugin load event
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
@ -221,6 +225,7 @@ extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_InitApi()
return false;
s_DX12BackendDesc.device = device;
s_FrameFenceEventHandle = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
return LoadFidelityFXLibrary(TEXT("amd_fidelityfx_dx12.dll"));
}
@ -243,6 +248,19 @@ extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_InitApi()
static void DestroyFeature(uint32_t featureSlot)
{
auto& feature = s_Features[featureSlot];
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())
{
frameFence->SetEventOnCompletion(feature.dispatchFrameValue, s_FrameFenceEventHandle);
WaitForSingleObjectEx(s_FrameFenceEventHandle, INFINITE, false);
}
}
if (feature.upscalingContext != nullptr)
{
s_ffxFunctions.DestroyContext(&feature.upscalingContext, nullptr);
@ -276,6 +294,12 @@ extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_ShutdownApi()
s_Fsr2Interface.scratchBuffer = nullptr;
}
if (s_FrameFenceEventHandle != nullptr)
{
CloseHandle(s_FrameFenceEventHandle);
s_FrameFenceEventHandle = nullptr;
}
s_VulkanBackendDesc.vkDevice = nullptr;
s_VulkanBackendDesc.vkPhysicalDevice = nullptr;
s_VulkanBackendDesc.vkDeviceProcAddr = nullptr;
@ -442,6 +466,9 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data)
s_GraphicsD3D12->CommandRecordingState(&state);
dispatchUpscale.commandList = state.commandList;
// Keep track of which frame this dispatch is happening on, so we can verify when it's finished
feature.dispatchFrameValue = s_GraphicsD3D12->GetNextFrameFenceValue();
dispatchUpscale.color = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.colorInput.image, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ);
dispatchUpscale.depth = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.depth.image, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ);
dispatchUpscale.motionVectors = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.motionVectors.image, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ);
@ -456,6 +483,9 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data)
s_GraphicsVulkan->CommandRecordingState(&state, kUnityVulkanGraphicsQueueAccess_DontCare);
dispatchUpscale.commandList = state.commandBuffer;
// TODO: probably need to make use of state.currentFrameNumber and state.safeFrameNumber to keep track of when it's safe to destroy the upscaling context
feature.dispatchFrameValue = state.currentFrameNumber;
UnityVulkanInstance instance = s_GraphicsVulkan->Instance();
dispatchUpscale.color = GetVulkanTextureResource(instance, feature.textureTable.colorInput);
dispatchUpscale.depth = GetVulkanTextureResource(instance, feature.textureTable.depth);

Loading…
Cancel
Save