From c47e871c5a4ce3ff88aad56bec8bb3d04ba2a8e2 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Thu, 26 Mar 2026 02:07:21 +0800 Subject: [PATCH] Fix OpenGL device initialization and file shaders --- .../XCEngine/RHI/OpenGL/OpenGLDevice.h | 6 + engine/src/RHI/OpenGL/OpenGLDevice.cpp | 159 +++++++++++++++--- engine/src/RHI/OpenGL/OpenGLShader.cpp | 104 +++++++++++- tests/RHI/unit/test_device.cpp | 45 ++++- tests/RHI/unit/test_shader.cpp | 38 ++++- 5 files changed, 310 insertions(+), 42 deletions(-) diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLDevice.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLDevice.h index 90572e6b..6f9bdf5c 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLDevice.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLDevice.h @@ -76,12 +76,18 @@ public: private: friend class OpenGLSwapChain; + bool AttachWindow(HWND hwnd); + bool ConfigureWindowPixelFormat(HDC hdc); + bool CreateContextForCurrentWindow(); + void DestroyOwnedWindow(); + HDC GetPresentationDC() const { return m_hdc; } OpenGLTextureUnitAllocator* GetTextureUnitAllocator() { return m_textureUnitAllocator.get(); } OpenGLUniformBufferManager* GetUniformBufferManager() { return m_uniformBufferManager.get(); } void SwapBuffers(); HWND m_hwnd = nullptr; + HWND m_ownedWindow = nullptr; HDC m_hdc = nullptr; HGLRC m_hglrc = nullptr; RHIDeviceInfo m_deviceInfo; diff --git a/engine/src/RHI/OpenGL/OpenGLDevice.cpp b/engine/src/RHI/OpenGL/OpenGLDevice.cpp index 47ece74f..b1790191 100644 --- a/engine/src/RHI/OpenGL/OpenGLDevice.cpp +++ b/engine/src/RHI/OpenGL/OpenGLDevice.cpp @@ -38,6 +38,7 @@ static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr; #define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x0001 +#define XCE_OPENGL_HIDDEN_WINDOW_CLASS L"XCEngineOpenGLHiddenWindow" namespace XCEngine { namespace RHI { @@ -120,6 +121,31 @@ FramebufferAttachmentType ResolveDepthAttachmentType(Format format) { : FramebufferAttachmentType::Depth; } +HWND CreateHiddenOpenGLWindow() { + WNDCLASSEXW wc = {}; + wc.cbSize = sizeof(WNDCLASSEXW); + wc.style = CS_OWNDC; + wc.lpfnWndProc = DefWindowProcW; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = XCE_OPENGL_HIDDEN_WINDOW_CLASS; + + const ATOM atom = RegisterClassExW(&wc); + if (atom == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { + return nullptr; + } + + return CreateWindowExW( + 0, + XCE_OPENGL_HIDDEN_WINDOW_CLASS, + L"XCEngineOpenGLHiddenWindow", + WS_OVERLAPPED, + 0, 0, 1, 1, + nullptr, + nullptr, + wc.hInstance, + nullptr); +} + } // namespace OpenGLDevice::OpenGLDevice() @@ -136,28 +162,92 @@ OpenGLDevice::~OpenGLDevice() { } bool OpenGLDevice::Initialize(const RHIDeviceDesc& desc) { + (void)desc; if (m_initialized) { return true; } - return false; + m_ownedWindow = CreateHiddenOpenGLWindow(); + if (!m_ownedWindow) { + return false; + } + + if (!AttachWindow(m_ownedWindow)) { + DestroyOwnedWindow(); + return false; + } + + return true; } bool OpenGLDevice::InitializeWithExistingWindow(HWND hwnd) { if (m_initialized) { - return true; + return hwnd == m_hwnd ? true : AttachWindow(hwnd); } if (!hwnd) { return false; } - m_hwnd = hwnd; - m_hdc = ::GetDC(m_hwnd); - if (!m_hdc) { + return AttachWindow(hwnd); +} + +bool OpenGLDevice::AttachWindow(HWND hwnd) { + if (!hwnd) { return false; } + HDC newDC = ::GetDC(hwnd); + if (!newDC) { + return false; + } + + if (!ConfigureWindowPixelFormat(newDC)) { + ReleaseDC(hwnd, newDC); + return false; + } + + if (m_hglrc != nullptr) { + if (!wglMakeCurrent(newDC, m_hglrc)) { + ReleaseDC(hwnd, newDC); + return false; + } + + if (m_hdc && m_hwnd) { + ReleaseDC(m_hwnd, m_hdc); + } + + m_hwnd = hwnd; + m_hdc = newDC; + + if (m_ownedWindow != nullptr && m_ownedWindow != hwnd) { + DestroyOwnedWindow(); + } + + return true; + } + + m_hwnd = hwnd; + m_hdc = newDC; + if (!CreateContextForCurrentWindow()) { + ReleaseDC(hwnd, newDC); + m_hdc = nullptr; + m_hwnd = nullptr; + return false; + } + + return true; +} + +bool OpenGLDevice::ConfigureWindowPixelFormat(HDC hdc) { + if (!hdc) { + return false; + } + + if (GetPixelFormat(hdc) != 0) { + return true; + } + PIXELFORMATDESCRIPTOR pfd = {}; pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; @@ -168,23 +258,25 @@ bool OpenGLDevice::InitializeWithExistingWindow(HWND hwnd) { pfd.cStencilBits = 8; pfd.iLayerType = PFD_MAIN_PLANE; - int pixelFormat = ChoosePixelFormat(m_hdc, &pfd); + int pixelFormat = ChoosePixelFormat(hdc, &pfd); if (!pixelFormat) { - ReleaseDC(m_hwnd, m_hdc); - m_hdc = nullptr; return false; } - if (!SetPixelFormat(m_hdc, pixelFormat, &pfd)) { - ReleaseDC(m_hwnd, m_hdc); - m_hdc = nullptr; + if (!SetPixelFormat(hdc, pixelFormat, &pfd)) { + return false; + } + + return true; +} + +bool OpenGLDevice::CreateContextForCurrentWindow() { + if (m_hdc == nullptr || m_hwnd == nullptr) { return false; } HGLRC tempRC = wglCreateContext(m_hdc); if (!tempRC) { - ReleaseDC(m_hwnd, m_hdc); - m_hdc = nullptr; return false; } @@ -229,26 +321,20 @@ bool OpenGLDevice::InitializeWithExistingWindow(HWND hwnd) { 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); m_hglrc = nullptr; - m_hdc = nullptr; return false; } if (!gladLoadGL()) { wglMakeCurrent(nullptr, nullptr); wglDeleteContext(m_hglrc); - ReleaseDC(m_hwnd, m_hdc); m_hglrc = nullptr; - m_hdc = nullptr; return false; } @@ -328,9 +414,11 @@ void OpenGLDevice::Shutdown() { if (m_hdc && m_hwnd) { ReleaseDC(m_hwnd, m_hdc); m_hdc = nullptr; - m_hwnd = nullptr; } + m_hwnd = nullptr; + DestroyOwnedWindow(); + if (m_uniformBufferManager) { m_uniformBufferManager->Shutdown(); } @@ -348,6 +436,13 @@ bool OpenGLDevice::MakeContextCurrent() { return false; } +void OpenGLDevice::DestroyOwnedWindow() { + if (m_ownedWindow != nullptr) { + DestroyWindow(m_ownedWindow); + m_ownedWindow = nullptr; + } +} + void OpenGLDevice::SwapBuffers() { if (m_hdc) { ::SwapBuffers(m_hdc); @@ -451,7 +546,7 @@ RHISwapChain* OpenGLDevice::CreateSwapChain(const SwapChainDesc& desc, RHIComman auto* swapChain = new OpenGLSwapChain(); HWND hwnd = static_cast(desc.windowHandle); - if (hwnd && swapChain->Initialize(this, hwnd, desc.width, desc.height)) { + if (hwnd && InitializeWithExistingWindow(hwnd) && swapChain->Initialize(this, hwnd, desc.width, desc.height)) { return swapChain; } @@ -471,12 +566,17 @@ RHICommandQueue* OpenGLDevice::CreateCommandQueue(const CommandQueueDesc& desc) RHIShader* OpenGLDevice::CreateShader(const ShaderCompileDesc& desc) { auto* shader = new OpenGLShader(); - + + if (!MakeContextCurrent()) { + delete shader; + return nullptr; + } + if (desc.sourceLanguage == ShaderLanguage::GLSL && !desc.source.empty()) { const char* sourceStr = reinterpret_cast(desc.source.data()); ShaderType shaderType = ShaderType::Vertex; - - std::string profile(desc.profile.begin(), desc.profile.end()); + + std::string profile = NarrowAscii(desc.profile); if (profile.find("vs") != std::string::npos) { shaderType = ShaderType::Vertex; } else if (profile.find("ps") != std::string::npos || profile.find("fs") != std::string::npos) { @@ -496,10 +596,13 @@ RHIShader* OpenGLDevice::CreateShader(const ShaderCompileDesc& desc) { if (!desc.fileName.empty()) { std::wstring filePath = desc.fileName; - std::string entryPoint(desc.entryPoint.begin(), desc.entryPoint.end()); - std::string profile(desc.profile.begin(), desc.profile.end()); - shader->CompileFromFile(filePath.c_str(), entryPoint.c_str(), profile.c_str()); - return shader; + std::string entryPoint = NarrowAscii(desc.entryPoint); + std::string profile = NarrowAscii(desc.profile); + if (shader->CompileFromFile(filePath.c_str(), entryPoint.c_str(), profile.c_str())) { + return shader; + } + delete shader; + return nullptr; } delete shader; diff --git a/engine/src/RHI/OpenGL/OpenGLShader.cpp b/engine/src/RHI/OpenGL/OpenGLShader.cpp index ac4c52e4..48d93c33 100644 --- a/engine/src/RHI/OpenGL/OpenGLShader.cpp +++ b/engine/src/RHI/OpenGL/OpenGLShader.cpp @@ -3,10 +3,85 @@ #include #include #include +#include namespace XCEngine { namespace RHI { +namespace { + +bool EndsWith(const std::string& value, const char* suffix) { + const size_t suffixLength = strlen(suffix); + return value.size() >= suffixLength && value.compare(value.size() - suffixLength, suffixLength, suffix) == 0; +} + +std::string NarrowAscii(const std::wstring& value) { + std::string result; + result.reserve(value.size()); + for (wchar_t ch : value) { + result.push_back(static_cast(ch)); + } + return result; +} + +bool ResolveShaderType(const std::string& path, const char* target, ShaderType& type) { + if (target != nullptr) { + if (strstr(target, "vs_") != nullptr) { + type = ShaderType::Vertex; + return true; + } + if (strstr(target, "ps_") != nullptr || strstr(target, "fs_") != nullptr) { + type = ShaderType::Fragment; + return true; + } + if (strstr(target, "gs_") != nullptr) { + type = ShaderType::Geometry; + return true; + } + if (strstr(target, "cs_") != nullptr) { + type = ShaderType::Compute; + return true; + } + if (strstr(target, "hs_") != nullptr || strstr(target, "tcs") != nullptr) { + type = ShaderType::TessControl; + return true; + } + if (strstr(target, "ds_") != nullptr || strstr(target, "tes") != nullptr) { + type = ShaderType::TessEvaluation; + return true; + } + } + + if (EndsWith(path, ".vert") || EndsWith(path, ".vs.glsl")) { + type = ShaderType::Vertex; + return true; + } + if (EndsWith(path, ".frag") || EndsWith(path, ".fs.glsl")) { + type = ShaderType::Fragment; + return true; + } + if (EndsWith(path, ".geom") || EndsWith(path, ".gs.glsl")) { + type = ShaderType::Geometry; + return true; + } + if (EndsWith(path, ".comp") || EndsWith(path, ".cs.glsl")) { + type = ShaderType::Compute; + return true; + } + if (EndsWith(path, ".tesc")) { + type = ShaderType::TessControl; + return true; + } + if (EndsWith(path, ".tese")) { + type = ShaderType::TessEvaluation; + return true; + } + + return false; +} + +} // namespace + OpenGLShader::OpenGLShader() : m_program(0), m_uniformsCached(false) { } @@ -211,9 +286,30 @@ bool OpenGLShader::Compile(const char* source, ShaderType type) { } bool OpenGLShader::CompileFromFile(const wchar_t* filePath, const char* entryPoint, const char* target) { - std::wstring ws(filePath); - std::string path(ws.begin(), ws.end()); - return CompileFromFile(path.c_str(), nullptr); + (void)entryPoint; + + if (filePath == nullptr) { + return false; + } + + const std::wstring ws(filePath); + const std::string path = NarrowAscii(ws); + + std::ifstream shaderFile(path); + if (!shaderFile.is_open()) { + return false; + } + + std::stringstream shaderStream; + shaderStream << shaderFile.rdbuf(); + + ShaderType type = ShaderType::Vertex; + if (!ResolveShaderType(path, target, type)) { + return false; + } + + const std::string source = shaderStream.str(); + return Compile(source.c_str(), type); } bool OpenGLShader::Compile(const void* sourceData, size_t sourceSize, const char* entryPoint, const char* target) { @@ -340,4 +436,4 @@ bool OpenGLShader::CheckLinkErrors(unsigned int program) { } } // namespace RHI -} // namespace XCEngine \ No newline at end of file +} // namespace XCEngine diff --git a/tests/RHI/unit/test_device.cpp b/tests/RHI/unit/test_device.cpp index d21466fe..bdb19258 100644 --- a/tests/RHI/unit/test_device.cpp +++ b/tests/RHI/unit/test_device.cpp @@ -11,21 +11,48 @@ TEST_P(RHITestFixture, Device_Initialize_Shutdown) { RHIDevice* device = RHIFactory::CreateRHIDevice(GetBackendType()); ASSERT_NE(device, nullptr); - bool initResult = false; - if (GetBackendType() == RHIType::D3D12) { - RHIDeviceDesc desc = {}; - desc.enableDebugLayer = true; - initResult = device->Initialize(desc); - } else if (GetBackendType() == RHIType::OpenGL) { - auto* oglDevice = static_cast(device); - initResult = oglDevice->InitializeWithExistingWindow(GetWindowHandle()); - } + RHIDeviceDesc desc = {}; + desc.enableDebugLayer = true; + const bool initResult = device->Initialize(desc); ASSERT_TRUE(initResult); device->Shutdown(); delete device; } +TEST_P(RHITestFixture, Device_Initialize_OpenGLUnifiedPath_CanCreateSwapChain) { + if (GetBackendType() != RHIType::OpenGL) { + GTEST_SKIP() << "OpenGL-specific unified initialization verification"; + } + + auto* device = new OpenGLDevice(); + ASSERT_NE(device, nullptr); + + RHIDeviceDesc deviceDesc = {}; + ASSERT_TRUE(device->Initialize(deviceDesc)); + + CommandQueueDesc queueDesc = {}; + queueDesc.queueType = static_cast(CommandQueueType::Direct); + RHICommandQueue* queue = device->CreateCommandQueue(queueDesc); + ASSERT_NE(queue, nullptr); + + SwapChainDesc swapDesc = {}; + swapDesc.windowHandle = GetWindowHandle(); + swapDesc.width = 320; + swapDesc.height = 180; + RHISwapChain* swapChain = device->CreateSwapChain(swapDesc, queue); + ASSERT_NE(swapChain, nullptr); + + swapChain->Present(0, 0); + + swapChain->Shutdown(); + delete swapChain; + queue->Shutdown(); + delete queue; + device->Shutdown(); + delete device; +} + TEST_P(RHITestFixture, Device_GetCapabilities_ReturnsValid) { const auto& caps = GetDevice()->GetCapabilities(); diff --git a/tests/RHI/unit/test_shader.cpp b/tests/RHI/unit/test_shader.cpp index cba1805c..290b7213 100644 --- a/tests/RHI/unit/test_shader.cpp +++ b/tests/RHI/unit/test_shader.cpp @@ -140,4 +140,40 @@ TEST_P(RHITestFixture, Shader_Shutdown_Invalidates) { EXPECT_FALSE(shader->IsValid()); delete shader; } -} \ No newline at end of file +} + +TEST_P(RHITestFixture, Shader_Compile_FromFile_ReturnsValidShader) { + ShaderCompileDesc desc = {}; + if (GetBackendType() == RHIType::D3D12) { + desc.fileName = L"tests/RHI/D3D12/integration/quad/Res/Shader/quad.hlsl"; + desc.entryPoint = L"MainVS"; + desc.profile = L"vs_5_0"; + } else { + desc.fileName = L"tests/RHI/OpenGL/integration/triangle/Res/Shader/triangle.vert"; + desc.entryPoint = L"main"; + desc.profile = L"vs_4_30"; + } + + RHIShader* shader = GetDevice()->CreateShader(desc); + ASSERT_NE(shader, nullptr); + EXPECT_TRUE(shader->IsValid()); + EXPECT_EQ(shader->GetType(), ShaderType::Vertex); + shader->Shutdown(); + delete shader; +} + +TEST_P(RHITestFixture, Shader_Compile_MissingFile_ReturnsNullptr) { + ShaderCompileDesc desc = {}; + if (GetBackendType() == RHIType::D3D12) { + desc.fileName = L"tests/RHI/D3D12/integration/quad/Res/Shader/does_not_exist.hlsl"; + desc.entryPoint = L"MainVS"; + desc.profile = L"vs_5_0"; + } else { + desc.fileName = L"tests/RHI/OpenGL/integration/triangle/Res/Shader/does_not_exist.vert"; + desc.entryPoint = L"main"; + desc.profile = L"vs_4_30"; + } + + RHIShader* shader = GetDevice()->CreateShader(desc); + EXPECT_EQ(shader, nullptr); +}