From 36683b4bb38a515cbbc1148ff3e79de35cfb2f4b Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Mon, 23 Mar 2026 18:35:49 +0800 Subject: [PATCH] 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' --- engine/src/Debug/RenderDocCapture.cpp | 9 ++- engine/src/RHI/OpenGL/OpenGLDevice.cpp | 75 ++++++++++++++++++- tests/RHI/OpenGL/integration/minimal/main.cpp | 33 +++++++- 3 files changed, 112 insertions(+), 5 deletions(-) diff --git a/engine/src/Debug/RenderDocCapture.cpp b/engine/src/Debug/RenderDocCapture.cpp index bc7b6c29..1e32dfc9 100644 --- a/engine/src/Debug/RenderDocCapture.cpp +++ b/engine/src/Debug/RenderDocCapture.cpp @@ -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() { diff --git a/engine/src/RHI/OpenGL/OpenGLDevice.cpp b/engine/src/RHI/OpenGL/OpenGLDevice.cpp index 9618426e..2f51ee62 100644 --- a/engine/src/RHI/OpenGL/OpenGLDevice.cpp +++ b/engine/src/RHI/OpenGL/OpenGLDevice.cpp @@ -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(glGetString(GL_VENDOR)); const char* renderer = reinterpret_cast(glGetString(GL_RENDERER)); const char* version = reinterpret_cast(glGetString(GL_VERSION)); diff --git a/tests/RHI/OpenGL/integration/minimal/main.cpp b/tests/RHI/OpenGL/integration/minimal/main.cpp index c63a139e..f73d6347 100644 --- a/tests/RHI/OpenGL/integration/minimal/main.cpp +++ b/tests/RHI/OpenGL/integration/minimal/main.cpp @@ -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()); @@ -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();