Skip to content

Commit f181f22

Browse files
Merge branch 'light-debugger-crash-fix' into 'main'
[REMIX-2785] Fix crashes related to light manager threading See merge request lightspeedrtx/dxvk-remix-nv!1602
2 parents a11bd46 + 11ae937 commit f181f22

File tree

3 files changed

+35
-7
lines changed

3 files changed

+35
-7
lines changed

src/dxvk/rtx_render/rtx_light_manager.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,24 @@ namespace dxvk {
9696
}
9797

9898
void LightManager::clear() {
99+
if (!m_lightDebugUILock.owns_lock()) {
100+
m_lightDebugUILock.lock();
101+
}
99102
m_lights.clear();
103+
m_linearizedLights.clear();
104+
m_lightDebugUILock.unlock();
105+
}
106+
107+
void LightManager::clearFromUIThread() {
108+
// This needs to wait for `m_lightDebugUILock` to be unlocked, so it doesn't
109+
// cause crashes.
110+
std::lock_guard<std::mutex> lock(m_lightUIMutex);
111+
m_lights.clear();
112+
m_linearizedLights.clear();
113+
114+
// Note: Fallback light reset here so that changes to its settings will take effect, does not need to be part
115+
// of usual light clearing logic though.
116+
m_fallbackLight.reset();
100117
}
101118

102119
void LightManager::garbageCollectionInternal() {
@@ -126,6 +143,9 @@ namespace dxvk {
126143
}
127144

128145
void LightManager::garbageCollection(RtCamera& camera) {
146+
if (!m_lightDebugUILock.owns_lock()) {
147+
m_lightDebugUILock.lock();
148+
}
129149
if (RtxOptions::AntiCulling::isLightAntiCullingEnabled()) {
130150
cFrustum& cameraLightAntiCullingFrustum = camera.getLightAntiCullingFrustum();
131151
for (auto& [lightHash, rtLight] : getLightTable()) {
@@ -162,6 +182,7 @@ namespace dxvk {
162182
}
163183

164184
garbageCollectionInternal();
185+
m_lightDebugUILock.unlock();
165186
}
166187

167188
void LightManager::dynamicLightMatching() {
@@ -316,7 +337,7 @@ namespace dxvk {
316337
// can be processed like all other lights without complex logic at the cost of potentially more computational
317338
// cost, but it might actually work out in favor of performance since unordered map traversal done redundantly
318339
// may be more expensive than simple vector traversal on the linearized list.
319-
340+
320341
m_linearizedLights.clear();
321342

322343
if (m_fallbackLight) {
@@ -333,7 +354,7 @@ namespace dxvk {
333354
auto found = m_externalLights.find(handle);
334355
if (found != m_externalLights.end()) {
335356
m_linearizedLights.emplace_back(&found->second);
336-
}
357+
}
337358
}
338359

339360
// Count the active light of each type
@@ -569,6 +590,11 @@ namespace dxvk {
569590
}
570591

571592
RtLight* LightManager::addLight(const RtLight& rtLight, const RtLightAntiCullingType antiCullingType, const XXH64_hash_t lightToReplace) {
593+
if (!m_lightDebugUILock.owns_lock()) {
594+
// As addLight can actually erase old lights, we need to lock the mutex starting from the first call each frame.
595+
m_lightDebugUILock.lock();
596+
}
597+
572598
// This light is "off". This includes negative valued lights which in D3D games originally would act as subtractive lighting.
573599
const Vector3 originalRadiance = rtLight.getRadiance();
574600
if (originalRadiance.x < 0 || originalRadiance.y < 0 || originalRadiance.z < 0

src/dxvk/rtx_render/rtx_light_manager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ struct LightManager : public CommonDeviceObject {
7878
const DomeLightArgs& getDomeLightArgs() const { return m_gpuDomeLightArgs; }
7979

8080
void clear();
81+
void clearFromUIThread();
8182

8283
void garbageCollection(RtCamera& camera);
8384

@@ -124,6 +125,10 @@ struct LightManager : public CommonDeviceObject {
124125
std::vector<unsigned char> m_lightsGPUData{};
125126
std::vector<uint16_t> m_lightMappingData{};
126127

128+
// Mutex to prevent the debugging UI from accessing the light data after it's been deleted.
129+
mutable std::mutex m_lightUIMutex;
130+
std::unique_lock<std::mutex> m_lightDebugUILock = std::unique_lock<std::mutex>(m_lightUIMutex, std::defer_lock);
131+
127132
bool getActiveDomeLight(DomeLight& lightOut);
128133

129134
void garbageCollectionInternal();

src/dxvk/rtx_render/rtx_light_manager_gui.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,7 @@ namespace dxvk {
180180

181181
// Clear the lights and fallback light if the settings are dirty to recreate the lights on the next frame.
182182
if (lightSettingsDirty) {
183-
clear();
184-
185-
// Note: Fallback light reset here so that changes to its settings will take effect, does not need to be part
186-
// of usual light clearing logic though.
187-
m_fallbackLight.reset();
183+
clearFromUIThread();
188184
}
189185
}
190186

@@ -424,6 +420,7 @@ namespace dxvk {
424420
ImGui::SetNextWindowSize(viewport->Size);
425421
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.f, 0.f, 0.f, 0.0f));
426422
if (ImGui::Begin("Light Debug View", nullptr, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings)) {
423+
std::lock_guard<std::mutex> lock(m_lightUIMutex);
427424
ImDrawList* drawList = ImGui::GetWindowDrawList();
428425
drawList->PushClipRectFullScreen();
429426
const RtCamera& camera = device()->getCommon()->getSceneManager().getCamera();

0 commit comments

Comments
 (0)