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:
@@ -122,14 +122,19 @@ void RenderDocCapture::BeginCapture(const char* title) {
|
|||||||
if (title) {
|
if (title) {
|
||||||
m_api->SetCaptureTitle(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() {
|
void RenderDocCapture::EndCapture() {
|
||||||
if (!m_isLoaded || !m_api) {
|
if (!m_isLoaded || !m_api) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_api->EndFrameCapture(m_device, nullptr);
|
m_api->EndFrameCapture(m_device, m_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderDocCapture::TriggerCapture() {
|
void RenderDocCapture::TriggerCapture() {
|
||||||
|
|||||||
@@ -14,10 +14,26 @@
|
|||||||
#include "XCEngine/RHI/OpenGL/OpenGLCommandList.h"
|
#include "XCEngine/RHI/OpenGL/OpenGLCommandList.h"
|
||||||
#include "XCEngine/RHI/OpenGL/OpenGLCommandQueue.h"
|
#include "XCEngine/RHI/OpenGL/OpenGLCommandQueue.h"
|
||||||
#include "XCEngine/RHI/OpenGL/OpenGLSwapChain.h"
|
#include "XCEngine/RHI/OpenGL/OpenGLSwapChain.h"
|
||||||
|
#include "XCEngine/Debug/Logger.h"
|
||||||
|
|
||||||
static bool s_windowClassRegistered = false;
|
static bool s_windowClassRegistered = false;
|
||||||
static const wchar_t kWindowClassName[] = L"XCEngine_OpenGL_WindowClass";
|
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 XCEngine {
|
||||||
namespace RHI {
|
namespace RHI {
|
||||||
|
|
||||||
@@ -135,13 +151,60 @@ bool OpenGLDevice::InitializeWithExistingWindow(HWND hwnd) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hglrc = wglCreateContext(m_hdc);
|
HGLRC tempRC = wglCreateContext(m_hdc);
|
||||||
if (!m_hglrc) {
|
if (!tempRC) {
|
||||||
ReleaseDC(m_hwnd, m_hdc);
|
ReleaseDC(m_hwnd, m_hdc);
|
||||||
m_hdc = nullptr;
|
m_hdc = nullptr;
|
||||||
return false;
|
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)) {
|
if (!wglMakeCurrent(m_hdc, m_hglrc)) {
|
||||||
wglDeleteContext(m_hglrc);
|
wglDeleteContext(m_hglrc);
|
||||||
ReleaseDC(m_hwnd, m_hdc);
|
ReleaseDC(m_hwnd, m_hdc);
|
||||||
@@ -159,6 +222,14 @@ bool OpenGLDevice::InitializeWithExistingWindow(HWND hwnd) {
|
|||||||
return false;
|
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* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||||
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
||||||
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "XCEngine/RHI/OpenGL/OpenGLScreenshot.h"
|
#include "XCEngine/RHI/OpenGL/OpenGLScreenshot.h"
|
||||||
#include "XCEngine/Debug/Logger.h"
|
#include "XCEngine/Debug/Logger.h"
|
||||||
#include "XCEngine/Debug/ConsoleLogSink.h"
|
#include "XCEngine/Debug/ConsoleLogSink.h"
|
||||||
|
#include "XCEngine/Debug/RenderDocCapture.h"
|
||||||
#include "XCEngine/Containers/String.h"
|
#include "XCEngine/Containers/String.h"
|
||||||
|
|
||||||
#pragma comment(lib, "opengl32.lib")
|
#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) {
|
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
|
// Initialize logger
|
||||||
Logger::Get().Initialize();
|
Logger::Get().Initialize();
|
||||||
Logger::Get().AddSink(std::make_unique<ConsoleLogSink>());
|
Logger::Get().AddSink(std::make_unique<ConsoleLogSink>());
|
||||||
@@ -81,6 +86,14 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
return -1;
|
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
|
// Initialize OpenGL device with existing window
|
||||||
OpenGLDevice device;
|
OpenGLDevice device;
|
||||||
if (!device.InitializeWithExistingWindow(hwnd)) {
|
if (!device.InitializeWithExistingWindow(hwnd)) {
|
||||||
@@ -88,6 +101,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
return -1;
|
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);
|
ShowWindow(hwnd, nShowCmd);
|
||||||
UpdateWindow(hwnd);
|
UpdateWindow(hwnd);
|
||||||
|
|
||||||
@@ -115,6 +132,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessageW(&msg);
|
DispatchMessageW(&msg);
|
||||||
} else {
|
} 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
|
// Set viewport and clear color using encapsulated command list
|
||||||
commandList.SetViewport(0, 0, gWidth, gHeight);
|
commandList.SetViewport(0, 0, gWidth, gHeight);
|
||||||
commandList.Clear(1.0f, 0.0f, 0.0f, 1.0f, 1 | 2);
|
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
|
// Present the rendered frame
|
||||||
swapChain.Present(0, 0);
|
swapChain.Present(0, 0);
|
||||||
frameCount++;
|
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
|
// 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");
|
OpenGLScreenshot::Capture(device, swapChain, "minimal.ppm");
|
||||||
|
|
||||||
// Shutdown in reverse order of initialization
|
// Shutdown in reverse order of initialization
|
||||||
|
RenderDocCapture::Get().Shutdown();
|
||||||
swapChain.Shutdown();
|
swapChain.Shutdown();
|
||||||
device.Shutdown();
|
device.Shutdown();
|
||||||
Logger::Get().Shutdown();
|
Logger::Get().Shutdown();
|
||||||
|
|||||||
Reference in New Issue
Block a user