Skip to content

Commit 730eb4d

Browse files
committed
Merge branch 'ajaus/input-ping-main' into 'main'
[REMIX-5970] Keep an unfocused window's input queue serviced by generating WM_NULL every 2 seconds See merge request lightspeedrtx/dxvk-remix-nv!2328
2 parents c08e247 + 8d78ec8 commit 730eb4d

3 files changed

Lines changed: 94 additions & 0 deletions

File tree

RtxOptions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ This file is auto-generated by RTX Remix. To regenerate it, run Remix with `DXVK
230230
|rtx.enableIndexBufferMemoization|bool|True|||CPU performance optimization, should generally be enabled\. Will reduce main thread time by caching processIndexBuffer operations and reusing when possible, this will come at the expense of some CPU RAM\.|
231231
|rtx.enableIndirectAlphaBlendShadows|bool|True|||Calculate shadows for semi\-transparent \(alpha blended\) objects in indirect lighting \(i\.e\. reflections and GI\)\. In engineering terms: include OBJECT\_MASK\_ALPHA\_BLEND into secondary visibility rays\.|
232232
|rtx.enableIndirectTranslucentShadows|bool|False|||Calculate coloured shadows for translucent materials \(i\.e\. glass, water\) in indirect lighting \(i\.e\. reflections and GI\)\. In engineering terms: include OBJECT\_MASK\_TRANSLUCENT into secondary visibility rays\.|
233+
|rtx.enableInputQueuePing|bool|False|||When enabled, Remix posts a WM\_NULL message to the game window every rtx\.inputQueuePingIntervalMs milliseconds so Windows keeps servicing the window's input queue and does not flag the game as not responding while it presents without focus\.<br>This setting is read when the swapchain is created; changing it will not take effect at runtime\.|
233234
|rtx.enableInstanceDebuggingTools|bool|False|||NOTE: This will disable temporal correllation for instances, but allow the use of instance developer debug tools|
234235
|rtx.enableLegacyRectLightConeShaping|bool|False|||If true, restores the legacy cos\(angle\) \* aspectRatio formula for RectLight cone shaping\. Enable this only to preserve the look of existing scenes that were authored against the legacy behavior\. Note this can silently kill the light at high aspect ratios\.|
235236
|rtx.enableMultiStageTextureFactorBlending|bool|True|||Support texture factor blending in stage 1~7\. Currently only support 1 additional blending stage, more than 1 additional blending stages will be ignored\.|
@@ -319,6 +320,7 @@ This file is auto-generated by RTX Remix. To regenerate it, run Remix with `DXVK
319320
|rtx.initializer.asyncAssetLoading|bool|True|||If true, a separate thread is created to load USD assets asynchronously\.|
320321
|rtx.initializer.asyncShaderFinalizing|bool|True|||When set to true, shader prewarming will be finalized asynchronously rather than Remix's initializer blocking synchronously until it is finished\.<br>Do note that this only controls if Remix waits for prewarming to finish or not on startup, if shaders are not finished prewarming by the time they are first used by Remix \(e\.g\. once ray tracing starts\) they will still block synchronously until finished even with this option set\. See rtx\.shader\.enableAsyncCompilation for true async shader compilation\.<br>This option should usually be set to true and is usually combined with async shader compilation to faciliate a better user experience, but can be to set to false to ensure all shaders are loaded to allow for slightly more deterministic behavior when debugging, or if prewarming all shaders before rendering is desired behavior \(at the cost of blocking on startup for a while\)\.<br>Finally, this option only takes effect for the most part when shader prewarming is enabled \(rtx\.initializer\.asyncShaderPrewarming\) as otherwise there will be no prewarmed shaders to worry about finalizing\.|
321322
|rtx.initializer.asyncShaderPrewarming|bool|True|||When set to true, shader prewarming will be enabled, allowing for Remix to start compiling shaders before their first use\.<br>Typically shaders will only begin compilation on their first use, but this is generally undesirable from a user experience perspective as this often causes stalls or wait times while using the application until all shaders have been used at least once\.<br>By prewarming permutations of potentially required shaders in advance this can be avoided by ensuring all required shaders are compiled before they are used\.<br>Additionally, this prewarming work can often be overlapped with an application's existing startup sequence \(e\.g\. the initial loading screen of a game\), allowing Remix's shaders to be ready before they are actually used and avoiding any stalls or wait times\.<br>As such this should generally be set to true and is often used in conjunction with rtx\.initializer\.asyncShaderFinalizing to avoid Remix blocking on initialization for the prewarming to complete, and rtx\.shader\.enableAsyncCompilation to avoid shaders from blocking if the application starts using Remix shaders before prewarming is complete\.<br>Since prewarming uses shader permutation however a greater amount of shaders will need to be compiled when this option is enabled compared to the minimal required set \(mainly to accomodate various runtime situations and user\-facing options that may be altered\)\. Setting this option to false may be useful in specific cases where minimizing this compilation cost is important over user experience \(e\.g\. for automated testing\)\.|
323+
|rtx.inputQueuePingIntervalMs|int|2000|1||Interval in milliseconds between WM\_NULL pings when rtx\.enableInputQueuePing is enabled\. Unlike the enable flag, this is re\-read by the ping thread every iteration, so changes take effect after at most one interval\.|
322324
|rtx.instanceOverrideInstanceIdx|int|-1||||
323325
|rtx.instanceOverrideInstanceIdxRange|int|15||||
324326
|rtx.instanceOverrideSelectedInstancePrintMaterialHash|bool|False||||

src/d3d9/d3d9_swapchain.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,10 +370,28 @@ namespace dxvk {
370370
} else {
371371
HookWindowProc(m_window);
372372
}
373+
374+
// NV-DXVK start: input-queue liveness ping
375+
if (m_window && enableInputQueuePing()) {
376+
m_inputPingThread = dxvk::thread([this] { RunInputQueuePing(); });
377+
}
378+
// NV-DXVK end
373379
}
374380

375381

376382
D3D9SwapChainEx::~D3D9SwapChainEx() {
383+
// NV-DXVK start: input-queue liveness ping
384+
if (m_inputPingThread.joinable()) {
385+
{
386+
std::lock_guard<dxvk::mutex> lock(m_inputPingMutex);
387+
m_inputPingStop = true;
388+
}
389+
390+
m_inputPingCond.notify_one();
391+
m_inputPingThread.join();
392+
}
393+
// NV-DXVK end
394+
377395
DestroyBackBuffers();
378396

379397
ResetWindowProc(m_window);
@@ -1574,6 +1592,49 @@ namespace dxvk {
15741592
}
15751593

15761594

1595+
// NV-DXVK start
1596+
// Generate a WM_NULL message every few seconds to keep Windows from inaccurately
1597+
// declaring the host application to be not responsive
1598+
void D3D9SwapChainEx::RunInputQueuePing() {
1599+
env::setThreadName("dxvk-input-ping");
1600+
1601+
#ifdef REMIX_DEVELOPMENT
1602+
bool wasHung = false;
1603+
#endif
1604+
1605+
while (true) {
1606+
const auto interval = std::chrono::milliseconds(inputQueuePingIntervalMs());
1607+
1608+
{
1609+
std::unique_lock<dxvk::mutex> lock(m_inputPingMutex);
1610+
1611+
if (m_inputPingCond.wait_for(lock, interval, [this] { return m_inputPingStop; })) {
1612+
return;
1613+
}
1614+
}
1615+
1616+
if (!IsWindow(m_window)) {
1617+
continue;
1618+
}
1619+
1620+
#ifdef REMIX_DEVELOPMENT
1621+
// The classifier's verdict is the validity gate for this ping: a run that
1622+
// never logs is a run where the ping is working.
1623+
const bool hung = IsHungAppWindow(m_window);
1624+
1625+
if (hung != wasHung) {
1626+
Logger::info(str::format("Input queue ping: window ",
1627+
hung ? "flagged as not responding" : "responding"));
1628+
wasHung = hung;
1629+
}
1630+
#endif
1631+
1632+
PostMessageW(m_window, WM_NULL, 0, 0);
1633+
}
1634+
}
1635+
// NV-DXVK end
1636+
1637+
15771638
uint32_t D3D9SwapChainEx::GetActualFrameLatency() {
15781639
uint32_t maxFrameLatency = m_parent->GetFrameLatency();
15791640

src/d3d9/d3d9_swapchain.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
#include "../dxvk/dxvk_swapchain_blitter.h"
3434

3535
#include "../util/sync/sync_signal.h"
36+
#include "../util/thread.h"
37+
38+
// NV-DXVK start: input-queue liveness ping
39+
#include "../dxvk/rtx_render/rtx_options.h"
40+
// NV-DXVK end
3641

3742
#include <vector>
3843

@@ -123,6 +128,21 @@ namespace dxvk {
123128
return m_device;
124129
}
125130

131+
// NV-DXVK start: input-queue liveness ping, see RunInputQueuePing
132+
RTX_OPTION("rtx", bool, enableInputQueuePing, false,
133+
"When enabled, Remix posts a WM_NULL message to the game window every "
134+
"rtx.inputQueuePingIntervalMs milliseconds so Windows keeps servicing the window's "
135+
"input queue and does not flag the game as not responding while it presents without "
136+
"focus.\n"
137+
"This setting is read when the swapchain is created; changing it will not take effect at runtime.");
138+
139+
RTX_OPTION_ARGS("rtx", uint32_t, inputQueuePingIntervalMs, 2000,
140+
"Interval in milliseconds between WM_NULL pings when rtx.enableInputQueuePing is enabled. "
141+
"Unlike the enable flag, this is re-read by the ping thread every iteration, so changes "
142+
"take effect after at most one interval.",
143+
args.minValue = static_cast<uint32_t>(1));
144+
// NV-DXVK end
145+
126146
protected:
127147

128148
enum BindingIds : uint32_t {
@@ -181,6 +201,13 @@ namespace dxvk {
181201
HWND m_window = nullptr;
182202
HMONITOR m_monitor = nullptr;
183203

204+
// NV-DXVK start: input-queue liveness ping, see RunInputQueuePing
205+
dxvk::thread m_inputPingThread;
206+
dxvk::mutex m_inputPingMutex;
207+
dxvk::condition_variable m_inputPingCond;
208+
bool m_inputPingStop = false;
209+
// NV-DXVK end
210+
184211
WindowState m_windowState;
185212

186213
uint32_t m_originalWidth;
@@ -196,6 +223,10 @@ namespace dxvk {
196223

197224
void SynchronizePresent();
198225

226+
// NV-DXVK start: input-queue liveness ping
227+
void RunInputQueuePing();
228+
// NV-DXVK end
229+
199230
void RecreateSwapChain(
200231
BOOL Vsync);
201232

0 commit comments

Comments
 (0)