Skip to content

Commit 740b913

Browse files
Merge branch 'REMIX-4758-graph-gui-improvements' into 'main'
[REMIX-4758] add simple GUI controls for graphs. Closes REMIX-4758 See merge request lightspeedrtx/dxvk-remix-nv!1794
2 parents b4874b8 + c202693 commit 740b913

File tree

5 files changed

+127
-67
lines changed

5 files changed

+127
-67
lines changed

RtxOptions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
303303
|rtx.freeCameraSpeed|float|200|||Free camera speed \[GameUnits/s\]\.|
304304
|rtx.freeCameraTurningSpeed|float|1|||Free camera turning speed \(applies to keyboard, not mouse\) \[radians/s\]\.|
305305
|rtx.fusedWorldViewMode|int|0|||Set if game uses a fused World\-View transform matrix\.|
306+
|rtx.graph.enable|bool|True|||Enable graph loading\. If disabled, all graphs will be unloaded, losing any state\.|
307+
|rtx.graph.pauseGraphUpdates|bool|False|||Pause graph updating\. If enabled, graphs logic will not be updated, but graph state will be retained\.|
306308
|rtx.graphicsPreset|int|5|||Overall rendering preset, higher presets result in higher image quality, lower presets result in better performance\.|
307309
|rtx.gui.backgroundAlpha|float|0.9|0|1|A value controlling the alpha of the GUI background\.|
308310
|rtx.gui.compactGui|bool|False|||A setting to toggle between compact and spacious GUI modes\.|

src/dxvk/imgui/dxvk_imgui.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2691,7 +2691,7 @@ namespace dxvk {
26912691

26922692
// Graph Visualization Section
26932693
ImGui::Separator();
2694-
if (ImGui::CollapsingHeader("Graph Visualization", ImGuiTreeNodeFlags_DefaultOpen)) {
2694+
if (ImGui::CollapsingHeader("Remix Logic", ImGuiTreeNodeFlags_DefaultOpen)) {
26952695
ImGui::Indent();
26962696
m_graphGUI->showGraphVisualization(ctx);
26972697
ImGui::Unindent();

src/dxvk/rtx_render/graph/rtx_graph_gui.cpp

Lines changed: 68 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,37 @@
2020
* DEALINGS IN THE SOFTWARE.
2121
*/
2222

23+
#include "imgui/imgui.h"
24+
#include "rtx_graph_batch.h"
2325
#include "rtx_graph_gui.h"
24-
#include "../../imgui/imgui.h"
25-
#include "../rtx_scene_manager.h"
26-
#include "../rtx_context.h"
2726
#include "rtx_graph_instance.h"
28-
#include "rtx_graph_batch.h"
29-
#include "../../util/util_string.h"
27+
#include "rtx_render/rtx_context.h"
28+
#include "rtx_render/rtx_imgui.h"
29+
#include "rtx_render/rtx_scene_manager.h"
30+
#include "../util/util_string.h"
3031
#include <algorithm>
31-
#include <sstream>
3232
#include <iomanip>
33+
#include <sstream>
3334

3435
namespace dxvk {
3536

3637
void RtxGraphGUI::showGraphVisualization(const Rc<DxvkContext>& ctx) {
3738
RtxContext* rtxContext = static_cast<RtxContext*>(ctx.ptr());
3839
const SceneManager& sceneManager = rtxContext->getSceneManager();
39-
40-
ImGui::Text("RTX Graph Visualization");
4140
ImGui::Separator();
41+
ImGui::Checkbox("Enable", &GraphManager::enableObject());
42+
ImGui::SameLine(0.0f, 20.f);
43+
ImGui::Checkbox("Pause", &GraphManager::pauseGraphUpdatesObject());
44+
ImGui::SameLine(0.0f, 20.f);
45+
if (IMGUI_ADD_TOOLTIP(ImGui::Button("Reset Graph State"), "Destroys then recreates all graphs, clearing any stored state.")) {
46+
const GraphManager& graphManager = sceneManager.getGraphManager();
47+
graphManager.resetGraphState();
48+
}
4249

4350
// Graph selector
4451
showGraphSelector(sceneManager);
4552

4653
if (m_selectedInstanceId != kInvalidInstanceId) {
47-
ImGui::Separator();
48-
4954
// Update graph data
5055
updateGraphData(sceneManager);
5156

@@ -126,6 +131,10 @@ void RtxGraphGUI::showGraphSelector(const SceneManager& sceneManager) {
126131
for (const auto& [name, id] : instanceList) {
127132
if (id == m_selectedInstanceId) {
128133
ImGui::Text("Selected: %s", name.c_str());
134+
if (IMGUI_ADD_TOOLTIP(ImGui::Button("Reset Instance"), "Destroys then recreates this graph instance, clearing any stored state.")) {
135+
graphManager.queueInstanceReset(m_selectedInstanceId);
136+
m_selectedInstanceId = kInvalidInstanceId; // Clear selection since instance will be removed
137+
}
129138
break;
130139
}
131140
}
@@ -135,75 +144,73 @@ void RtxGraphGUI::showGraphSelector(const SceneManager& sceneManager) {
135144
}
136145

137146
void RtxGraphGUI::showComponentList() {
138-
ImGui::Text("Components:");
139-
ImGui::Separator();
140-
141147
if (m_components.empty()) {
142148
ImGui::Text("No components in selected graph instance");
143149
return;
144150
}
145-
146-
// Show components in a scrollable list with resizable height
147-
ImGui::BeginChild("ComponentList", ImVec2(0, m_componentListHeight), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
148-
149-
for (size_t i = 0; i < m_components.size(); ++i) {
150-
const auto& component = m_components[i];
151-
152-
// Component header with collapsible tree node
153-
std::string headerText = component.typeName;
151+
if (ImGui::CollapsingHeader("Components:", ImGuiTreeNodeFlags_DefaultOpen)) {
152+
// Show components in a scrollable list with resizable height
153+
ImGui::BeginChild("ComponentList", ImVec2(0, m_componentListHeight), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
154154

155-
if (ImGui::CollapsingHeader(headerText.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
156-
// Show component description as tooltip on header hover
157-
if (ImGui::IsItemHovered() && !component.docString.empty()) {
158-
ImGui::BeginTooltip();
159-
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); // Set wrap width
160-
ImGui::TextWrapped("%s", component.docString.c_str());
161-
ImGui::PopTextWrapPos();
162-
ImGui::EndTooltip();
163-
}
155+
for (size_t i = 0; i < m_components.size(); ++i) {
156+
const auto& component = m_components[i];
164157

165-
ImGui::Indent();
158+
// Component header with collapsible tree node
159+
std::string headerText = component.typeName;
166160

167-
// Show properties
168-
ImGui::Text("Properties:");
169-
for (const auto& prop : component.properties) {
170-
// Display property with name, value, and index
171-
std::string propText = " [" + std::to_string(prop.topologyIndex) + "] " + prop.name + ": " + prop.currentValue;
172-
ImGui::Text("%s", propText.c_str());
173-
174-
// Show tooltip with doc string and property paths on hover
175-
if (ImGui::IsItemHovered() && (!prop.docString.empty() || !prop.propertyPaths.empty())) {
161+
if (ImGui::CollapsingHeader(headerText.c_str())) {
162+
// Show component description as tooltip on header hover
163+
if (ImGui::IsItemHovered() && !component.docString.empty()) {
176164
ImGui::BeginTooltip();
177165
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); // Set wrap width
166+
ImGui::TextWrapped("%s", component.docString.c_str());
167+
ImGui::PopTextWrapPos();
168+
ImGui::EndTooltip();
169+
}
170+
171+
ImGui::Indent();
172+
173+
// Show properties
174+
for (const auto& prop : component.properties) {
175+
// Display property with name, value, and index
176+
std::string propText = " [" + std::to_string(prop.topologyIndex) + "] " + prop.name + ": " + prop.currentValue;
177+
ImGui::Text("%s", propText.c_str());
178178

179-
// Show all property paths
180-
if (!prop.propertyPaths.empty()) {
181-
if (prop.propertyPaths.size() == 1) {
182-
ImGui::Text("Path: %s", prop.propertyPaths[0].c_str());
183-
} else {
184-
ImGui::Text("Paths:");
185-
for (const auto& path : prop.propertyPaths) {
186-
ImGui::Text(" %s", path.c_str());
179+
// Show tooltip with doc string and property paths on hover
180+
if (ImGui::IsItemHovered() && (!prop.docString.empty() || !prop.propertyPaths.empty())) {
181+
ImGui::BeginTooltip();
182+
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); // Set wrap width
183+
184+
// Show all property paths
185+
if (!prop.propertyPaths.empty()) {
186+
if (prop.propertyPaths.size() == 1) {
187+
ImGui::Text("Path: %s", prop.propertyPaths[0].c_str());
188+
} else {
189+
ImGui::Text("Paths:");
190+
for (const auto& path : prop.propertyPaths) {
191+
ImGui::Text(" %s", path.c_str());
192+
}
193+
}
194+
if (!prop.docString.empty()) {
195+
ImGui::Separator();
187196
}
188197
}
198+
199+
// Show description
189200
if (!prop.docString.empty()) {
190-
ImGui::Separator();
201+
ImGui::TextWrapped("%s", prop.docString.c_str());
191202
}
203+
204+
ImGui::PopTextWrapPos();
205+
ImGui::EndTooltip();
192206
}
193-
194-
// Show description
195-
if (!prop.docString.empty()) {
196-
ImGui::TextWrapped("%s", prop.docString.c_str());
197-
}
198-
199-
ImGui::PopTextWrapPos();
200-
ImGui::EndTooltip();
201207
}
208+
209+
ImGui::Unindent();
210+
ImGui::Spacing();
202211
}
203-
204-
ImGui::Unindent();
205-
ImGui::Spacing();
206212
}
213+
207214
}
208215

209216
ImGui::EndChild();

src/dxvk/rtx_render/graph/rtx_graph_gui.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class RtxGraphGUI {
6262

6363
// Graph visualization state
6464
static constexpr uint64_t kInvalidInstanceId = std::numeric_limits<uint64_t>::max();
65-
static constexpr float kDefaultComponentListHeight = 400.0f;
65+
static constexpr float kDefaultComponentListHeight = 800.0f;
6666
uint64_t m_selectedInstanceId = kInvalidInstanceId;
6767
std::vector<ComponentInfo> m_components;
6868
std::array<char, 256> m_instanceFilter = {};

src/dxvk/rtx_render/graph/rtx_graph_manager.h

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@
2020
* DEALINGS IN THE SOFTWARE.
2121
*/
2222

23-
#include "../util/util_fast_cache.h"
24-
#include "rtx_graph_batch.h"
25-
#include "rtx_graph_types.h"
26-
#include "../rtx_asset_replacer.h"
2723
#include "dxvk_context.h"
2824
#include "dxvk_scoped_annotation.h"
25+
#include "rtx_graph_batch.h"
2926
#include "rtx_graph_ogn_writer.h"
27+
#include "rtx_graph_types.h"
28+
#include "rtx_render/rtx_asset_replacer.h"
29+
#include "rtx_render/rtx_option.h"
30+
#include "../util/util_fast_cache.h"
31+
#include <atomic>
3032
#include <mutex>
3133

3234

@@ -35,6 +37,9 @@ namespace dxvk {
3537
// The class responsible for managing graph lifetime and updates.
3638
class GraphManager {
3739
public:
40+
RTX_OPTION("rtx.graph", bool, enable, true, "Enable graph loading. If disabled, all graphs will be unloaded, losing any state.");
41+
RTX_OPTION("rtx.graph", bool, pauseGraphUpdates, false, "Pause graph updating. If enabled, graphs logic will not be updated, but graph state will be retained.");
42+
3843
GraphManager() {
3944
static std::once_flag schemaWriteFlag;
4045
std::call_once(schemaWriteFlag, [this]() {
@@ -55,6 +60,9 @@ class GraphManager {
5560

5661
GraphInstance* addInstance(Rc<DxvkContext> context, const RtGraphState& graphState) {
5762
ScopedCpuProfileZone();
63+
if (!enable()) {
64+
return nullptr;
65+
}
5866
auto iter = m_batches.find(graphState.topology.graphHash);
5967
if (iter == m_batches.end()) {
6068
iter = m_batches.emplace(graphState.topology.graphHash, RtGraphBatch()).first;
@@ -86,20 +94,59 @@ class GraphManager {
8694
m_graphInstances.erase(instanceId);
8795
}
8896

97+
// Queues the graph manager to wipe all graphs in the next update.
98+
void resetGraphState() const {
99+
std::lock_guard<std::mutex> lock(m_instanceResetMutex);
100+
m_resetPending = true;
101+
}
102+
103+
// Queues a specific graph instance to be reset in the next update.
104+
void queueInstanceReset(const uint64_t instanceId) const {
105+
std::lock_guard<std::mutex> lock(m_instanceResetMutex);
106+
m_instanceResetQueue.push_back(instanceId);
107+
}
108+
89109
void clear() {
90110
m_batches.clear();
91111
m_graphInstances.clear();
92112
}
93113

94114
void update(Rc<DxvkContext>& context) {
95115
ScopedCpuProfileZone();
116+
117+
// Check if a reset was requested from another thread
118+
{
119+
std::lock_guard<std::mutex> lock(m_instanceResetMutex);
120+
if (m_resetPending) {
121+
clear();
122+
m_instanceResetQueue.clear();
123+
m_resetPending = false;
124+
} else {
125+
// Process queued instance resets
126+
for (uint64_t instanceId : m_instanceResetQueue) {
127+
removeInstance(instanceId);
128+
}
129+
m_instanceResetQueue.clear();
130+
}
131+
}
132+
133+
if (!enable()) {
134+
clear();
135+
return;
136+
}
137+
if (pauseGraphUpdates()) {
138+
return;
139+
}
96140
for (auto& batch : m_batches) {
97141
batch.second.update(context);
98142
}
99143
}
100144

101145
void applySceneOverrides(Rc<DxvkContext> context) {
102146
ScopedCpuProfileZone();
147+
if (!enable() || pauseGraphUpdates()) {
148+
return;
149+
}
103150
for (auto& batch : m_batches) {
104151
batch.second.applySceneOverrides(context);
105152
}
@@ -122,6 +169,10 @@ class GraphManager {
122169
std::unordered_map<uint64_t, GraphInstance> m_graphInstances;
123170

124171
uint64_t m_nextInstanceId = 1;
172+
173+
mutable std::mutex m_instanceResetMutex;
174+
mutable bool m_resetPending = false;
175+
mutable std::vector<uint64_t> m_instanceResetQueue;
125176
};
126177

127178
}

0 commit comments

Comments
 (0)