You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
339 lines
11 KiB
339 lines
11 KiB
#include <stdint.h>
|
|
|
|
#include <vector>
|
|
#include <queue>
|
|
#include <mutex>
|
|
|
|
#include "UnityPluginAPI/IUnityInterface.h"
|
|
#include "UnityPluginAPI/IUnityLog.h"
|
|
#include "UnityPluginAPI/IUnityGraphics.h"
|
|
#include "UnityPluginAPI/IUnityRenderingExtensions.h"
|
|
|
|
#include <d3d12.h>
|
|
#include <dxgi.h>
|
|
#include "UnityPluginAPI/IUnityGraphicsD3D12.h"
|
|
|
|
#include "ffx_api/ffx_upscale.hpp"
|
|
#include "ffx_api/dx12/ffx_api_dx12.hpp"
|
|
|
|
#include "FSR3UnityTypes.h"
|
|
|
|
static const int32_t BaseEventId = 0;
|
|
|
|
static IUnityInterfaces* s_UnityInterfaces = nullptr;
|
|
static IUnityLog* s_Log = nullptr;
|
|
static IUnityGraphics* s_Graphics = nullptr;
|
|
static IUnityGraphicsD3D12v7* s_GraphicsD3D12 = nullptr;
|
|
static UnityGfxRenderer s_RendererType = kUnityGfxRendererNull;
|
|
|
|
static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType);
|
|
static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data);
|
|
static void UNITY_INTERFACE_API OnSetTextureEvent(int eventID, void* data);
|
|
|
|
struct FSR3Feature
|
|
{
|
|
ffx::Context upscalingContext;
|
|
|
|
uint32_t upscaleSizeWidth;
|
|
uint32_t upscaleSizeHeight;
|
|
uint32_t flags;
|
|
|
|
FSR3TextureTable textureTable;
|
|
};
|
|
|
|
static std::vector<FSR3Feature> s_Features;
|
|
static std::queue<uint32_t> s_FeatureSlots;
|
|
static std::mutex s_FeatureMutex;
|
|
|
|
static ffx::CreateBackendDX12Desc s_BackendDesc{};
|
|
|
|
// Unity plugin load event
|
|
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
|
|
{
|
|
s_UnityInterfaces = unityInterfaces;
|
|
s_Log = unityInterfaces->Get<IUnityLog>();
|
|
s_Graphics = unityInterfaces->Get<IUnityGraphics>();
|
|
|
|
s_Graphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
|
|
|
|
// Run OnGraphicsDeviceEvent(initialize) manually on plugin load
|
|
// to not miss the event in case the graphics device is already initialized
|
|
OnGraphicsDeviceEvent(kUnityGfxDeviceEventInitialize);
|
|
}
|
|
|
|
// Unity plugin unload event
|
|
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload()
|
|
{
|
|
s_Graphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent);
|
|
}
|
|
|
|
static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType)
|
|
{
|
|
switch (eventType)
|
|
{
|
|
case kUnityGfxDeviceEventInitialize:
|
|
{
|
|
s_RendererType = s_Graphics->GetRenderer();
|
|
if (s_RendererType != kUnityGfxRendererD3D12)
|
|
return;
|
|
|
|
s_GraphicsD3D12 = s_UnityInterfaces->Get<IUnityGraphicsD3D12v7>();
|
|
if (s_GraphicsD3D12 == nullptr)
|
|
{
|
|
UNITY_LOG_ERROR(s_Log, "Could not obtain D3D12 Graphics interface!");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case kUnityGfxDeviceEventShutdown:
|
|
{
|
|
s_GraphicsD3D12 = nullptr;
|
|
s_RendererType = kUnityGfxRendererNull;
|
|
break;
|
|
}
|
|
case kUnityGfxDeviceEventBeforeReset:
|
|
{
|
|
break;
|
|
}
|
|
case kUnityGfxDeviceEventAfterReset:
|
|
{
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
|
|
// Thread-safe allocation of FSR3 feature slot
|
|
static uint32_t AllocateFeatureSlot()
|
|
{
|
|
std::lock_guard<std::mutex> lock(s_FeatureMutex);
|
|
|
|
if (s_FeatureSlots.empty())
|
|
{
|
|
uint32_t featureSlot = (uint32_t)s_Features.size();
|
|
s_Features.push_back(std::move(FSR3Feature()));
|
|
return featureSlot;
|
|
}
|
|
|
|
uint32_t featureSlot = s_FeatureSlots.front();
|
|
s_FeatureSlots.pop();
|
|
return featureSlot;
|
|
}
|
|
|
|
// Thread-safe freeing and clearing of FSR3 feature slot
|
|
static void FreeFeatureSlot(uint32_t featureSlot)
|
|
{
|
|
std::lock_guard<std::mutex> lock(s_FeatureMutex);
|
|
|
|
s_FeatureSlots.push(featureSlot);
|
|
memset(&s_Features[featureSlot], 0, sizeof(FSR3Feature));
|
|
}
|
|
|
|
extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_InitApi()
|
|
{
|
|
if (s_GraphicsD3D12 == nullptr)
|
|
return false;
|
|
|
|
ID3D12Device* device = s_GraphicsD3D12->GetDevice();
|
|
if (device == nullptr)
|
|
return false;
|
|
|
|
s_BackendDesc.device = device;
|
|
return true;
|
|
}
|
|
|
|
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_ShutdownApi()
|
|
{
|
|
// TODO: destroy any remaining FSR3Features?
|
|
s_BackendDesc.device = nullptr;
|
|
}
|
|
|
|
extern "C" uint32_t UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_GetDeviceVersion()
|
|
{
|
|
return 0x0u;
|
|
}
|
|
|
|
extern "C" UnityRenderingEventAndData UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_GetRenderEventCallback()
|
|
{
|
|
return OnRenderEventAndData;
|
|
}
|
|
|
|
extern "C" UnityRenderingEventAndData UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_GetSetTextureEventCallback()
|
|
{
|
|
return OnSetTextureEvent;
|
|
}
|
|
|
|
extern "C" uint32_t UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_CreateFeatureSlot()
|
|
{
|
|
return AllocateFeatureSlot();
|
|
}
|
|
|
|
extern "C" float UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_GetUpscaleRatioFromQualityMode(FSR3Quality qualityMode)
|
|
{
|
|
switch (qualityMode)
|
|
{
|
|
case FSR3Quality::qQuality:
|
|
return 1.5f;
|
|
case FSR3Quality::qBalanced:
|
|
return 1.7f;
|
|
case FSR3Quality::qPerformance:
|
|
return 2.0f;
|
|
case FSR3Quality::qUltraPerformance:
|
|
return 3.0f;
|
|
default:
|
|
return 1.0f;
|
|
}
|
|
}
|
|
|
|
extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_GetRenderResolutionFromQualityMode(FSR3Quality qualityMode, uint32_t displayWidth, uint32_t displayHeight, uint32_t* renderWidth, uint32_t* renderHeight)
|
|
{
|
|
if (renderWidth == nullptr || renderHeight == nullptr)
|
|
return false;
|
|
|
|
float ratio = AMDUP_GetUpscaleRatioFromQualityMode(qualityMode);
|
|
*renderWidth = (uint32_t)roundf(displayWidth / ratio);
|
|
*renderHeight = (uint32_t)roundf(displayHeight / ratio);
|
|
return true;
|
|
}
|
|
|
|
extern "C" int32_t UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_GetBaseEventId()
|
|
{
|
|
return BaseEventId;
|
|
}
|
|
|
|
// Plugin function to handle a specific rendering event
|
|
static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data)
|
|
{
|
|
if (s_GraphicsD3D12 == nullptr || s_BackendDesc.device == nullptr)
|
|
return;
|
|
|
|
// User rendering code
|
|
switch (eventID)
|
|
{
|
|
case BaseEventId + FSR3PluginEvent::eDestroyFeature:
|
|
{
|
|
uint32_t featureSlot = (uint32_t)(int64_t)data;
|
|
if (featureSlot < 0 || featureSlot >= s_Features.size())
|
|
return;
|
|
|
|
auto& feature = s_Features[featureSlot];
|
|
ffx::DestroyContext(feature.upscalingContext);
|
|
|
|
FreeFeatureSlot(featureSlot);
|
|
break;
|
|
}
|
|
case BaseEventId + FSR3PluginEvent::eExecute:
|
|
{
|
|
auto* params = (FSR3CommandExecutionData*)data;
|
|
if (params->featureSlot < 0 || params->featureSlot >= s_Features.size())
|
|
return;
|
|
|
|
auto& feature = s_Features[params->featureSlot];
|
|
|
|
UnityGraphicsD3D12RecordingState state;
|
|
s_GraphicsD3D12->CommandRecordingState(&state);
|
|
|
|
ffx::DispatchDescUpscale dispatchUpscale{};
|
|
dispatchUpscale.commandList = state.commandList;
|
|
|
|
dispatchUpscale.color = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.colorInput, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
|
dispatchUpscale.depth = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.depth, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
|
dispatchUpscale.motionVectors = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.motionVectors, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
|
dispatchUpscale.exposure = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.exposureTexture, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
|
dispatchUpscale.reactive = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.reactiveMask, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
|
dispatchUpscale.transparencyAndComposition = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.transparencyMask, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
|
dispatchUpscale.output = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.colorOutput, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); // TODO: shouldn't this be FFX_API_RESOURCE_STATE_UNORDERED_ACCESS?
|
|
|
|
dispatchUpscale.jitterOffset.x = params->jitterOffsetX; // TODO: might need to negate these
|
|
dispatchUpscale.jitterOffset.y = params->jitterOffsetY;
|
|
dispatchUpscale.motionVectorScale.x = params->MVScaleX;
|
|
dispatchUpscale.motionVectorScale.y = params->MVScaleY;
|
|
dispatchUpscale.reset = params->reset;
|
|
dispatchUpscale.enableSharpening = params->enableSharpening;
|
|
dispatchUpscale.sharpness = params->sharpness;
|
|
dispatchUpscale.frameTimeDelta = params->frameTimeDelta;
|
|
dispatchUpscale.preExposure = params->preExposure;
|
|
dispatchUpscale.renderSize.width = params->renderSizeWidth;
|
|
dispatchUpscale.renderSize.height = params->renderSizeHeight;
|
|
dispatchUpscale.upscaleSize.width = feature.upscaleSizeWidth;
|
|
dispatchUpscale.upscaleSize.height = feature.upscaleSizeHeight;
|
|
dispatchUpscale.cameraFovAngleVertical = params->cameraFovAngleVertical;
|
|
|
|
// TODO: it's possible that Unity already does this flip, check!
|
|
if (feature.flags & FFX_UPSCALE_ENABLE_DEPTH_INVERTED)
|
|
{
|
|
dispatchUpscale.cameraFar = params->cameraNear;
|
|
dispatchUpscale.cameraNear = params->cameraFar;
|
|
}
|
|
else
|
|
{
|
|
dispatchUpscale.cameraFar = params->cameraFar;
|
|
dispatchUpscale.cameraNear = params->cameraNear;
|
|
}
|
|
|
|
ffx::Dispatch(feature.upscalingContext, dispatchUpscale);
|
|
break;
|
|
}
|
|
case BaseEventId + FSR3PluginEvent::ePostExecute:
|
|
{
|
|
auto* params = (FSR3CommandExecutionData*)data;
|
|
break;
|
|
}
|
|
case BaseEventId + FSR3PluginEvent::eInit:
|
|
{
|
|
auto* params = (FSR3CommandInitializationData*)data;
|
|
if (params->featureSlot < 0 || params->featureSlot >= s_Features.size())
|
|
return;
|
|
|
|
auto& feature = s_Features[params->featureSlot];
|
|
feature.upscaleSizeWidth = params->displaySizeWidth;
|
|
feature.upscaleSizeHeight = params->displaySizeHeight;
|
|
feature.flags = params->flags;
|
|
|
|
ffx::CreateContextDescUpscale createUpscaling;
|
|
createUpscaling.maxUpscaleSize = { params->displaySizeWidth, params->displaySizeHeight };
|
|
createUpscaling.maxRenderSize = { params->maxRenderSizeWidth, params->maxRenderSizeHeight };
|
|
createUpscaling.flags = params->flags;
|
|
|
|
ffx::CreateContext(feature.upscalingContext, nullptr, createUpscaling, s_BackendDesc);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void UNITY_INTERFACE_API OnSetTextureEvent(int eventID, void* data)
|
|
{
|
|
if (s_GraphicsD3D12 == nullptr)
|
|
return;
|
|
|
|
auto* params = (UnityRenderingExtTextureUpdateParamsV2*)data;
|
|
|
|
uint32_t featureSlot = (params->userData >> 16) & 0xFFFF;
|
|
uint32_t textureSlot = (params->userData >> 1) & 0x7FFF;
|
|
uint32_t clearTextureTable = params->userData & 0x1;
|
|
|
|
if (featureSlot < 0 || featureSlot >= s_Features.size())
|
|
return;
|
|
|
|
auto& feature = s_Features[featureSlot];
|
|
|
|
// User rendering code
|
|
switch (eventID)
|
|
{
|
|
case kUnityRenderingExtEventUpdateTextureBeginV2:
|
|
{
|
|
if (clearTextureTable)
|
|
{
|
|
memset(&feature.textureTable, 0, sizeof(FSR3TextureTable));
|
|
}
|
|
break;
|
|
}
|
|
case kUnityRenderingExtEventUpdateTextureEndV2:
|
|
{
|
|
// We organized the texture table struct to be ordered the same as the texture slot enum
|
|
// This way we can use the texture slot value simply as a pointer offset into the texture table
|
|
*(((intptr_t*)&feature.textureTable) + textureSlot) = (intptr_t)s_GraphicsD3D12->TextureFromNativeTexture((UnityTextureID)params->textureID);
|
|
break;
|
|
}
|
|
}
|
|
}
|