#define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #include #include "XCEngine/RHI/OpenGL/OpenGLDevice.h" #include "XCEngine/RHI/OpenGL/OpenGLBuffer.h" #include "XCEngine/RHI/OpenGL/OpenGLTexture.h" #include "XCEngine/RHI/OpenGL/OpenGLShader.h" #include "XCEngine/RHI/OpenGL/OpenGLPipelineState.h" #include "XCEngine/RHI/OpenGL/OpenGLFence.h" #include "XCEngine/RHI/OpenGL/OpenGLSampler.h" #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 { OpenGLDevice::OpenGLDevice() : m_hwnd(nullptr) , m_hdc(nullptr) , m_hglrc(nullptr) , m_initialized(false) , m_ownsWindow(false) , m_shouldClose(false) { } OpenGLDevice::~OpenGLDevice() { Shutdown(); } bool OpenGLDevice::Initialize(const RHIDeviceDesc& desc) { if (m_initialized) { return true; } if (desc.windowHandle) { return InitializeWithExistingWindow(static_cast(desc.windowHandle)); } std::string titleStr = "XCEngine"; if (!desc.appName.empty()) { titleStr = std::string(desc.appName.begin(), desc.appName.end()); } return CreateRenderWindow(desc.width, desc.height, titleStr.c_str(), desc.enableDebugLayer); } bool OpenGLDevice::CreateRenderWindow(int width, int height, const char* title, bool enableDebug) { if (m_initialized) { return true; } if (!s_windowClassRegistered) { WNDCLASSEXW wc = {}; wc.cbSize = sizeof(WNDCLASSEXW); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = DefWindowProcW; wc.hInstance = GetModuleHandleW(nullptr); wc.lpszClassName = kWindowClassName; if (!RegisterClassExW(&wc)) { return false; } s_windowClassRegistered = true; } std::wstring titleW(title ? std::wstring(title, title + strlen(title)) : L"XCEngine"); HWND hwnd = CreateWindowExW( 0, kWindowClassName, titleW.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, nullptr, nullptr, GetModuleHandleW(nullptr), nullptr ); if (!hwnd) { return false; } m_hwnd = hwnd; m_ownsWindow = true; ShowWindow(m_hwnd, SW_SHOWNORMAL); UpdateWindow(m_hwnd); return InitializeWithExistingWindow(m_hwnd); } bool OpenGLDevice::InitializeWithExistingWindow(HWND hwnd) { if (m_initialized) { return true; } if (!hwnd) { return false; } m_hwnd = hwnd; m_hdc = ::GetDC(m_hwnd); if (!m_hdc) { return false; } PIXELFORMATDESCRIPTOR pfd = {}; pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 32; pfd.cDepthBits = 24; pfd.cStencilBits = 8; pfd.iLayerType = PFD_MAIN_PLANE; int pixelFormat = ChoosePixelFormat(m_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; return false; } 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); 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; } 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)); m_deviceInfo.vendor = std::wstring(vendor ? vendor : "", vendor ? vendor + strlen(vendor) : nullptr); m_deviceInfo.renderer = std::wstring(renderer ? renderer : "", renderer ? renderer + strlen(renderer) : nullptr); m_deviceInfo.version = std::wstring(version ? version : "", version ? version + strlen(version) : nullptr); GLint majorVersion = 0, minorVersion = 0; glGetIntegerv(GL_MAJOR_VERSION, &majorVersion); glGetIntegerv(GL_MINOR_VERSION, &minorVersion); m_deviceInfo.majorVersion = static_cast(majorVersion); m_deviceInfo.minorVersion = static_cast(minorVersion); m_capabilities.majorVersion = majorVersion; m_capabilities.minorVersion = minorVersion; m_capabilities.bSupportsGeometryShaders = true; m_capabilities.bSupportsComputeShaders = GLVersion.major >= 4 && GLVersion.minor >= 3; m_capabilities.bSupportsTessellation = GLVersion.major >= 4 && GLVersion.minor >= 1; m_capabilities.bSupportsExplicitMultiThreading = false; GLint maxTexSize = 0; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); m_capabilities.maxTexture2DSize = static_cast(maxTexSize); GLint maxCubeSize = 0; glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeSize); m_capabilities.maxTextureCubeSize = static_cast(maxCubeSize); GLint maxRenderTargets = 0; glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxRenderTargets); m_capabilities.maxRenderTargets = static_cast(maxRenderTargets); m_capabilities.maxColorAttachments = static_cast(maxRenderTargets); GLint maxViewports = 0; glGetIntegerv(GL_MAX_VIEWPORTS, &maxViewports); m_capabilities.maxViewports = static_cast(maxViewports); GLint maxAnisotropy = 0; glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &maxAnisotropy); m_capabilities.maxAnisotropy = static_cast(maxAnisotropy); GLint maxAttribs = 0; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttribs); m_capabilities.maxVertexAttribs = static_cast(maxAttribs); m_initialized = true; return true; } void OpenGLDevice::Shutdown() { if (m_hglrc) { wglMakeCurrent(nullptr, nullptr); wglDeleteContext(m_hglrc); m_hglrc = nullptr; } if (m_hdc && m_hwnd) { ReleaseDC(m_hwnd, m_hdc); m_hdc = nullptr; } if (m_ownsWindow && m_hwnd) { DestroyWindow(m_hwnd); m_hwnd = nullptr; } m_initialized = false; m_ownsWindow = false; m_shouldClose = false; } void OpenGLDevice::SwapBuffers() { if (m_hdc) { ::SwapBuffers(m_hdc); } } bool OpenGLDevice::PollEvents() { MSG msg; while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { m_shouldClose = true; return false; } TranslateMessage(&msg); DispatchMessageW(&msg); } return !m_shouldClose; } void OpenGLDevice::SetShouldClose(bool shouldClose) { m_shouldClose = shouldClose; if (m_hwnd && shouldClose) { PostMessageW(m_hwnd, WM_CLOSE, 0, 0); } } bool OpenGLDevice::ShouldClose() const { return m_shouldClose; } RHIBuffer* OpenGLDevice::CreateBuffer(const BufferDesc& desc) { auto* buffer = new OpenGLBuffer(); OpenGLBufferType bufferType = OpenGLBufferType::Vertex; switch (desc.bufferType) { case 1: bufferType = OpenGLBufferType::Index; break; case 2: bufferType = OpenGLBufferType::Uniform; break; default: bufferType = OpenGLBufferType::Vertex; break; } buffer->Initialize(bufferType, desc.size, nullptr, false); buffer->SetStride(desc.stride); return buffer; } RHITexture* OpenGLDevice::CreateTexture(const TextureDesc& desc) { auto* texture = new OpenGLTexture(); OpenGLTextureType type = OpenGLTextureType::Texture2D; switch (desc.textureType) { case 0: type = OpenGLTextureType::Texture1D; break; case 2: type = OpenGLTextureType::Texture2DArray; break; case 3: type = OpenGLTextureType::Texture3D; break; case 4: type = OpenGLTextureType::TextureCube; break; default: type = OpenGLTextureType::Texture2D; break; } OpenGLFormat format = OpenGLFormat::RGBA8; switch (desc.format) { case 1: format = OpenGLFormat::R8; break; case 2: format = OpenGLFormat::RG8; break; case 3: format = OpenGLFormat::RGBA8; break; case 4: format = OpenGLFormat::RGBA16F; break; case 5: format = OpenGLFormat::RGBA32F; break; case 6: format = OpenGLFormat::RGBA16F; break; case 7: format = OpenGLFormat::RGBA32F; break; case 8: format = OpenGLFormat::Depth24Stencil8; break; case 9: format = OpenGLFormat::Depth32F; break; default: format = OpenGLFormat::RGBA8; break; } texture->Initialize(type, desc.width, desc.height, desc.depth, desc.mipLevels, format, nullptr); texture->SetFormat(static_cast(desc.format)); return texture; } RHISwapChain* OpenGLDevice::CreateSwapChain(const SwapChainDesc& desc) { auto* swapChain = new OpenGLSwapChain(); if (m_hwnd) { swapChain->Initialize(m_hwnd, desc.width, desc.height); } return swapChain; } RHICommandList* OpenGLDevice::CreateCommandList(const CommandListDesc& desc) { auto* cmdList = new OpenGLCommandList(); return cmdList; } RHICommandQueue* OpenGLDevice::CreateCommandQueue(const CommandQueueDesc& desc) { auto* queue = new OpenGLCommandQueue(); return queue; } RHIShader* OpenGLDevice::CompileShader(const ShaderCompileDesc& desc) { std::wstring filePath = desc.fileName; if (filePath.empty()) { return nullptr; } auto* shader = new OpenGLShader(); 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; } RHIPipelineState* OpenGLDevice::CreatePipelineState(const PipelineStateDesc& desc) { auto* pso = new OpenGLPipelineState(); return pso; } RHIFence* OpenGLDevice::CreateFence(const FenceDesc& desc) { auto* fence = new OpenGLFence(); fence->Initialize(desc.initialValue > 0); return fence; } RHISampler* OpenGLDevice::CreateSampler(const SamplerDesc& desc) { auto* sampler = new OpenGLSampler(); OpenGLSamplerDesc samplerDesc = {}; sampler->Initialize(samplerDesc); return sampler; } const RHICapabilities& OpenGLDevice::GetCapabilities() const { return m_capabilities; } const RHIDeviceInfo& OpenGLDevice::GetDeviceInfo() const { return m_deviceInfo; } void* OpenGLDevice::GetNativeDevice() { return nullptr; } void* OpenGLDevice::GetNativeHandle() const { return nullptr; } } // namespace RHI } // namespace XCEngine