Fix RenderDoc OpenGL capture - pass HGLRC instead of HDC

Critical fix: For OpenGL, RenderDoc requires HGLRC (OpenGL context) as
the device pointer, not HDC. Previously OpenGL test incorrectly passed
HDC which caused capture to silently fail (NumCaptures=0).

Changes:
- OpenGLDevice: Add wglCreateContextAttribsARB support for debug context
- OpenGLDevice: Create OpenGL 4.6 core profile with debug flag
- RenderDocCapture: Pass correct window to SetActiveWindow/StartFrameCapture
- OpenGL test: Pass HGLRC via SetDevice(), use BeginCapture/EndCapture

Fix root cause identified via RenderDoc docs analysis:
  'For OpenGL it must be the HGLRC, GLXContext, or EGLContext'
This commit is contained in:
2026-03-23 18:35:49 +08:00
parent e9f4f2dc49
commit 36683b4bb3
3 changed files with 112 additions and 5 deletions

View File

@@ -122,14 +122,19 @@ void RenderDocCapture::BeginCapture(const char* title) {
if (title) {
m_api->SetCaptureTitle(title);
}
m_api->StartFrameCapture(m_device, nullptr);
SetForegroundWindow((HWND)m_window);
SetFocus((HWND)m_window);
m_api->SetActiveWindow(m_device, m_window);
m_api->StartFrameCapture(m_device, m_window);
}
void RenderDocCapture::EndCapture() {
if (!m_isLoaded || !m_api) {
return;
}
m_api->EndFrameCapture(m_device, nullptr);
m_api->EndFrameCapture(m_device, m_window);
}
void RenderDocCapture::TriggerCapture() {

View File

@@ -14,10 +14,26 @@
#include "XCEngine/RHI/OpenGL/OpenGLCommandList.h"
#include "XCEngine/RHI/OpenGL/OpenGLCommandQueue.h"
#include "XCEngine/RHI/OpenGL/OpenGLSwapChain.h"
#include "XCEngine/Debug/Logger.h"
static bool s_windowClassRegistered = false;
static const wchar_t kWindowClassName[] = L"XCEngine_OpenGL_WindowClass";
typedef const char* (WINAPI* PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc);
typedef BOOL (WINAPI* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
typedef HGLRC (WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hShareContext, const int* attribList);
static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = nullptr;
static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = nullptr;
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr;
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x0001
namespace XCEngine {
namespace RHI {
@@ -135,13 +151,60 @@ bool OpenGLDevice::InitializeWithExistingWindow(HWND hwnd) {
return false;
}
m_hglrc = wglCreateContext(m_hdc);
if (!m_hglrc) {
HGLRC tempRC = wglCreateContext(m_hdc);
if (!tempRC) {
ReleaseDC(m_hwnd, m_hdc);
m_hdc = nullptr;
return false;
}
if (!wglMakeCurrent(m_hdc, tempRC)) {
wglDeleteContext(tempRC);
ReleaseDC(m_hwnd, m_hdc);
m_hdc = nullptr;
return false;
}
wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
if (wglGetExtensionsStringARB) {
const char* extensions = wglGetExtensionsStringARB(m_hdc);
if (extensions && strstr(extensions, "WGL_ARB_pixel_format")) {
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
}
if (extensions && strstr(extensions, "WGL_ARB_create_context")) {
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
}
}
wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(tempRC);
if (wglCreateContextAttribsARB) {
int debugAttribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
WGL_CONTEXT_MINOR_VERSION_ARB, 6,
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0, 0
};
m_hglrc = wglCreateContextAttribsARB(m_hdc, nullptr, debugAttribs);
if (m_hglrc) {
XCEngine::Debug::Logger::Get().Info(XCEngine::Debug::LogCategory::General, "Created OpenGL debug context with WGL_ARB_create_context");
}
}
if (!m_hglrc) {
m_hglrc = wglCreateContext(m_hdc);
if (m_hglrc) {
XCEngine::Debug::Logger::Get().Warning(XCEngine::Debug::LogCategory::General, "Created OpenGL context without debug bit (wglCreateContextAttribsARB failed)");
} else {
ReleaseDC(m_hwnd, m_hdc);
m_hdc = nullptr;
return false;
}
}
if (!wglMakeCurrent(m_hdc, m_hglrc)) {
wglDeleteContext(m_hglrc);
ReleaseDC(m_hwnd, m_hdc);
@@ -159,6 +222,14 @@ bool OpenGLDevice::InitializeWithExistingWindow(HWND hwnd) {
return false;
}
GLint contextFlags = 0;
glGetIntegerv(GL_CONTEXT_FLAGS, &contextFlags);
if (contextFlags & GL_CONTEXT_FLAG_DEBUG_BIT) {
XCEngine::Debug::Logger::Get().Info(XCEngine::Debug::LogCategory::General, "OpenGL debug context is active");
} else {
XCEngine::Debug::Logger::Get().Warning(XCEngine::Debug::LogCategory::General, "OpenGL debug context is NOT active");
}
const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));

View File

@@ -11,6 +11,7 @@
#include "XCEngine/RHI/OpenGL/OpenGLScreenshot.h"
#include "XCEngine/Debug/Logger.h"
#include "XCEngine/Debug/ConsoleLogSink.h"
#include "XCEngine/Debug/RenderDocCapture.h"
#include "XCEngine/Containers/String.h"
#pragma comment(lib, "opengl32.lib")
@@ -41,6 +42,10 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
// Set RenderDoc environment variables for global capture
_putenv_s("RENDERDOC_CAPTUREINTERACTIVE", "0");
_putenv_s("RENDERDOC_CAPTUREFRAMESTART", "0");
// Initialize logger
Logger::Get().Initialize();
Logger::Get().AddSink(std::make_unique<ConsoleLogSink>());
@@ -81,6 +86,14 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
return -1;
}
// Initialize RenderDoc capture (MUST be before OpenGL init)
// For OpenGL: device=nullptr (will be set via SetDevice), window=hwnd
if (!RenderDocCapture::Get().Initialize(nullptr, hwnd)) {
Log("[WARNING] Failed to initialize RenderDoc, frame capture will not be available");
} else {
RenderDocCapture::Get().SetCaptureFilePath(".\\minimal_frame30");
}
// Initialize OpenGL device with existing window
OpenGLDevice device;
if (!device.InitializeWithExistingWindow(hwnd)) {
@@ -88,6 +101,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
return -1;
}
// Set device for RenderDoc (must be called after OpenGL init)
// For OpenGL, device pointer must be HGLRC, not HDC!
RenderDocCapture::Get().SetDevice(device.GetContext());
ShowWindow(hwnd, nShowCmd);
UpdateWindow(hwnd);
@@ -115,6 +132,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
TranslateMessage(&msg);
DispatchMessageW(&msg);
} else {
if (frameCount == targetFrameCount - 1) {
Log("[INFO] Starting RenderDoc capture at frame %d", frameCount + 1);
wglMakeCurrent(device.GetDC(), device.GetContext());
RenderDocCapture::Get().BeginCapture("OpenGL_Minimal_Test");
Log("[INFO] IsCapturing after BeginCapture: %s", RenderDocCapture::Get().IsCapturing() ? "true" : "false");
}
// Set viewport and clear color using encapsulated command list
commandList.SetViewport(0, 0, gWidth, gHeight);
commandList.Clear(1.0f, 0.0f, 0.0f, 1.0f, 1 | 2);
@@ -122,14 +146,21 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
// Present the rendered frame
swapChain.Present(0, 0);
frameCount++;
if (frameCount >= targetFrameCount) {
Log("[INFO] Ending RenderDoc capture at frame %d", frameCount);
RenderDocCapture::Get().EndCapture();
Log("[INFO] NumCaptures: %u", RenderDocCapture::Get().GetNumCaptures());
}
}
}
// Take screenshot after target frame count is reached
Log("[INFO] Reached target frame count %d - taking screenshot!", targetFrameCount);
Log("[INFO] Taking screenshot!");
OpenGLScreenshot::Capture(device, swapChain, "minimal.ppm");
// Shutdown in reverse order of initialization
RenderDocCapture::Get().Shutdown();
swapChain.Shutdown();
device.Shutdown();
Logger::Get().Shutdown();