- Remove BeginCapture/EndCapture complexity for manual frame capture - Use TriggerCapture for single frame capture (simpler API) - Move TriggerCapture before Present to ensure frame is captured - Add SetForegroundWindow/SetFocus before TriggerCapture - Remove default capture path in Initialize to allow SetCaptureFilePath to work
217 lines
5.6 KiB
C++
217 lines
5.6 KiB
C++
#include "XCEngine/Debug/RenderDocCapture.h"
|
|
#include "XCEngine/Debug/Logger.h"
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <windows.h>
|
|
|
|
namespace XCEngine {
|
|
namespace Debug {
|
|
|
|
static const char* RENDERDOC_DLL_PATH = "renderdoc.dll";
|
|
|
|
RenderDocCapture& RenderDocCapture::Get() {
|
|
static RenderDocCapture instance;
|
|
return instance;
|
|
}
|
|
|
|
bool RenderDocCapture::Initialize(void* device, void* window) {
|
|
if (m_initialized) {
|
|
return true;
|
|
}
|
|
|
|
m_device = device;
|
|
m_window = window;
|
|
|
|
if (!LoadRenderDoc()) {
|
|
return false;
|
|
}
|
|
|
|
m_api->SetCaptureOptionU32(2, 1);
|
|
m_api->SetCaptureOptionU32(8, 1);
|
|
m_api->SetCaptureOptionU32(9, 1);
|
|
|
|
m_initialized = true;
|
|
Logger::Get().Info(LogCategory::General, "RenderDocCapture initialized successfully");
|
|
return true;
|
|
}
|
|
|
|
void RenderDocCapture::Shutdown() {
|
|
if (m_initialized) {
|
|
if (IsCapturing()) {
|
|
EndCapture();
|
|
}
|
|
UnloadRenderDoc();
|
|
m_initialized = false;
|
|
}
|
|
}
|
|
|
|
void RenderDocCapture::SetDevice(void* device) {
|
|
m_device = device;
|
|
}
|
|
|
|
void RenderDocCapture::SetWindow(void* window) {
|
|
m_window = window;
|
|
}
|
|
|
|
bool RenderDocCapture::LoadRenderDoc() {
|
|
char exePath[MAX_PATH];
|
|
GetModuleFileNameA(NULL, exePath, MAX_PATH);
|
|
char* end = strrchr(exePath, '\\');
|
|
if (end) *end = '\0';
|
|
strcat_s(exePath, "\\renderdoc.dll");
|
|
|
|
HMODULE module = LoadLibraryA(exePath);
|
|
if (!module) {
|
|
DWORD error = GetLastError();
|
|
char buf[512];
|
|
sprintf(buf, "Failed to load renderdoc.dll from %s, error=%lu", exePath, error);
|
|
Logger::Get().Warning(LogCategory::General, buf);
|
|
return false;
|
|
}
|
|
|
|
using PFN_RENDERDOC_GetAPI = int (RENDERDOC_CC*)(int version, void** apiOut);
|
|
PFN_RENDERDOC_GetAPI GetAPI = (PFN_RENDERDOC_GetAPI)GetProcAddress(module, "RENDERDOC_GetAPI");
|
|
if (!GetAPI) {
|
|
Logger::Get().Error(LogCategory::General, "Failed to get RENDERDOC_GetAPI");
|
|
FreeLibrary(module);
|
|
return false;
|
|
}
|
|
|
|
int apiVersion = 10700;
|
|
void* apiPtr = nullptr;
|
|
int ret = GetAPI(apiVersion, &apiPtr);
|
|
if (ret != 1 || !apiPtr) {
|
|
char buf[256];
|
|
sprintf(buf, "Failed to get RenderDoc API: ret=%d, ptr=%p", ret, apiPtr);
|
|
Logger::Get().Error(LogCategory::General, buf);
|
|
FreeLibrary(module);
|
|
return false;
|
|
}
|
|
|
|
m_renderDocModule = module;
|
|
m_api = (RENDERDOC_API_1_7_0*)apiPtr;
|
|
m_isLoaded = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void RenderDocCapture::UnloadRenderDoc() {
|
|
if (m_renderDocModule) {
|
|
FreeLibrary(m_renderDocModule);
|
|
m_renderDocModule = nullptr;
|
|
}
|
|
m_api = nullptr;
|
|
m_isLoaded = false;
|
|
}
|
|
|
|
bool RenderDocCapture::IsCapturing() const {
|
|
if (!m_isLoaded || !m_api) {
|
|
return false;
|
|
}
|
|
return m_api->IsFrameCapturing() != 0;
|
|
}
|
|
|
|
uint32_t RenderDocCapture::GetNumCaptures() const {
|
|
if (!m_isLoaded || !m_api) {
|
|
return 0;
|
|
}
|
|
return m_api->GetNumCaptures();
|
|
}
|
|
|
|
bool RenderDocCapture::GetCapture(uint32_t index, RenderDocCaptureInfo* info) const {
|
|
if (!m_isLoaded || !m_api || !info) {
|
|
return false;
|
|
}
|
|
uint32_t length = 0;
|
|
uint64_t timestamp = 0;
|
|
uint32_t result = m_api->GetCapture(index, info->filename, &length, ×tamp);
|
|
info->length = length;
|
|
info->timestamp = timestamp;
|
|
return result == 1;
|
|
}
|
|
|
|
bool RenderDocCapture::BeginCapture(const char* title) {
|
|
if (!m_isLoaded || !m_api) {
|
|
Logger::Get().Warning(LogCategory::General, "BeginCapture called but RenderDoc not loaded");
|
|
return false;
|
|
}
|
|
if (IsCapturing()) {
|
|
Logger::Get().Warning(LogCategory::General, "BeginCapture called while already capturing");
|
|
return false;
|
|
}
|
|
if (!m_device || !m_window) {
|
|
Logger::Get().Warning(LogCategory::General, "BeginCapture called with null device or window");
|
|
return false;
|
|
}
|
|
|
|
if (title) {
|
|
m_api->SetCaptureTitle(title);
|
|
}
|
|
|
|
SetForegroundWindow((HWND)m_window);
|
|
SetFocus((HWND)m_window);
|
|
|
|
m_api->SetActiveWindow(m_device, m_window);
|
|
m_api->StartFrameCapture(m_device, m_window);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RenderDocCapture::EndCapture() {
|
|
if (!m_isLoaded || !m_api) {
|
|
Logger::Get().Warning(LogCategory::General, "EndCapture called but RenderDoc not loaded");
|
|
return false;
|
|
}
|
|
if (!IsCapturing()) {
|
|
Logger::Get().Warning(LogCategory::General, "EndCapture called but not currently capturing");
|
|
return false;
|
|
}
|
|
|
|
m_api->EndFrameCapture(m_device, m_window);
|
|
return true;
|
|
}
|
|
|
|
void RenderDocCapture::TriggerCapture() {
|
|
if (!m_isLoaded || !m_api) {
|
|
return;
|
|
}
|
|
if (m_window) {
|
|
SetForegroundWindow((HWND)m_window);
|
|
SetFocus((HWND)m_window);
|
|
}
|
|
m_api->TriggerCapture();
|
|
}
|
|
|
|
void RenderDocCapture::SetCaptureFilePath(const char* path) {
|
|
if (!m_isLoaded || !m_api) {
|
|
return;
|
|
}
|
|
m_api->SetCaptureFilePathTemplate(path);
|
|
}
|
|
|
|
void RenderDocCapture::SetCaptureComments(const char* comments) {
|
|
if (!m_isLoaded || !m_api) {
|
|
return;
|
|
}
|
|
m_api->SetCaptureFileComments(nullptr, comments);
|
|
}
|
|
|
|
void RenderDocCapture::SetCaptureOptionU32(uint32_t option, uint32_t value) {
|
|
if (!m_isLoaded || !m_api) {
|
|
return;
|
|
}
|
|
m_api->SetCaptureOptionU32(option, value);
|
|
}
|
|
|
|
bool RenderDocCapture::LaunchReplayUI(uint32_t connect, const char* cmdline) {
|
|
if (!m_isLoaded || !m_api) {
|
|
Logger::Get().Warning(LogCategory::General, "LaunchReplayUI called but RenderDoc not loaded");
|
|
return false;
|
|
}
|
|
m_api->LaunchReplayUI(connect, cmdline);
|
|
return true;
|
|
}
|
|
|
|
} // namespace Debug
|
|
} // namespace XCEngine
|