use std::ffi::c_void;
use std::ptr;
use concinnity_core::gfx::jitter;
use concinnity_core::render::dlss::DlssPreset;
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::history_reset::UpscalerResetLatch;
use concinnity_core::render::reactive_mask::ReactiveReader;
use windows::Win32::Graphics::Direct3D12::*;
use windows::core::Interface;
use super::{UpscaleBackend, UpscaleDevice, UpscaleInputs, UpscaleOutput, UpscalerTarget};
use crate::upscale_sdk::dlss::{
ENGINE_VERSION, NVSDK_NGX_ENGINE_TYPE_CUSTOM, NVSDK_NGX_FEATURE_SUPERSAMPLING,
NVSDK_NGX_RESULT_FAIL, NVSDK_NGX_VERSION_API, P_COLOR, P_DEPTH, P_MOTION_VECTORS, P_OUTPUT,
PROJECT_ID, app_data_path, create_with_preset_fallback, loaded_feature, ngx_succeeded,
set_create_parameters, set_evaluate_parameters, supersampling_unavailable,
};
use crate::upscale_sdk::{UpscaleCamera, UpscaleExtent};
unsafe extern "C" {
fn NVSDK_NGX_D3D12_Init_with_ProjectID(
project_id: *const u8,
engine_type: i32,
engine_version: *const u8,
app_data_path: *const u16,
device: *mut c_void,
feature_info: *const c_void,
sdk_version: i32,
) -> u32;
fn NVSDK_NGX_D3D12_Shutdown1(device: *mut c_void) -> u32;
fn NVSDK_NGX_D3D12_GetCapabilityParameters(out_params: *mut *mut c_void) -> u32;
fn NVSDK_NGX_D3D12_DestroyParameters(params: *mut c_void) -> u32;
fn NVSDK_NGX_D3D12_CreateFeature(
cmd: *mut c_void,
feature_id: i32,
params: *const c_void,
out_handle: *mut *mut c_void,
) -> u32;
fn NVSDK_NGX_D3D12_ReleaseFeature(handle: *mut c_void) -> u32;
fn NVSDK_NGX_D3D12_EvaluateFeature_C(
cmd: *mut c_void,
handle: *const c_void,
params: *const c_void,
callback: *const c_void,
) -> u32;
fn NVSDK_NGX_Parameter_SetD3d12Resource(params: *mut c_void, name: *const u8, res: *mut c_void);
}
struct NgxSession {
device: ID3D12Device,
params: *mut c_void,
feature: *mut c_void,
}
impl Drop for NgxSession {
fn drop(&mut self) {
unsafe {
if !self.feature.is_null() {
NVSDK_NGX_D3D12_ReleaseFeature(self.feature);
}
if !self.params.is_null() {
NVSDK_NGX_D3D12_DestroyParameters(self.params);
}
NVSDK_NGX_D3D12_Shutdown1(self.device.as_raw());
}
}
}
impl NgxSession {
fn create_feature(
&mut self,
gpu: UpscaleDevice<'_>,
extent: UpscaleExtent,
preset: DlssPreset,
) -> RenderResult<bool> {
if !self.feature.is_null() {
unsafe { NVSDK_NGX_D3D12_ReleaseFeature(self.feature) };
self.feature = ptr::null_mut();
}
unsafe { set_create_parameters(self.params, extent, preset) };
let mut create_rc = NVSDK_NGX_RESULT_FAIL;
crate::directx::texture::one_shot_submit(gpu.device, gpu.command_queue, |cmd| {
create_rc = unsafe {
NVSDK_NGX_D3D12_CreateFeature(
cmd.as_raw(),
NVSDK_NGX_FEATURE_SUPERSAMPLING,
self.params,
&mut self.feature,
)
};
})?;
if !ngx_succeeded(create_rc) || self.feature.is_null() {
self.feature = ptr::null_mut();
tracing::warn!(
"DLSS: CreateFeature (render preset {}) returned {create_rc:#x}",
preset.label()
);
return Ok(false);
}
Ok(true)
}
}
pub(super) struct DlssUpscaler {
ngx: NgxSession,
extent: UpscaleExtent,
output: UpscaleOutput,
reset: UpscalerResetLatch,
preset: DlssPreset,
}
unsafe impl Send for DlssUpscaler {}
impl DlssUpscaler {
pub(super) fn try_new(target: UpscalerTarget<'_>) -> RenderResult<Option<Self>> {
let UpscalerTarget {
gpu,
extent,
descriptors,
dlss_preset,
} = target;
let app_path = app_data_path();
let rc = unsafe {
NVSDK_NGX_D3D12_Init_with_ProjectID(
PROJECT_ID.as_ptr(),
NVSDK_NGX_ENGINE_TYPE_CUSTOM,
ENGINE_VERSION.as_ptr(),
app_path.as_ptr(),
gpu.device.as_raw(),
ptr::null(),
NVSDK_NGX_VERSION_API,
)
};
if !ngx_succeeded(rc) {
tracing::warn!(
"DLSS: NVSDK_NGX_D3D12_Init returned {rc:#x} (NGX unavailable / not RTX). \
Trying the next backend."
);
return Ok(None);
}
let mut ngx = NgxSession {
device: gpu.device.clone(),
params: ptr::null_mut(),
feature: ptr::null_mut(),
};
let rc = unsafe { NVSDK_NGX_D3D12_GetCapabilityParameters(&mut ngx.params) };
if !ngx_succeeded(rc) || ngx.params.is_null() {
tracing::warn!(
"DLSS: GetCapabilityParameters returned {rc:#x}; trying the next backend"
);
return Ok(None);
}
if let Some(reason) = unsafe { supersampling_unavailable(ngx.params) } {
tracing::warn!("DLSS: {reason}; trying the next backend");
return Ok(None);
}
let Some(created) = create_with_preset_fallback(
"DLSS",
dlss_preset,
|preset| ngx.create_feature(gpu, extent, preset),
loaded_feature,
)?
else {
return Ok(None);
};
let output = UpscaleOutput::create(gpu.device, extent.output, descriptors)?;
tracing::info!(
"DLSS: feature created: {extent}, {}, render preset {}",
created.library,
created.preset.label()
);
Ok(Some(Self {
ngx,
extent,
output,
reset: UpscalerResetLatch::default(),
preset: created.preset,
}))
}
}
impl UpscaleBackend for DlssUpscaler {
fn extent(&self) -> UpscaleExtent {
self.extent
}
fn output(&self) -> &UpscaleOutput {
&self.output
}
fn jitter_offset(&self, frame_index: u32) -> [f32; 2] {
jitter::offset(frame_index)
}
fn dispatch(
&self,
cmd: &ID3D12GraphicsCommandList,
inputs: UpscaleInputs<'_>,
camera: UpscaleCamera,
) -> RenderResult<()> {
let params = self.ngx.params;
let resources = [
(P_COLOR, inputs.color),
(P_OUTPUT, self.output.resource()),
(P_DEPTH, inputs.depth),
(P_MOTION_VECTORS, inputs.motion_vectors),
];
unsafe {
for (name, resource) in resources {
NVSDK_NGX_Parameter_SetD3d12Resource(params, name.as_ptr(), resource.as_raw());
}
set_evaluate_parameters(
params,
camera.jitter_offset,
crate::upscale_reset::consume(&self.reset),
self.extent,
);
}
let rc = unsafe {
NVSDK_NGX_D3D12_EvaluateFeature_C(cmd.as_raw(), self.ngx.feature, params, ptr::null())
};
if !ngx_succeeded(rc) {
return Err(RenderError::Other(format!(
"NVSDK_NGX_D3D12_EvaluateFeature returned {rc:#x}"
)));
}
Ok(())
}
fn request_history_reset(&self) {
self.reset.request();
}
fn reactive_reader(&self) -> ReactiveReader {
ReactiveReader::Dlss
}
fn dlss_preset(&self) -> Option<DlssPreset> {
Some(self.preset)
}
}