From 6a71511129d2f617ef3d770befe7472c225732c5 Mon Sep 17 00:00:00 2001 From: RavenSystem Date: Sun, 3 Dec 2023 17:15:59 +0100 Subject: [PATCH] Version 3.0 --- README.md | 12 +- src/config.cpp | 67 ++++++- src/config.h | 10 +- src/d3d11/d3d11_injector.cpp | 8 +- src/d3d11/d3d11_post_processor.cpp | 165 ++++++++++++++--- src/d3d11/d3d11_post_processor.h | 15 +- src/d3d11/d3d11_variable_rate_shading.cpp | 47 +++-- src/d3d11/d3d11_variable_rate_shading.h | 3 + src/dllmain.cpp | 2 +- src/hotkeys.cpp | 12 -- src/hrm/hidden_radial_mask.hlsl | 1 - src/hrm/radial_density_mask.frag.hlsl | 29 +++ src/hrm/reconstruction.compute.hlsl | 205 ++++++++++++++++++++++ src/openvr/openvr_manager.cpp | 1 + src/types.h | 1 + vrperfkit_RSF.yml | 49 ++++-- 16 files changed, 538 insertions(+), 89 deletions(-) create mode 100644 src/hrm/radial_density_mask.frag.hlsl create mode 100644 src/hrm/reconstruction.compute.hlsl diff --git a/README.md b/README.md index 4494b09..4f88a66 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ VR Performance Toolkit RavenSystem's Fork ========================================= +[![Donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/ravensystem) + In an effort to continue this project, I have created this fork with updated components and SDKs. I added too some improvements, like HRM and dynamic modes, and other compatibility options. @@ -12,11 +14,15 @@ Included mods: * AMD FidelityFX Super Resolution * NVIDIA Image Scaling * AMD Contrast Adaptive Sharpening -* FFR: Fixed foveated rendering (render center of image at full resolution, but drop resolution towards edges) - * Variable Rate Shading (only for NVIDIA RTX / GTX 16xx cards) +* FFR: Fixed foveated rendering: render center of image at full resolution, but drop resolution towards edges + * VRS: Variable Rate Shading (only for NVIDIA RTX / GTX 16xx cards) + * RDM: Radial Density Mask (all GPUs) * HRM: Hidden radial mask: don't render pixels at the edges that are not visible in the headset. Many games already use this mask, but not all. This mod will allow you to force its usage. -* Dynamic modes for FFR and HRM to apply only to keep target FPS. +* Dynamic modes for FFR and HRM based on FPS: + * Apply only when needed. + * Change the radius dinamically. +* Several extra compatibility options to work with more games. Supported VR runtimes: diff --git a/src/config.cpp b/src/config.cpp index 532bfda..eb4489c 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -32,6 +32,8 @@ namespace vrperfkit { case UpscaleMethod::CAS: return "CAS"; } + + return "Unknown"; } FixedFoveatedMethod FFRMethodFromString(std::string s) { @@ -39,6 +41,9 @@ namespace vrperfkit { if (s == "vrs") { return FixedFoveatedMethod::VRS; } + if (s == "rdm") { + return FixedFoveatedMethod::RDM; + } LOG_INFO << "Unknown fixed foveated method " << s << ", defaulting to VRS"; return FixedFoveatedMethod::VRS; } @@ -47,7 +52,11 @@ namespace vrperfkit { switch (method) { case FixedFoveatedMethod::VRS: return "VRS"; + case FixedFoveatedMethod::RDM: + return "RDM"; } + + return "Unknown"; } GameMode GameModeFromString(std::string s) { @@ -79,6 +88,8 @@ namespace vrperfkit { case GameMode::RIGHT_EYE_FIRST: return "right"; } + + return "Unknown"; } std::string PrintToggle(bool toggle) { @@ -122,15 +133,22 @@ namespace vrperfkit { FixedFoveatedConfig &ffr = g_config.ffr; ffr.enabled = ffrCfg["enabled"].as(ffr.enabled); ffr.apply = ffr.enabled; + ffr.method = FFRMethodFromString(ffrCfg["method"].as(FFRMethodToString(ffr.method))); ffr.favorHorizontal = ffrCfg["favorHorizontal"].as(ffr.favorHorizontal); ffr.innerRadius = ffrCfg["innerRadius"].as(ffr.innerRadius); ffr.midRadius = ffrCfg["midRadius"].as(ffr.midRadius); ffr.outerRadius = ffrCfg["outerRadius"].as(ffr.outerRadius); + ffr.edgeRadius = ffrCfg["edgeRadius"].as(ffr.edgeRadius); ffr.preciseResolution = ffrCfg["preciseResolution"].as(ffr.preciseResolution); ffr.ignoreFirstTargetRenders = ffrCfg["ignoreFirstTargetRenders"].as(ffr.ignoreFirstTargetRenders); + ffr.ignoreLastTargetRenders = ffrCfg["ignoreLastTargetRenders"].as(ffr.ignoreLastTargetRenders); ffr.maxRadius = ffr.innerRadius; ffr.overrideSingleEyeOrder = ffrCfg["overrideSingleEyeOrder"].as(ffr.overrideSingleEyeOrder); ffr.fastMode = ffrCfg["fastMode"].as(ffr.fastMode); + g_config.ffrFastModeUsesHRMCount = ffrCfg["fastModeUsesHRMCount"].as(g_config.ffrFastModeUsesHRMCount); + if (!ffr.fastMode) { + g_config.ffrFastModeUsesHRMCount = false; + } ffr.dynamic = ffrCfg["dynamic"].as(ffr.dynamic); ffr.targetFrameTime = 1.f / ffrCfg["targetFPS"].as(ffr.targetFrameTime); ffr.marginFrameTime = 1.f / ffrCfg["marginFPS"].as(ffr.marginFrameTime); @@ -142,10 +160,11 @@ namespace vrperfkit { YAML::Node hiddenMaskCfg = cfg["hiddenMask"]; HiddenRadialMask &hiddenMask= g_config.hiddenMask; hiddenMask.enabled = hiddenMaskCfg["enabled"].as(hiddenMask.enabled); - hiddenMask.radius = std::max(0.f, hiddenMaskCfg["radius"].as(hiddenMask.radius)); - hiddenMask.maxRadius = hiddenMask.radius; + hiddenMask.edgeRadius = std::max(0.f, hiddenMaskCfg["edgeRadius"].as(hiddenMask.edgeRadius)); + hiddenMask.maxRadius = hiddenMask.edgeRadius; hiddenMask.preciseResolution = hiddenMaskCfg["preciseResolution"].as(hiddenMask.preciseResolution); hiddenMask.ignoreFirstTargetRenders = hiddenMaskCfg["ignoreFirstTargetRenders"].as(hiddenMask.ignoreFirstTargetRenders); + hiddenMask.ignoreLastTargetRenders = hiddenMaskCfg["ignoreLastTargetRenders"].as(hiddenMask.ignoreLastTargetRenders); hiddenMask.dynamic = hiddenMaskCfg["dynamic"].as(hiddenMask.dynamic); hiddenMask.targetFrameTime = 1.f / hiddenMaskCfg["targetFPS"].as(hiddenMask.targetFrameTime); hiddenMask.marginFrameTime = 1.f / hiddenMaskCfg["marginFPS"].as(hiddenMask.marginFrameTime); @@ -163,6 +182,31 @@ namespace vrperfkit { if (g_config.dynamicFramesCheck < 1) { g_config.dynamicFramesCheck = 1; } + + if (g_config.ffr.enabled) { + if (g_config.ffr.method == FixedFoveatedMethod::RDM) { + g_config.ffr.fastMode = false; + g_config.ffrFastModeUsesHRMCount = false; + g_config.hiddenMask.enabled = false; + + if (!g_config.upscaling.enabled) { + g_config.upscaling.enabled = true; + g_config.upscaling.radius = g_config.ffr.edgeRadius; + g_config.upscaling.method = UpscaleMethod::CAS; + g_config.upscaling.renderScale = 1.0f; + g_config.upscaling.sharpness = 0.7f; + g_config.upscaling.applyMipBias = false; + } + + } else if (g_config.ffr.method == FixedFoveatedMethod::VRS && !g_config.hiddenMask.enabled && g_config.ffrFastModeUsesHRMCount) { + g_config.hiddenMask.enabled = true; + g_config.hiddenMask.dynamic = false; + g_config.hiddenMask.edgeRadius = 1.15f; + g_config.hiddenMask.ignoreFirstTargetRenders = 0; + g_config.hiddenMask.ignoreLastTargetRenders = 0; + g_config.hiddenMask.preciseResolution = true; + } + } } catch (const YAML::Exception &e) { LOG_ERROR << "Failed to load configuration file: " << e.msg; @@ -190,12 +234,18 @@ namespace vrperfkit { LOG_INFO << " * Inner radius: " << std::setprecision(6) << g_config.ffr.innerRadius; LOG_INFO << " * Mid radius: " << std::setprecision(6) << g_config.ffr.midRadius; LOG_INFO << " * Outer radius: " << std::setprecision(6) << g_config.ffr.outerRadius; + if (g_config.ffr.method == FixedFoveatedMethod::RDM) { + LOG_INFO << " * Edge radius: " << std::setprecision(6) << g_config.ffr.edgeRadius; + } LOG_INFO << " * Precise res: " << PrintToggle(g_config.ffr.preciseResolution); - LOG_INFO << " * No renders: " << std::setprecision(6) << g_config.ffr.ignoreFirstTargetRenders; - if (!g_config.ffr.overrideSingleEyeOrder.empty()) { + LOG_INFO << " * No first rend: " << std::setprecision(6) << g_config.ffr.ignoreFirstTargetRenders; + LOG_INFO << " * No last rend: " << std::setprecision(6) << g_config.ffr.ignoreLastTargetRenders; + LOG_INFO << " * Fast mode: " << PrintToggle(g_config.ffr.fastMode); + if (g_config.ffr.fastMode) { + LOG_INFO << " * HRM counter: " << PrintToggle(g_config.ffrFastModeUsesHRMCount); + } else if (!g_config.ffr.overrideSingleEyeOrder.empty()) { LOG_INFO << " * Eye order: " << g_config.ffr.overrideSingleEyeOrder; } - LOG_INFO << " * Fast mode: " << PrintToggle(g_config.ffr.fastMode); LOG_INFO << " * Dynamic: " << PrintToggle(g_config.ffr.dynamic); if (g_config.ffr.dynamic) { LOG_INFO << " * Target FPS: " << std::setprecision(6) << (1.f / g_config.ffr.targetFrameTime); @@ -211,12 +261,15 @@ namespace vrperfkit { } } else { g_config.ffr.dynamic = false; + g_config.ffr.fastMode = false; } + LOG_INFO << " Hidden radial mask is " << PrintToggle(g_config.hiddenMask.enabled); if (g_config.hiddenMask.enabled) { - LOG_INFO << " * Radius: " << std::setprecision(6) << g_config.hiddenMask.radius; + LOG_INFO << " * Edge radius: " << std::setprecision(6) << g_config.hiddenMask.edgeRadius; LOG_INFO << " * Precise res: " << PrintToggle(g_config.hiddenMask.preciseResolution); - LOG_INFO << " * No renders: " << std::setprecision(6) << g_config.hiddenMask.ignoreFirstTargetRenders; + LOG_INFO << " * No first rend: " << std::setprecision(6) << g_config.hiddenMask.ignoreFirstTargetRenders; + LOG_INFO << " * No last rend: " << std::setprecision(6) << g_config.hiddenMask.ignoreLastTargetRenders; LOG_INFO << " * Dynamic: " << PrintToggle(g_config.hiddenMask.dynamic); if (g_config.hiddenMask.dynamic) { LOG_INFO << " * Target FPS: " << std::setprecision(6) << (1.f / g_config.hiddenMask.targetFrameTime); diff --git a/src/config.h b/src/config.h index 0e449be..e3d49e2 100644 --- a/src/config.h +++ b/src/config.h @@ -29,6 +29,7 @@ namespace vrperfkit { float innerRadius = 0.50f; float midRadius = 0.65f; float outerRadius = 0.80f; + float edgeRadius = 1.15f; bool favorHorizontal = true; std::string overrideSingleEyeOrder; bool fastMode = false; @@ -42,12 +43,13 @@ namespace vrperfkit { float increaseRadiusStep = 0.03f; bool preciseResolution = true; int ignoreFirstTargetRenders = 0; + int ignoreLastTargetRenders = 0; bool radiusChanged[2] = { true, true }; }; struct HiddenRadialMask { bool enabled = false; - float radius = 1.15f; + float edgeRadius = 1.15f; bool dynamic = false; bool dynamicChangeRadius = false; float targetFrameTime = 0.0167f; @@ -58,6 +60,7 @@ namespace vrperfkit { float increaseRadiusStep = 0.03f; bool preciseResolution = true; int ignoreFirstTargetRenders = 0; + int ignoreLastTargetRenders = 0; }; struct Config { @@ -65,7 +68,10 @@ namespace vrperfkit { DxvkConfig dxvk; GameMode gameMode = GameMode::AUTO; bool renderingSecondEye = false; - int ffrDepthClearCount = 0; + bool ffrFastModeUsesHRMCount = false; + bool ffrApplyFastMode = true; + int ffrRenderTargetCount = 0; + int ffrRenderTargetCountMax = 0; FixedFoveatedConfig ffr; HiddenRadialMask hiddenMask; bool debugMode = false; diff --git a/src/d3d11/d3d11_injector.cpp b/src/d3d11/d3d11_injector.cpp index 2f47539..ae6d089 100644 --- a/src/d3d11/d3d11_injector.cpp +++ b/src/d3d11/d3d11_injector.cpp @@ -102,28 +102,28 @@ namespace vrperfkit { context->SetPrivateData(__uuidof(D3D11Injector), size, &instance); // Upscaling and FFR - if (g_config.upscaling.enabled || g_config.ffr.enabled) { + if (g_config.upscaling.enabled || (g_config.ffr.enabled && g_config.ffr.method == FixedFoveatedMethod::VRS)) { hooks::InstallVirtualFunctionHook("ID3D11DeviceContext::PSSetSamplers", context.Get(), 10, (void*)&D3D11ContextHook_PSSetSamplers); hooks::InstallVirtualFunctionHook("ID3D11DeviceContext::OMSetRenderTargets", context.Get(), 33, (void*)&D3D11ContextHook_OMSetRenderTargets); hooks::InstallVirtualFunctionHook("ID3D11DeviceContext::OMSetRenderTargetsAndUnorderedAccessViews", context.Get(), 34, (void*)&D3D11ContextHook_OMSetRenderTargetsAndUnorderedAccessViews); } // HRM - if (g_config.hiddenMask.enabled) { + if (g_config.hiddenMask.enabled || (g_config.ffr.enabled && g_config.ffr.method == FixedFoveatedMethod::RDM)) { hooks::InstallVirtualFunctionHook("ID3D11DeviceContext::ClearDepthStencilView", context.Get(), 53, (void*)&D3D11ContextHook_ClearDepthStencilView); } } D3D11Injector::~D3D11Injector() { // Upscaling && FFR - if (g_config.upscaling.enabled || g_config.ffr.enabled) { + if (g_config.upscaling.enabled || (g_config.ffr.enabled && g_config.ffr.method == FixedFoveatedMethod::VRS)) { hooks::RemoveHook((void*)&D3D11ContextHook_PSSetSamplers); hooks::RemoveHook((void*)&D3D11ContextHook_OMSetRenderTargets); hooks::RemoveHook((void*)&D3D11ContextHook_OMSetRenderTargetsAndUnorderedAccessViews); } // HRM - if (g_config.hiddenMask.enabled) { + if (g_config.hiddenMask.enabled || (g_config.ffr.enabled && g_config.ffr.method == FixedFoveatedMethod::RDM)) { hooks::RemoveHook((void*)&D3D11ContextHook_ClearDepthStencilView); } diff --git a/src/d3d11/d3d11_post_processor.cpp b/src/d3d11/d3d11_post_processor.cpp index 9809c09..16eda34 100644 --- a/src/d3d11/d3d11_post_processor.cpp +++ b/src/d3d11/d3d11_post_processor.cpp @@ -9,14 +9,32 @@ #include "shader_hrm_fullscreen_tri.h" #include "shader_hrm_mask.h" +#include "shader_rdm_mask.h" +#include "shader_rdm_reconstruction.h" #include namespace vrperfkit { D3D11PostProcessor::D3D11PostProcessor(ComPtr device) : device(device) { enableDynamic = g_config.hiddenMask.dynamic || g_config.ffr.dynamic; - hiddenMaskApply = g_config.hiddenMask.enabled; + + is_rdm = (g_config.ffr.enabled && g_config.ffr.method == FixedFoveatedMethod::RDM); + if (is_rdm) { + hiddenMaskApply = g_config.ffr.enabled; + preciseResolution = g_config.ffr.preciseResolution; + ignoreFirstTargetRenders = g_config.ffr.ignoreFirstTargetRenders; + ignoreLastTargetRenders = g_config.ffr.ignoreLastTargetRenders; + edgeRadius = g_config.ffr.edgeRadius; + } else { + hiddenMaskApply = g_config.hiddenMask.enabled; + preciseResolution = g_config.hiddenMask.preciseResolution; + ignoreFirstTargetRenders = g_config.hiddenMask.ignoreFirstTargetRenders; + ignoreLastTargetRenders = g_config.hiddenMask.ignoreLastTargetRenders; + edgeRadius = g_config.hiddenMask.edgeRadius; + } + device->GetImmediateContext(context.GetAddressOf()); + LOG_INFO << "Init PostProcessor"; } HRESULT D3D11PostProcessor::ClearDepthStencilView(ID3D11DepthStencilView *pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) { @@ -33,7 +51,7 @@ namespace vrperfkit { D3D11_TEXTURE2D_DESC texDesc; ((ID3D11Texture2D*)resource.Get())->GetDesc(&texDesc); - if (g_config.hiddenMask.preciseResolution) { + if (preciseResolution) { if (texDesc.Width != textureWidth || texDesc.Height != textureHeight) { return 0; } @@ -43,8 +61,8 @@ namespace vrperfkit { return 0; } - ApplyHiddenRadialMask((ID3D11Texture2D*)resource.Get(), Depth, Stencil); - + ApplyRadialDensityMask((ID3D11Texture2D*)resource.Get(), Depth, Stencil); + return 0; } @@ -58,6 +76,15 @@ namespace vrperfkit { float _padding; }; + struct RdmReconstructConstants { + int offset[2]; + float projectionCenter[2]; + float invClusterResolution[2]; + float invResolution[2]; + float radius[3]; + float edgeRadius; + }; + DXGI_FORMAT TranslateTypelessDepthFormats(DXGI_FORMAT format) { switch (format) { case DXGI_FORMAT_R16_TYPELESS: @@ -177,8 +204,39 @@ namespace vrperfkit { } void D3D11PostProcessor::PrepareRdmResources(DXGI_FORMAT format) { - CheckResult("Creating HRM fullscreen tri vertex shader", device->CreateVertexShader( g_HRM_FullscreenTriShader, sizeof( g_HRM_FullscreenTriShader ), nullptr, hrmFullTriVertexShader.GetAddressOf() )); - CheckResult("Creating HRM masking shader", device->CreatePixelShader( g_HRM_MaskShader, sizeof( g_HRM_MaskShader ), nullptr, hrmMaskingShader.GetAddressOf() )); + CheckResult("Creating HRM/RDM fullscreen tri vertex shader", device->CreateVertexShader( g_HRM_FullscreenTriShader, sizeof( g_HRM_FullscreenTriShader ), nullptr, hrmFullTriVertexShader.GetAddressOf() )); + if (is_rdm) { + CheckResult("Creating RDM masking shader", device->CreatePixelShader( g_RDM_MaskShader, sizeof( g_RDM_MaskShader ), nullptr, rdmMaskingShader.GetAddressOf() )); + CheckResult("Creating RDM reconstruction shader", device->CreateComputeShader( g_RDM_ReconstructionShader, sizeof( g_RDM_ReconstructionShader ), nullptr, rdmReconstructShader.GetAddressOf() )); + + D3D11_TEXTURE2D_DESC td; + td.Width = textureWidth; + td.Height = textureHeight; + td.MipLevels = 1; + td.CPUAccessFlags = 0; + td.Usage = D3D11_USAGE_DEFAULT; + td.BindFlags = D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE; + td.Format = format; + td.MiscFlags = 0; + td.SampleDesc.Count = 1; + td.SampleDesc.Quality = 0; + td.ArraySize = 1; + CheckResult("Creating RDM reconstructed texture", device->CreateTexture2D( &td, nullptr, rdmReconstructedTexture.GetAddressOf() )); + D3D11_UNORDERED_ACCESS_VIEW_DESC uav; + uav.Format = td.Format; + uav.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; + uav.Texture2D.MipSlice = 0; + CheckResult("Creating RDM reconstructed UAV", device->CreateUnorderedAccessView( rdmReconstructedTexture.Get(), &uav, rdmReconstructedUav.GetAddressOf() )); + D3D11_SHADER_RESOURCE_VIEW_DESC svd; + svd.Format = TranslateTypelessFormats(format); + svd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + svd.Texture2D.MostDetailedMip = 0; + svd.Texture2D.MipLevels = 1; + CheckResult("Creating RDM reconstructed view", device->CreateShaderResourceView( rdmReconstructedTexture.Get(), &svd, rdmReconstructedView.GetAddressOf() )); + + } else { + CheckResult("Creating HRM masking shader", device->CreatePixelShader( g_HRM_MaskShader, sizeof( g_HRM_MaskShader ), nullptr, hrmMaskingShader.GetAddressOf() )); + } D3D11_DEPTH_STENCIL_DESC dsd; dsd.DepthEnable = TRUE; @@ -219,6 +277,12 @@ namespace vrperfkit { bd.ByteWidth = sizeof(RdmMaskingConstants); CheckResult("Creating HRM masking constants buffer", device->CreateBuffer( &bd, nullptr, hrmMaskingConstantsBuffer[0].GetAddressOf() )); CheckResult("Creating HRM masking constants buffer", device->CreateBuffer( &bd, nullptr, hrmMaskingConstantsBuffer[1].GetAddressOf() )); + + if (is_rdm) { + bd.ByteWidth = sizeof(RdmReconstructConstants); + CheckResult("Creating RDM reconstruct constants buffer", device->CreateBuffer( &bd, nullptr, rdmReconstructConstantsBuffer[0].GetAddressOf() )); + CheckResult("Creating RDM reconstruct constants buffer", device->CreateBuffer( &bd, nullptr, rdmReconstructConstantsBuffer[1].GetAddressOf() )); + } } bool D3D11PostProcessor::HasBlacklistedTextureName(ID3D11Texture2D *tex) { @@ -277,7 +341,7 @@ namespace vrperfkit { return depthStencilViews[depthStencilTex].view[eye].Get(); } - void D3D11PostProcessor::ApplyHiddenRadialMask(ID3D11Texture2D *depthStencilTex, float depth, uint8_t stencil) { + void D3D11PostProcessor::ApplyRadialDensityMask(ID3D11Texture2D *depthStencilTex, float depth, uint8_t stencil) { if (HasBlacklistedTextureName(depthStencilTex)) { return; } @@ -290,10 +354,17 @@ namespace vrperfkit { vr::EVREye currentEye = vr::Eye_Left; - if (depthClearCount <= g_config.hiddenMask.ignoreFirstTargetRenders) { + if (depthClearCount <= ignoreFirstTargetRenders || (ignoreLastTargetRenders > 0 && depthClearCount > depthClearCountMax - ignoreLastTargetRenders)) { + if (g_config.ffrFastModeUsesHRMCount) { + g_config.ffrApplyFastMode = false; + } return; } + if (g_config.ffrFastModeUsesHRMCount) { + g_config.ffrApplyFastMode = true; + } + if (g_config.gameMode == GameMode::LEFT_EYE_FIRST) { if (g_config.renderingSecondEye) { currentEye = vr::Eye_Right; @@ -355,7 +426,11 @@ namespace vrperfkit { context->PSGetConstantBuffers( 0, 1, psConstantBuffer.GetAddressOf() ); context->VSSetShader( hrmFullTriVertexShader.Get(), nullptr, 0 ); - context->PSSetShader( hrmMaskingShader.Get(), nullptr, 0 ); + if (is_rdm) { + context->PSSetShader( rdmMaskingShader.Get(), nullptr, 0 ); + } else { + context->PSSetShader( hrmMaskingShader.Get(), nullptr, 0 ); + } context->IASetInputLayout( nullptr ); context->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); context->IASetVertexBuffers( 0, 0, nullptr, nullptr, nullptr ); @@ -366,10 +441,12 @@ namespace vrperfkit { RdmMaskingConstants constants; constants.depthOut = 1.f - depth; - //constants.radius[0] = g_config.hiddenMask.radius; - //constants.radius[1] = g_config.hiddenMask.radius; - //constants.radius[2] = g_config.hiddenMask.radius; - constants.edgeRadius = g_config.hiddenMask.radius; + if (is_rdm) { + constants.radius[0] = g_config.ffr.innerRadius; + constants.radius[1] = g_config.ffr.midRadius; + constants.radius[2] = g_config.ffr.outerRadius; + } + constants.edgeRadius = edgeRadius; constants.invClusterResolution[0] = 8.f / renderWidth; constants.invClusterResolution[1] = 8.f / renderHeight; constants.projectionCenter[0] = projX[currentEye]; @@ -438,6 +515,40 @@ namespace vrperfkit { } + void D3D11PostProcessor::ReconstructRdmRender(const D3D11PostProcessInput &input) { + context->CSSetShader( rdmReconstructShader.Get(), nullptr, 0 ); + ID3D11Buffer *emptyBind[] = {nullptr}; + context->CSSetConstantBuffers( 0, 1, emptyBind ); + + RdmReconstructConstants constants; + constants.offset[0] = input.inputViewport.x; + constants.offset[1] = input.inputViewport.y; + constants.projectionCenter[0] = projX[input.eye]; + constants.projectionCenter[1] = projY[input.eye]; + constants.invResolution[0] = 1.f / textureWidth; + constants.invResolution[1] = 1.f / textureHeight; + constants.invClusterResolution[0] = 8.f / input.inputViewport.width; + constants.invClusterResolution[1] = 8.f / input.inputViewport.height; + constants.radius[0] = g_config.ffr.innerRadius; + constants.radius[1] = g_config.ffr.midRadius; + constants.radius[2] = g_config.ffr.outerRadius; + constants.edgeRadius = edgeRadius; + if (g_config.gameMode == GameMode::GENERIC_SINGLE && input.eye == vr::Eye_Right) { + constants.projectionCenter[0] += 1.f; + } + D3D11_MAPPED_SUBRESOURCE mapped { nullptr, 0, 0 }; + context->Map( rdmReconstructConstantsBuffer[input.eye].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped ); + memcpy(mapped.pData, &constants, sizeof(constants)); + context->Unmap( rdmReconstructConstantsBuffer[input.eye].Get(), 0 ); + UINT uavCount = -1; + context->CSSetUnorderedAccessViews( 0, 1, rdmReconstructedUav.GetAddressOf(), &uavCount ); + context->CSSetConstantBuffers( 0, 1, rdmReconstructConstantsBuffer[input.eye].GetAddressOf() ); + ID3D11ShaderResourceView *srvs[1] = {input.inputView}; + context->CSSetShaderResources( 0, 1, srvs ); + context->CSSetSamplers(0, 1, sampler.GetAddressOf()); + context->Dispatch( (input.inputViewport.width + 7) / 8, (input.inputViewport.height + 7) / 8, 1 ); + } + bool D3D11PostProcessor::Apply(const D3D11PostProcessInput &input, Viewport &outputViewport) { bool didPostprocessing = false; /* @@ -446,11 +557,7 @@ namespace vrperfkit { } */ - g_config.renderingSecondEye = !g_config.renderingSecondEye; - g_config.ffrDepthClearCount = 0; - depthClearCount = 0; - - if (g_config.hiddenMask.enabled) { + if (g_config.hiddenMask.enabled || is_rdm) { if (!hrmInitialized) { try { PrepareResources(input.inputTexture); @@ -460,7 +567,7 @@ namespace vrperfkit { } } } - + if (g_config.upscaling.enabled) { try { D3D11State previousState; @@ -481,6 +588,12 @@ namespace vrperfkit { outputViewport.x += outputViewport.width; } } + + if (is_rdm) { + ReconstructRdmRender(input); + context->CopyResource(input.inputTexture, rdmReconstructedTexture.Get()); + } + upscaler->Upscale(input, outputViewport); float newLodBias = -log2f(outputViewport.width / (float)input.inputViewport.width); @@ -501,6 +614,12 @@ namespace vrperfkit { } } + g_config.renderingSecondEye = !g_config.renderingSecondEye; + g_config.ffrRenderTargetCountMax = g_config.ffrRenderTargetCount; + g_config.ffrRenderTargetCount = 0; + depthClearCountMax = depthClearCount; + depthClearCount = 0; + if (enableDynamic && (g_config.renderingSecondEye || g_config.gameMode == GameMode::GENERIC_SINGLE)) { EndDynamicProfiling(); } @@ -625,16 +744,16 @@ namespace vrperfkit { if (g_config.hiddenMask.dynamic) { if (frameTime > g_config.hiddenMask.targetFrameTime) { if (g_config.hiddenMask.dynamicChangeRadius) { - if ((g_config.hiddenMask.radius - g_config.hiddenMask.decreaseRadiusStep) >= g_config.hiddenMask.minRadius) { - g_config.hiddenMask.radius -= g_config.hiddenMask.decreaseRadiusStep; + if ((edgeRadius - g_config.hiddenMask.decreaseRadiusStep) >= g_config.hiddenMask.minRadius) { + edgeRadius -= g_config.hiddenMask.decreaseRadiusStep; } } else { hiddenMaskApply = true; } } else if (frameTime < g_config.hiddenMask.marginFrameTime) { if (g_config.hiddenMask.dynamicChangeRadius) { - if ((g_config.hiddenMask.radius + g_config.hiddenMask.increaseRadiusStep) <= g_config.hiddenMask.maxRadius) { - g_config.hiddenMask.radius += g_config.hiddenMask.increaseRadiusStep; + if ((edgeRadius + g_config.hiddenMask.increaseRadiusStep) <= g_config.hiddenMask.maxRadius) { + edgeRadius += g_config.hiddenMask.increaseRadiusStep; } } else { hiddenMaskApply = false; diff --git a/src/d3d11/d3d11_post_processor.h b/src/d3d11/d3d11_post_processor.h index 1144179..0b8fdfb 100644 --- a/src/d3d11/d3d11_post_processor.h +++ b/src/d3d11/d3d11_post_processor.h @@ -66,6 +66,10 @@ namespace vrperfkit { bool is_DynamicProfiling = false; bool enableDynamic = false; bool hiddenMaskApply = false; + bool is_rdm = false; + bool preciseResolution = false; + int ignoreFirstTargetRenders = 0; + int ignoreLastTargetRenders = 0; void CreateDynamicProfileQueries(); void StartDynamicProfiling(); @@ -81,12 +85,20 @@ namespace vrperfkit { bool inputIsSrgb = false; ComPtr hrmFullTriVertexShader; ComPtr hrmMaskingShader; + ComPtr rdmMaskingShader; + ComPtr rdmReconstructShader; ComPtr hrmMaskingConstantsBuffer[2]; + ComPtr rdmReconstructConstantsBuffer[2]; + ComPtr rdmReconstructedTexture; + ComPtr rdmReconstructedUav; + ComPtr rdmReconstructedView; ComPtr hrmDepthStencilState; ComPtr hrmRasterizerState; float projX[2]; float projY[2]; int depthClearCount = 0; + int depthClearCountMax = 0; + float edgeRadius = 1.15f; struct DepthStencilViews { ComPtr view[2]; @@ -98,7 +110,8 @@ namespace vrperfkit { void D3D11PostProcessor::PrepareResources(ID3D11Texture2D *inputTexture); void D3D11PostProcessor::PrepareCopyResources(DXGI_FORMAT format); void D3D11PostProcessor::PrepareRdmResources(DXGI_FORMAT format); - void ApplyHiddenRadialMask(ID3D11Texture2D *depthStencilTex, float depth, uint8_t stencil); + void D3D11PostProcessor::ApplyRadialDensityMask(ID3D11Texture2D *depthStencilTex, float depth, uint8_t stencil); + void D3D11PostProcessor::ReconstructRdmRender(const D3D11PostProcessInput &input); /* struct ProfileQuery { diff --git a/src/d3d11/d3d11_variable_rate_shading.cpp b/src/d3d11/d3d11_variable_rate_shading.cpp index d63098a..93b1f84 100644 --- a/src/d3d11/d3d11_variable_rate_shading.cpp +++ b/src/d3d11/d3d11_variable_rate_shading.cpp @@ -62,7 +62,12 @@ namespace vrperfkit { D3D11VariableRateShading::D3D11VariableRateShading(ComPtr device) { active = false; - LOG_INFO << "Trying to load NVAPI..."; + + if (!g_config.ffr.enabled || g_config.ffr.method == FixedFoveatedMethod::RDM) { + return; + } + + LOG_INFO << "Loading NVAPI for VRS..."; if (!nvapiLoaded) { NvAPI_Status result = NvAPI_Initialize(); @@ -83,17 +88,21 @@ namespace vrperfkit { this->device = device; device->GetImmediateContext(context.GetAddressOf()); active = true; + ignoreFirstTargetRenders = g_config.ffr.ignoreFirstTargetRenders; + ignoreLastTargetRenders = g_config.ffr.ignoreLastTargetRenders; LOG_INFO << "Successfully initialized NVAPI; Variable Rate Shading is available."; } void D3D11VariableRateShading::UpdateTargetInformation(int targetWidth, int targetHeight, TextureMode mode, float leftProjX, float leftProjY, float rightProjX, float rightProjY) { - this->targetWidth = targetWidth; - this->targetHeight = targetHeight; - this->targetMode = mode; - proj[0][0] = leftProjX; - proj[0][1] = leftProjY; - proj[1][0] = rightProjX; - proj[1][1] = rightProjY; + if (nvapiLoaded) { + this->targetWidth = targetWidth; + this->targetHeight = targetHeight; + this->targetMode = mode; + proj[0][0] = leftProjX; + proj[0][1] = leftProjY; + proj[1][0] = rightProjX; + proj[1][1] = rightProjY; + } } void D3D11VariableRateShading::EndFrame() { @@ -128,14 +137,7 @@ namespace vrperfkit { void D3D11VariableRateShading::PostOMSetRenderTargets(UINT numViews, ID3D11RenderTargetView * const *renderTargetViews, ID3D11DepthStencilView *depthStencilView) { - if (!active || numViews == 0 || renderTargetViews == nullptr || renderTargetViews[0] == nullptr || !g_config.ffr.apply) { - DisableVRS(); - return; - } - - ++g_config.ffrDepthClearCount; - - if (g_config.ffrDepthClearCount <= g_config.ffr.ignoreFirstTargetRenders) { + if (!active || numViews == 0 || renderTargetViews == nullptr || renderTargetViews[0] == nullptr || !g_config.ffr.apply || !g_config.ffrApplyFastMode) { DisableVRS(); return; } @@ -159,6 +161,15 @@ namespace vrperfkit { return; } + if (!g_config.ffrFastModeUsesHRMCount) { + ++g_config.ffrRenderTargetCount; + + if (g_config.ffrRenderTargetCount < ignoreFirstTargetRenders || (ignoreLastTargetRenders > 0 && g_config.ffrRenderTargetCount > g_config.ffrRenderTargetCountMax - ignoreLastTargetRenders)) { + DisableVRS(); + return; + } + } + if (g_config.ffr.fastMode) { if (ResolutionMatches(td.Width, targetWidth) && ResolutionMatches(td.Height, targetHeight)) { if (g_config.gameMode == GameMode::RIGHT_EYE_FIRST) { @@ -176,6 +187,7 @@ namespace vrperfkit { } } else { DisableVRS(); + return; } } else if (targetMode == TextureMode::SINGLE && ResolutionMatches(td.Width, 2 * targetWidth) && ResolutionMatches(td.Height, targetHeight)) { @@ -201,15 +213,18 @@ namespace vrperfkit { break; default: DisableVRS(); + return; } } else { LOG_DEBUG << "VRS: Single eye target, don't know which eye"; DisableVRS(); + return; } ++currentSingleEyeRT; } else { DisableVRS(); + return; } } diff --git a/src/d3d11/d3d11_variable_rate_shading.h b/src/d3d11/d3d11_variable_rate_shading.h index 4412037..a46b6df 100644 --- a/src/d3d11/d3d11_variable_rate_shading.h +++ b/src/d3d11/d3d11_variable_rate_shading.h @@ -23,6 +23,9 @@ namespace vrperfkit { bool nvapiLoaded = false; bool active = false; + int ignoreFirstTargetRenders = 0; + int ignoreLastTargetRenders = 0; + int targetWidth = 1000000; int targetHeight = 1000000; TextureMode targetMode = TextureMode::SINGLE; diff --git a/src/dllmain.cpp b/src/dllmain.cpp index 2117e62..60fa809 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -88,7 +88,7 @@ namespace { vrperfkit::OpenLogFile(vrperfkit::g_basePath / "vrperfkit_RSF.log"); LOG_INFO << "==============================="; - LOG_INFO << "VR Performance Toolkit RSF v2.0"; + LOG_INFO << "VR Performance Toolkit RSF v3.0"; LOG_INFO << "===============================\n"; vrperfkit::LoadConfig(vrperfkit::g_basePath / "vrperfkit_RSF.yml"); diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp index 7deed78..9efc8c2 100644 --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -66,16 +66,6 @@ namespace { LOG_INFO << "Fixed foveated now favors " << (g_config.ffr.favorHorizontal ? "horizontal" : "vertical") << " resolution"; } - void IncreaseHiddenMaskRadius() { - g_config.hiddenMask.radius += 0.05f; - LOG_INFO << "New hidden mask radius: " << g_config.hiddenMask.radius; - } - - void DecreaseHiddenMaskRadius() { - g_config.hiddenMask.radius = std::max(0.f, g_config.hiddenMask.radius - 0.05f); - LOG_INFO << "New hidden mask radius: " << g_config.hiddenMask.radius; - } - struct HotkeyDefinition { std::string name; std::function action; @@ -93,8 +83,6 @@ namespace { {"toggleUpscalingApplyMipBias", ToggleUpscalingApplyMipBias}, {"toggleFixedFoveated", ToggleFixedFoveated}, {"toggleFFRFavorHorizontal", ToggleFFRFavorHorizontal}, - {"increaseHiddenMaskRadius", IncreaseHiddenMaskRadius}, - {"decreaseHiddenMaskRadius", DecreaseHiddenMaskRadius}, }; } diff --git a/src/hrm/hidden_radial_mask.hlsl b/src/hrm/hidden_radial_mask.hlsl index 4fad1d7..a4929fa 100644 --- a/src/hrm/hidden_radial_mask.hlsl +++ b/src/hrm/hidden_radial_mask.hlsl @@ -11,7 +11,6 @@ cbuffer cb : register(b0) { float4 main(float4 position : SV_POSITION) : SV_TARGET { // working in blocks of 8x8 pixels float2 pos = float2(position.x, position.y * yFix.x + yFix.y); - //float2 toCenter = trunc(pos.xy * 0.125f) * invClusterResolution.xy - projectionCenter; float2 toCenter = pos.xy * 0.125f * invClusterResolution.xy - projectionCenter; float distToCenter = length(toCenter) * 2; diff --git a/src/hrm/radial_density_mask.frag.hlsl b/src/hrm/radial_density_mask.frag.hlsl new file mode 100644 index 0000000..cce879e --- /dev/null +++ b/src/hrm/radial_density_mask.frag.hlsl @@ -0,0 +1,29 @@ +cbuffer cb : register(b0) { + float depthOut; + float3 radius; + float2 invClusterResolution; + float2 projectionCenter; + float2 yFix; + float edgeRadius; + float _padding; +}; + +float4 main(float4 position : SV_POSITION) : SV_TARGET { + // working in blocks of 8x8 pixels + float2 pos = float2(position.x, position.y * yFix.x + yFix.y); + float2 toCenter = trunc(pos.xy * 0.125f) * invClusterResolution.xy - projectionCenter; + float distToCenter = length(toCenter) * 2; + + uint2 iFragCoordHalf = uint2( pos.xy * 0.5f ); + + if( distToCenter < radius.x ) + discard; + if( (iFragCoordHalf.x & 0x01u) == (iFragCoordHalf.y & 0x01u) && distToCenter < radius.y ) + discard; + if( !((iFragCoordHalf.x & 0x01u) != 0u || (iFragCoordHalf.y & 0x01u) != 0u) && distToCenter < radius.z ) + discard; + if( !((iFragCoordHalf.x & 0x03u) != 0u || (iFragCoordHalf.y & 0x03u) != 0u) && distToCenter < edgeRadius ) + discard; + + return float4(0, 0, 0, 0); +} diff --git a/src/hrm/reconstruction.compute.hlsl b/src/hrm/reconstruction.compute.hlsl new file mode 100644 index 0000000..6956e51 --- /dev/null +++ b/src/hrm/reconstruction.compute.hlsl @@ -0,0 +1,205 @@ +/** + * Adapted from Ogre: https://github.com/OGRECave/ogre-next under the MIT license + */ + +Texture2D u_srcTex : register(t0); +SamplerState bilinearSampler : register(s0); + +RWTexture2D u_dstTex : register(u0); + +cbuffer cb : register(b0) { + uint2 u_offset; + float2 u_projectionCenter; + float2 u_invClusterResolution; + float2 u_invResolution; + float3 u_radius; + float edgeRadius; +}; + +// FIXME: AMD/NVIDIA extensions? +#define anyInvocationARB(value) (value) +#define imageStore(outImage, iuv, value) outImage[uint2(iuv)] = value +#define texelFetch(srcImage, iuv, lod) srcImage.Load(int3(iuv, lod)) +#define textureLod(srcTex, uv, lod) srcTex.SampleLevel(bilinearSampler, uv, lod) + +/** Takes the pattern (low quality): + ab xx ef xx + cd xx gh xx + xx ij xx mn + xx kl xx op + And outputs: + ab ab ef ef + cd cd gh gh + ij ij mn mn + kl kl op op +*/ +void reconstructHalfResLow( int2 dstUV, uint2 uFragCoordHalf ) +{ + int2 offset; + if( (uFragCoordHalf.x & 0x01u) != (uFragCoordHalf.y & 0x01u) ) + offset.x = (uFragCoordHalf.y & 0x01u) == 0 ? -2 : 2; + else + offset.x = 0; + offset.y = 0; + + int2 uv = dstUV + offset; + float4 srcVal = texelFetch( u_srcTex, uv.xy, 0 ); + + imageStore( u_dstTex, dstUV, srcVal ); +} + +/* Uses Valve's Alex Vlachos Advanced VR Rendering Performance technique + (bilinear approximation) GDC 2016 +*/ +void reconstructHalfResHigh( int2 dstUV, uint2 uFragCoordHalf ) +{ + if( (uFragCoordHalf.x & 0x01u) != (uFragCoordHalf.y & 0x01u) ) + { + float2 offset0; + float2 offset1; + offset0.x = (dstUV.x & 0x01) == 0 ? -0.5f : 1.5f; + offset0.y = (dstUV.y & 0x01) == 0 ? 0.75f : 0.25f; + + offset1.x = (dstUV.x & 0x01) == 0 ? 0.75f : 0.25f; + offset1.y = (dstUV.y & 0x01) == 0 ? -0.5f : 1.5f; + + float2 offset0N = offset0; + offset0N.x = (dstUV.x & 0x01) == 0 ? 2.5f : -1.5f; + float2 offset1N = offset1; + offset1N.y = (dstUV.y & 0x01) == 0 ? 2.5f : -1.5f; + + float2 uv0 = ( float2( dstUV ) + offset0 ) * u_invResolution; + float4 srcVal0 = textureLod( u_srcTex, uv0.xy, 0 ); + float2 uv1 = ( float2( dstUV ) + offset1 ) * u_invResolution; + float4 srcVal1 = textureLod( u_srcTex, uv1.xy, 0 ); + float2 uv0N = ( float2( dstUV ) + offset0N ) * u_invResolution; + float4 srcVal0N = textureLod( u_srcTex, uv0N.xy, 0 ); + float2 uv1N = ( float2( dstUV ) + offset1N ) * u_invResolution; + float4 srcVal1N = textureLod( u_srcTex, uv1N.xy, 0 ); + + float4 finalVal = srcVal0 * 0.375f + srcVal1 * 0.375f + srcVal0N * 0.125f + srcVal1N * 0.125f; + imageStore( u_dstTex, dstUV, finalVal ); + } + else + { + float2 uv = float2( dstUV ); + uv.x += (dstUV.x & 0x01) == 0 ? 0.75f : 0.25f; + uv.y += (dstUV.y & 0x01) == 0 ? 0.75f : 0.25f; + uv.xy *= u_invResolution; + float4 srcVal = textureLod( u_srcTex, uv.xy, 0 ); + + int2 uv0 = int2( uFragCoordHalf << 1u ); + float4 srcTL = texelFetch( u_srcTex, uv0 + int2( -1, -1 ), 0 ); + float4 srcTR = texelFetch( u_srcTex, uv0 + int2( 2, -1 ), 0 ); + float4 srcBL = texelFetch( u_srcTex, uv0 + int2( -1, 2 ), 0 ); + float4 srcBR = texelFetch( u_srcTex, uv0 + int2( 2, 2 ), 0 ); + + float weights[4] = { 0.28125f, 0.09375f, 0.09375f, 0.03125f }; + + int idx = (dstUV.x & 0x01) + ((dstUV.y & 0x01) << 1u); + + float4 finalVal = srcVal * 0.5f + + srcTL * weights[(idx + 0)] + + srcTR * weights[(idx + 1) & 0x03] + + srcBL * weights[(idx + 2) & 0x03] + + srcBR * weights[(idx + 3) & 0x03]; + + imageStore( u_dstTex, dstUV, finalVal ); + } +} + +/** Takes the pattern: + a b x x + c d x x + x x x x + x x x x + And outputs: + a b a b + c d c d + a b a b + c d c d +*/ +void reconstructQuarterRes( int2 dstUV, uint2 uFragCoordHalf ) +{ + int2 offset; + offset.x = (uFragCoordHalf.x & 0x01u) == 0 ? 0 : -2; + offset.y = (uFragCoordHalf.y & 0x01u) == 0 ? 0 : -2; + + int2 uv = int2( int2( dstUV ) + offset ); + float4 srcVal = texelFetch( u_srcTex, uv.xy, 0 ); + + imageStore( u_dstTex, dstUV, srcVal ); +} + +/** Same as reconstructQuarterRes, but a lot more samples to repeat: + a b x x x x x x + c d x x x x x x + x x x x x x x x + x x x x x x x x + x x x x x x x x + x x x x x x x x + x x x x x x x x + x x x x x x x x + And outputs: + a b a b a b a b + c d c d c d c d + a b a b a b a b + c d c d c d c d + a b a b a b a b + c d c d c d c d + a b a b a b a b + c d c d c d c d +*/ +void reconstructSixteenthRes( int2 dstUV, uint2 uFragCoordHalf ) +{ + int2 block = int2( uFragCoordHalf ) & 0x03; + + int2 offset; + offset.x = block.x * -2; + offset.y = block.y * -2; + + int2 uv = int2( int2( dstUV ) + offset ); + float4 srcVal = texelFetch( u_srcTex, uv.xy, 0 ); + + imageStore( u_dstTex, dstUV, srcVal ); +} + +[numthreads(8, 8, 1)] +void main(uint3 globalInvocationID : SV_DispatchThreadID) { + uint2 currentUV = uint2(globalInvocationID.xy) + u_offset; + uint2 uFragCoordHalf = uint2(currentUV >> 1u); + + //We must work in blocks so the reconstruction filter can work properly + float2 toCenter = (currentUV >> 3u) * u_invClusterResolution - u_projectionCenter; + float distToCenter = 2 * length(toCenter); + + //We know for a fact distToCenter is in blocks of 8x8 + if( anyInvocationARB( distToCenter >= u_radius.x ) && anyInvocationARB( distToCenter <= edgeRadius ) ) + { + if( anyInvocationARB( distToCenter < u_radius.y ) ) + { + if( anyInvocationARB( distToCenter + 2 * u_invClusterResolution.x < u_radius.y ) ) + { + reconstructHalfResHigh( int2(currentUV), uFragCoordHalf ); + } + else + { + //Right next to the border with lower res rendering. + //We can't use anything else than low quality filter + reconstructHalfResLow( int2( currentUV ), uFragCoordHalf ); + } + } + else if( anyInvocationARB( distToCenter < u_radius.z ) ) + { + reconstructQuarterRes( int2( currentUV ), uFragCoordHalf ); + } + else + { + reconstructSixteenthRes( int2( currentUV ), uFragCoordHalf ); + } + } + else + { + imageStore( u_dstTex, int2(currentUV), texelFetch( u_srcTex, int2( currentUV ), 0 ) ); + } +} diff --git a/src/openvr/openvr_manager.cpp b/src/openvr/openvr_manager.cpp index c2153c8..b5c00e7 100644 --- a/src/openvr/openvr_manager.cpp +++ b/src/openvr/openvr_manager.cpp @@ -424,6 +424,7 @@ namespace vrperfkit { info.bounds = &outputBounds; PrepareOutputTexInfo(info.texture, info.submitFlags); + outputTexInfo->handle = d3d11Res->outputTexture.Get(); outputTexInfo->eColorSpace = inputIsSrgb ? ColorSpace_Gamma : ColorSpace_Auto; info.texture = outputTexInfo.get(); diff --git a/src/types.h b/src/types.h index a1b3701..1605159 100644 --- a/src/types.h +++ b/src/types.h @@ -56,6 +56,7 @@ namespace vrperfkit { enum class FixedFoveatedMethod { VRS, + RDM, }; FixedFoveatedMethod FFRMethodFromString(std::string s); std::string FFRMethodToString(FixedFoveatedMethod method); diff --git a/vrperfkit_RSF.yml b/vrperfkit_RSF.yml index e80212d..a75ad1d 100644 --- a/vrperfkit_RSF.yml +++ b/vrperfkit_RSF.yml @@ -22,6 +22,9 @@ upscaling: # Control how much the render resolution is lowered. The renderScale is the percentage # used to scale resolution used by the GPU to render. For example, a value of 50 (50%) # means GPU will use half of pixels. This is same from how render scale works in SteamVR. + # If you set a value higher than 100 for renderScale, then the game will render at the native + # resolution, i.e. the one configured in SteamVR. But FSR/NIS/CAS will then take this render and + # upscale it to a resolution increased by the value of renderScale percentage. renderScale: 70 # Configure how much the image is sharpened during upscaling. @@ -54,11 +57,13 @@ upscaling: # The third ring reaches from midRadius to outerRadius and is rendered at 1/4th resolution. # The final fourth ring reaches from outerRadius to the edges of the image and is rendered # at 1/16th resolution. -# Fixed foveated rendering is achieved with Variable Rate Shading. This technique is only -# available on NVIDIA RTX and GTX 16xx cards. fixedFoveated: # Enable (true) or disable (false) fixed foveated rendering - enabled: true + enabled: false + # Method + # - vrs (Variable Rate Shading: This is only available on NVIDIA RTX and GTX 16xx cards) + # - rdm (Radial Density Mask: Compatible with any GPU. Hidden Mask will be disabled and Upscaling wil be enabled) + method: vrs # Dynamic: FFR is applied only when needed to try to maintain at least target FPS dynamic: false @@ -73,7 +78,7 @@ fixedFoveated: # Decreased radius amount applied for each frametime check when needed decreaseRadiusStep: 0.01 # Increased radius amount applied for each frametime check when needed - increaseRadiusStep: 0.03 + increaseRadiusStep: 0.02 # Configure the end of the inner circle, which is the area that will be rendered at full resolution innerRadius: 0.50 @@ -83,15 +88,21 @@ fixedFoveated: outerRadius: 0.80 # The remainder of the image will be rendered at 1/16th resolution - # Only applies FFR to target renders that matches equal resolution than final render. + # Edge radius: Creates a Hidden Radial Mask. Available only in RDM mode + edgeRadius: 1.15 + + # Only applies FFR to target renders that matches equal resolution to final render. # Some games requires to disable it. preciseResolution: true - # FFR will not be applied to first specified target renders. Some games needs to skip - # some target renders to avoid crashes. This is different from HRM option. + # FFR will not be applied to first and last specified target renders. Some games needs to skip + # some target renders to avoid crashes. This is different from HRM option. Target renders from VSR mode + # are different to RDM mode target renders. ignoreFirstTargetRenders: 0 + ignoreLastTargetRenders: 0 - # When reducing resolution, prefer to keep horizontal (true) or vertical (false) resolution? + # When reducing resolution, prefer to keep horizontal (true) or vertical (false) resolution? Available + # only in VRS mode. favorHorizontal: true # When applying fixed foveated rendering, vrperfkit will do its best to guess when the game @@ -103,8 +114,11 @@ fixedFoveated: #overrideSingleEyeOrder: LRLRLR # Fast mode: When game renders each eye separatelly, enabling this will save a bit of performance, - # But overrideSingleEyeOrder will be ignored. + # But overrideSingleEyeOrder will be ignored. Available only in VRS mode. fastMode: false + # When fastMode is used, you can use HRM render targets counter instead default VRS counter. This will + # force to enable HRM. Ignored target renders configuration will be used from hiddenMask section instead. + fastModeUsesHRMCount: false # Hidden radial mask (HRM) applies a render mask to avoid GPU rendering hidden pixel that are not visible # in the headset and gain some performance without any disvantage. Not all games are compatible. @@ -125,10 +139,10 @@ hiddenMask: # Decreased radius amount applied for each frametime check when needed decreaseRadiusStep: 0.01 # Increased radius amount applied for each frametime check when needed - increaseRadiusStep: 0.03 + increaseRadiusStep: 0.02 - # Radius - radius: 1.15 + # Edge radius + edgeRadius: 1.15 # Only applies HRM to target renders that matches equal resolution than final render. # Some games requires to disable it. @@ -137,6 +151,7 @@ hiddenMask: # HRM will not be applied to first specified target renders. Some games needs to skip # some target renders to avoid crashes. This is different from FFR option. ignoreFirstTargetRenders: 0 + ignoreLastTargetRenders: 0 # Game Mode # Some game need a special mode: @@ -148,7 +163,7 @@ gameMode: auto # This controls how many frames must be generated to measure frametime when dynamic mode is used # with FFR and/or HRM. -dynamicFramesCheck: 3 +dynamicFramesCheck: 1 # Enabling debugMode will visualize the radius to which upscaling is applied (see above). # It will also output additional log messages and regularly report how much GPU frame time @@ -178,11 +193,7 @@ hotkeys: decreaseUpscalingSharpness: ["ctrl", "f6"] # Toggle the application of MIP bias (see above) toggleUpscalingApplyMipBias: ["ctrl", "f7"] - # Increase the hidden radial mask circle's radius (see above) by 0.05 - increaseHiddenMaskRadius: ["ctrl", "f8"] - # Decrease the hidden radial mask circle's radius (see above) by 0.05 - decreaseHiddenMaskRadius: ["ctrl", "f9"] # Toggle fixed foveated rendering - toggleFixedFoveated: ["alt", "f1"] + toggleFixedFoveated: ["ctrl", "f8"] # Toggle if you want to prefer horizontal or vertical resolution - toggleFFRFavorHorizontal: ["alt", "f2"] + toggleFFRFavorHorizontal: ["ctrl", "f9"]