Skip to content

Commit 067ea74

Browse files
committed
Move Renderdoc out of the debug_system.h and into a "real world" renderdoc_debug_system.cpp file that derives from debug_system.h and only loads a minimal amount of functionality at runtime to avoid tight integration requirements and still demonstrate what the tutorial is teaching.
Fix the install_dependencies_windows.bat script by replacing the multi-line PowerShell command with a single line version which should hopefully fix the Windows issue. Address other issues as reported.
1 parent 27715c0 commit 067ea74

File tree

6 files changed

+186
-54
lines changed

6 files changed

+186
-54
lines changed

attachments/simple_engine/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ set(SOURCES
119119
vulkan_device.cpp
120120
pipeline.cpp
121121
descriptor_manager.cpp
122+
renderdoc_debug_system.cpp
122123
)
123124

124125
# Create executable

attachments/simple_engine/camera_component.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ void CameraComponent::UpdateViewMatrix() {
4141
glm::vec3 position = transformComponent->GetPosition();
4242
viewMatrix = glm::lookAt(position, target, up);
4343
} else {
44-
viewMatrix = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), target, up);
44+
viewMatrix = glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), target, up);
4545
}
4646
viewMatrixDirty = false;
4747
}

attachments/simple_engine/debug_system.h

Lines changed: 8 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <mutex>
99
#include <unordered_map>
1010
#include <functional>
11+
#include <ctime>
1112

1213
/**
1314
* @brief Enum for different log levels.
@@ -135,8 +136,8 @@ class DebugSystem {
135136
}
136137

137138
// Call registered callbacks
138-
for (const auto& callback : logCallbacks) {
139-
callback(level, tag, message);
139+
for (const auto& kv : logCallbacks) {
140+
kv.second(level, tag, message);
140141
}
141142

142143
// If fatal, trigger crash handler
@@ -202,49 +203,15 @@ class DebugSystem {
202203
Log(LogLevel::Debug, "Performance", name + ": " + std::to_string(duration) + " us");
203204
measurements.erase(it);
204205
} else {
205-
Log(LogLevel::Warning, "Performance", "No measurement started with name: " + name);
206+
Log(LogLevel::Error, "Performance", "No measurement started with name: " + name);
206207
}
207208
}
208209

209-
/**
210-
* @brief Enable or disable RenderDoc integration.
211-
* @param enable Whether to enable RenderDoc integration.
212-
*/
213-
void EnableRenderDoc(bool enable) {
214-
std::lock_guard<std::mutex> lock(mutex);
215-
216-
renderDocEnabled = enable;
217-
Log(LogLevel::Info, "DebugSystem", std::string("RenderDoc integration ") + (enable ? "enabled" : "disabled"));
218-
219-
// In a real implementation, this would initialize RenderDoc API
220-
}
221-
222-
/**
223-
* @brief Check if RenderDoc integration is enabled.
224-
* @return True if RenderDoc integration is enabled, false otherwise.
225-
*/
226-
bool IsRenderDocEnabled() const {
227-
return renderDocEnabled;
228-
}
229-
230-
/**
231-
* @brief Trigger a RenderDoc frame capture.
232-
*/
233-
void CaptureRenderDocFrame() {
234-
std::lock_guard<std::mutex> lock(mutex);
235-
236-
if (renderDocEnabled) {
237-
Log(LogLevel::Info, "DebugSystem", "Capturing RenderDoc frame");
238-
239-
// In a real implementation, this would trigger a RenderDoc frame capture
240-
} else {
241-
Log(LogLevel::Warning, "DebugSystem", "RenderDoc integration is not enabled");
242-
}
243-
}
244210

245-
private:
246-
// Private constructor for singleton
211+
protected:
212+
// Protected constructor for inheritance
247213
DebugSystem() = default;
214+
virtual ~DebugSystem() = default;
248215

249216
// Delete copy constructor and assignment operator
250217
DebugSystem(const DebugSystem&) = delete;
@@ -269,8 +236,6 @@ class DebugSystem {
269236
// Performance measurements
270237
std::unordered_map<std::string, std::chrono::high_resolution_clock::time_point> measurements;
271238

272-
// RenderDoc integration
273-
bool renderDocEnabled = false;
274239
};
275240

276241
// Convenience macros for logging
@@ -282,4 +247,4 @@ class DebugSystem {
282247

283248
// Convenience macros for performance measurement
284249
#define MEASURE_START(name) DebugSystem::GetInstance().StartMeasurement(name)
285-
#define MEASURE_END(name) DebugSystem::GetInstance().EndMeasurement(name)
250+
#define MEASURE_END(name) DebugSystem::GetInstance().StopMeasurement(name)

attachments/simple_engine/install_dependencies_windows.bat

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,7 @@ if defined SLANGC_EXE (
122122
set "SLANG_ROOT=%LOCALAPPDATA%\slang"
123123
if not exist "%SLANG_ROOT%" mkdir "%SLANG_ROOT%"
124124
echo Downloading latest Slang release...
125-
powershell -NoProfile -ExecutionPolicy Bypass -Command "\
126-
$ErrorActionPreference='Stop'; \
127-
$r=Invoke-RestMethod 'https://api.github.com/repos/shader-slang/slang/releases/latest'; \
128-
$asset=$r.assets | Where-Object { $_.name -match 'win64.*\\.zip$' } | Select-Object -First 1; \
129-
if(-not $asset){ throw 'No win64 asset found'; } \
130-
$out=Join-Path $env:TEMP $asset.name; \
131-
Invoke-WebRequest $asset.browser_download_url -OutFile $out; \
132-
Expand-Archive -Path $out -DestinationPath $env:LOCALAPPDATA\slang -Force; \
133-
Write-Host ('Downloaded Slang ' + $r.tag_name) \
134-
"
125+
powershell -NoProfile -ExecutionPolicy Bypass -Command "$ErrorActionPreference='Stop'; $r=Invoke-RestMethod 'https://api.github.com/repos/shader-slang/slang/releases/latest'; $asset=$r.assets | Where-Object { $_.name -match 'win64.*\.zip$' } | Select-Object -First 1; if(-not $asset){ throw 'No win64 asset found'; } $out=Join-Path $env:TEMP $asset.name; Invoke-WebRequest $asset.browser_download_url -OutFile $out; Expand-Archive -Path $out -DestinationPath $env:LOCALAPPDATA\slang -Force; Write-Host ('Downloaded Slang ' + $r.tag_name)"
135126
echo Locating slangc.exe...
136127
set "SLANGC_PATH="
137128
for /f "delims=" %%F in ('dir /b /s "%LOCALAPPDATA%\slang\slangc.exe" 2^>nul') do (
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#include "renderdoc_debug_system.h"
2+
3+
#include <cstring>
4+
#include <iostream>
5+
6+
#if defined(_WIN32)
7+
#define WIN32_LEAN_AND_MEAN
8+
#include <windows.h>
9+
#elif defined(__APPLE__) || defined(__linux__)
10+
#include <dlfcn.h>
11+
#endif
12+
13+
// Value for eRENDERDOC_API_Version_1_4_1 from RenderDoc's header to avoid including it
14+
#ifndef RENDERDOC_API_VERSION_1_4_1
15+
#define RENDERDOC_API_VERSION_1_4_1 10401
16+
#endif
17+
18+
// Minimal local typedefs and struct to receive function pointers without including renderdoc_app.h
19+
using pTriggerCaptureLocal = void (*)();
20+
using pStartFrameCaptureLocal = void (*)(void*, void*);
21+
using pEndFrameCaptureLocal = unsigned int (*)(void*, void*);
22+
23+
struct RENDERDOC_API_1_4_1_MIN {
24+
pTriggerCaptureLocal TriggerCapture;
25+
void* _pad0; // We don't rely on layout beyond the subset we read via memcpy
26+
pStartFrameCaptureLocal StartFrameCapture;
27+
pEndFrameCaptureLocal EndFrameCapture;
28+
};
29+
30+
bool RenderDocDebugSystem::LoadRenderDocAPI() {
31+
if (renderdocAvailable) return true;
32+
33+
// Try to fetch RENDERDOC_GetAPI from a loaded module without forcing a dependency
34+
pRENDERDOC_GetAPI getAPI = nullptr;
35+
36+
#if defined(_WIN32)
37+
HMODULE mod = GetModuleHandleA("renderdoc.dll");
38+
if (!mod) {
39+
// If not already injected/loaded, do not force-load. We can attempt LoadLibraryA as a fallback
40+
mod = LoadLibraryA("renderdoc.dll");
41+
if (!mod) {
42+
LOG_INFO("RenderDoc", "RenderDoc not loaded into process");
43+
return false;
44+
}
45+
}
46+
getAPI = reinterpret_cast<pRENDERDOC_GetAPI>(GetProcAddress(mod, "RENDERDOC_GetAPI"));
47+
#elif defined(__APPLE__) || defined(__linux__)
48+
void* mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
49+
if (!mod) {
50+
// Try to load if not already loaded; if unavailable, just no-op
51+
mod = dlopen("librenderdoc.so", RTLD_NOW);
52+
if (!mod) {
53+
LOG_INFO("RenderDoc", "RenderDoc not loaded into process");
54+
return false;
55+
}
56+
}
57+
getAPI = reinterpret_cast<pRENDERDOC_GetAPI>(dlsym(mod, "RENDERDOC_GetAPI"));
58+
#endif
59+
60+
if (!getAPI) {
61+
LOG_WARNING("RenderDoc", "RENDERDOC_GetAPI symbol not found");
62+
return false;
63+
}
64+
65+
// Request API 1.4.1 into a temporary buffer and then extract needed functions
66+
RENDERDOC_API_1_4_1_MIN apiMin{};
67+
void* apiPtr = nullptr;
68+
int result = getAPI(RENDERDOC_API_VERSION_1_4_1, &apiPtr);
69+
if (result == 0 || apiPtr == nullptr) {
70+
LOG_WARNING("RenderDoc", "Failed to acquire RenderDoc API 1.4.1");
71+
return false;
72+
}
73+
74+
// Copy only the subset we care about; layout is stable for these early members
75+
std::memcpy(&apiMin, apiPtr, sizeof(apiMin));
76+
77+
fnTriggerCapture = apiMin.TriggerCapture;
78+
fnStartFrameCapture = apiMin.StartFrameCapture;
79+
fnEndFrameCapture = apiMin.EndFrameCapture;
80+
81+
renderdocAvailable = (fnTriggerCapture || fnStartFrameCapture || fnEndFrameCapture);
82+
83+
if (renderdocAvailable) {
84+
LOG_INFO("RenderDoc", "RenderDoc API loaded");
85+
} else {
86+
LOG_WARNING("RenderDoc", "RenderDoc API did not provide expected functions");
87+
}
88+
89+
return renderdocAvailable;
90+
}
91+
92+
void RenderDocDebugSystem::TriggerCapture() {
93+
if (!renderdocAvailable && !LoadRenderDocAPI()) return;
94+
if (fnTriggerCapture) {
95+
fnTriggerCapture();
96+
LOG_INFO("RenderDoc", "Triggered capture");
97+
} else {
98+
LOG_WARNING("RenderDoc", "TriggerCapture not available");
99+
}
100+
}
101+
102+
void RenderDocDebugSystem::StartFrameCapture(void* device, void* window) {
103+
if (!renderdocAvailable && !LoadRenderDocAPI()) return;
104+
if (fnStartFrameCapture) {
105+
fnStartFrameCapture(device, window);
106+
LOG_DEBUG("RenderDoc", "StartFrameCapture called");
107+
} else {
108+
LOG_WARNING("RenderDoc", "StartFrameCapture not available");
109+
}
110+
}
111+
112+
bool RenderDocDebugSystem::EndFrameCapture(void* device, void* window) {
113+
if (!renderdocAvailable && !LoadRenderDocAPI()) return false;
114+
if (fnEndFrameCapture) {
115+
unsigned int ok = fnEndFrameCapture(device, window);
116+
LOG_DEBUG("RenderDoc", ok ? "EndFrameCapture succeeded" : "EndFrameCapture failed");
117+
return ok != 0;
118+
}
119+
LOG_WARNING("RenderDoc", "EndFrameCapture not available");
120+
return false;
121+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#pragma once
2+
3+
#include "debug_system.h"
4+
5+
// RenderDoc integration is optional and loaded at runtime.
6+
// This header intentionally does NOT include <renderdoc_app.h> to avoid a hard dependency.
7+
// Instead, we declare a minimal interface and dynamically resolve the API if present.
8+
9+
class RenderDocDebugSystem : public DebugSystem {
10+
public:
11+
static RenderDocDebugSystem& GetInstance() {
12+
static RenderDocDebugSystem instance;
13+
return instance;
14+
}
15+
16+
// Attempt to load the RenderDoc API from the current process.
17+
// Safe to call multiple times.
18+
bool LoadRenderDocAPI();
19+
20+
// Returns true if the RenderDoc API has been successfully loaded.
21+
bool IsAvailable() const { return renderdocAvailable; }
22+
23+
// Triggers an immediate capture (equivalent to pressing the capture hotkey in the UI).
24+
void TriggerCapture();
25+
26+
// Starts a frame capture for the given device/window (can be nullptr to auto-detect on many backends).
27+
void StartFrameCapture(void* device = nullptr, void* window = nullptr);
28+
29+
// Ends a frame capture previously started. Returns true on success.
30+
bool EndFrameCapture(void* device = nullptr, void* window = nullptr);
31+
32+
private:
33+
RenderDocDebugSystem() = default;
34+
~RenderDocDebugSystem() override = default;
35+
36+
RenderDocDebugSystem(const RenderDocDebugSystem&) = delete;
37+
RenderDocDebugSystem& operator=(const RenderDocDebugSystem&) = delete;
38+
39+
// Internal function pointers matching the subset of RenderDoc API we use.
40+
// We avoid including the official header by declaring minimal signatures.
41+
using pRENDERDOC_GetAPI = int (*)(int, void**);
42+
43+
// Subset of API function pointers
44+
typedef void (*pRENDERDOC_TriggerCapture)();
45+
typedef void (*pRENDERDOC_StartFrameCapture)(void* device, void* window);
46+
typedef unsigned int (*pRENDERDOC_EndFrameCapture)(void* device, void* window); // returns bool in C API
47+
48+
// Storage for resolved API
49+
pRENDERDOC_TriggerCapture fnTriggerCapture = nullptr;
50+
pRENDERDOC_StartFrameCapture fnStartFrameCapture = nullptr;
51+
pRENDERDOC_EndFrameCapture fnEndFrameCapture = nullptr;
52+
53+
bool renderdocAvailable = false;
54+
};

0 commit comments

Comments
 (0)