use std::ffi::{CStr, c_void};
use concinnity_core::render::error::RenderResult;
use concinnity_core::render::reactive_mask::ReactiveReader;
use windows::Win32::Graphics::Direct3D12::*;
use windows::core::Interface;
use super::{Dll, UpscaleBackend, UpscaleInputs, UpscaleOutput, UpscalerTarget};
use crate::upscale_sdk::fsr::{FfxContext, FfxDispatchHandles, ffxApiHeader};
use crate::upscale_sdk::{UpscaleCamera, UpscaleExtent};
const LIBRARY: &CStr = c"amd_fidelityfx_dx12.dll";
const LABEL: &str = "FidelityFX FSR3";
const FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_DX12: u64 = 0x000_0002;
#[repr(C)]
struct ffxCreateBackendDX12Desc {
header: ffxApiHeader,
device: *mut c_void,
}
pub(super) struct FsrUpscaler {
ffx: FfxContext<Dll>,
output: UpscaleOutput,
}
unsafe impl Send for FsrUpscaler {}
impl FsrUpscaler {
pub(super) fn try_new(target: UpscalerTarget<'_>) -> RenderResult<Option<Self>> {
let Some(library) = Dll::open(LIBRARY) else {
if cfg!(ffx_sdk_bundled) {
tracing::warn!(
"{LABEL}: amd_fidelityfx_dx12.dll was bundled at build time but failed to \
load at runtime; trying the next backend"
);
} else {
tracing::warn!(
"{LABEL}: amd_fidelityfx_dx12.dll not found (build.rs did not bundle it; set \
CN_FIDELITYFX_SDK or put the DLL on PATH). Trying the next backend."
);
}
return Ok(None);
};
let mut backend = ffxCreateBackendDX12Desc {
header: ffxApiHeader::new(FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_DX12),
device: target.gpu.device.as_raw(),
};
let ffx = unsafe { FfxContext::create(library, &mut backend.header, target.extent, LABEL) };
let Some(ffx) = ffx else {
return Ok(None);
};
let output =
UpscaleOutput::create(target.gpu.device, target.extent.output, target.descriptors)?;
Ok(Some(Self { ffx, output }))
}
}
impl UpscaleBackend for FsrUpscaler {
fn extent(&self) -> UpscaleExtent {
self.ffx.extent()
}
fn output(&self) -> &UpscaleOutput {
&self.output
}
fn jitter_offset(&self, frame_index: u32) -> [f32; 2] {
self.ffx.jitter_offset(frame_index)
}
fn dispatch(
&self,
cmd: &ID3D12GraphicsCommandList,
inputs: UpscaleInputs<'_>,
camera: UpscaleCamera,
) -> RenderResult<()> {
let handles = FfxDispatchHandles {
command_list: cmd.as_raw(),
color: inputs.color.as_raw(),
depth: inputs.depth.as_raw(),
motion_vectors: inputs.motion_vectors.as_raw(),
reactive: inputs
.reactive
.map_or(std::ptr::null_mut(), Interface::as_raw),
output: self.output.resource().as_raw(),
};
unsafe { self.ffx.dispatch(handles, camera) }
}
fn request_history_reset(&self) {
self.ffx.request_history_reset();
}
fn reactive_reader(&self) -> ReactiveReader {
ReactiveReader::Fsr
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::{offset_of, size_of};
#[test]
fn ffx_dx12_backend_layout_matches_sdk_v114() {
assert_eq!(size_of::<ffxCreateBackendDX12Desc>(), 24);
assert_eq!(offset_of!(ffxCreateBackendDX12Desc, header), 0);
assert_eq!(offset_of!(ffxCreateBackendDX12Desc, device), 16);
}
}