2026-03-16 16:07:12 +08:00
|
|
|
#define GLFW_INCLUDE_NONE
|
2026-03-16 17:22:45 +08:00
|
|
|
#include "XCEngine/RHI/OpenGL/OpenGLDevice.h"
|
2026-03-18 02:29:12 +08:00
|
|
|
#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"
|
2026-03-16 16:07:12 +08:00
|
|
|
#include <glad/glad.h>
|
|
|
|
|
#include <GLFW/glfw3.h>
|
|
|
|
|
|
|
|
|
|
namespace XCEngine {
|
|
|
|
|
namespace RHI {
|
|
|
|
|
|
|
|
|
|
OpenGLDevice::OpenGLDevice()
|
|
|
|
|
: m_window(nullptr)
|
|
|
|
|
, m_initialized(false)
|
|
|
|
|
, m_ownsWindow(false) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OpenGLDevice::~OpenGLDevice() {
|
|
|
|
|
Shutdown();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 17:45:01 +08:00
|
|
|
bool OpenGLDevice::Initialize(const RHIDeviceDesc& desc) {
|
|
|
|
|
if (m_initialized) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (desc.windowHandle) {
|
|
|
|
|
return InitializeWithExistingWindow(static_cast<GLFWwindow*>(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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 17:22:45 +08:00
|
|
|
bool OpenGLDevice::CreateRenderWindow(int width, int height, const char* title, bool enableDebug) {
|
2026-03-16 16:07:12 +08:00
|
|
|
if (m_initialized) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 18:06:57 +08:00
|
|
|
static bool glfwInitialized = false;
|
|
|
|
|
if (!glfwInitialized) {
|
|
|
|
|
glfwInit();
|
|
|
|
|
glfwInitialized = true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 16:07:12 +08:00
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
|
|
|
|
|
|
|
|
m_window = glfwCreateWindow(width, height, title, nullptr, nullptr);
|
|
|
|
|
if (!m_window) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 18:06:57 +08:00
|
|
|
glfwSetWindowShouldClose(m_window, GLFW_FALSE);
|
|
|
|
|
|
2026-03-16 16:07:12 +08:00
|
|
|
m_ownsWindow = true;
|
|
|
|
|
return InitializeWithExistingWindow(m_window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool OpenGLDevice::InitializeWithExistingWindow(GLFWwindow* window) {
|
|
|
|
|
if (m_initialized) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_window = window;
|
|
|
|
|
if (!m_window) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glfwMakeContextCurrent(m_window);
|
|
|
|
|
|
|
|
|
|
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 19:35:51 +08:00
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
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);
|
2026-03-16 16:07:12 +08:00
|
|
|
|
2026-03-17 19:44:50 +08:00
|
|
|
GLint majorVersion = 0, minorVersion = 0;
|
|
|
|
|
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
|
|
|
|
|
glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
|
|
|
|
|
m_deviceInfo.majorVersion = static_cast<uint32_t>(majorVersion);
|
|
|
|
|
m_deviceInfo.minorVersion = static_cast<uint32_t>(minorVersion);
|
|
|
|
|
|
2026-03-18 02:36:40 +08:00
|
|
|
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<uint32_t>(maxTexSize);
|
|
|
|
|
|
|
|
|
|
GLint maxCubeSize = 0;
|
|
|
|
|
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeSize);
|
|
|
|
|
m_capabilities.maxTextureCubeSize = static_cast<uint32_t>(maxCubeSize);
|
|
|
|
|
|
|
|
|
|
GLint maxRenderTargets = 0;
|
|
|
|
|
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxRenderTargets);
|
|
|
|
|
m_capabilities.maxRenderTargets = static_cast<uint32_t>(maxRenderTargets);
|
|
|
|
|
m_capabilities.maxColorAttachments = static_cast<uint32_t>(maxRenderTargets);
|
|
|
|
|
|
|
|
|
|
GLint maxViewports = 0;
|
|
|
|
|
glGetIntegerv(GL_MAX_VIEWPORTS, &maxViewports);
|
|
|
|
|
m_capabilities.maxViewports = static_cast<uint32_t>(maxViewports);
|
|
|
|
|
|
|
|
|
|
GLint maxAnisotropy = 0;
|
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &maxAnisotropy);
|
|
|
|
|
m_capabilities.maxAnisotropy = static_cast<uint32_t>(maxAnisotropy);
|
|
|
|
|
|
|
|
|
|
GLint maxAttribs = 0;
|
|
|
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttribs);
|
|
|
|
|
m_capabilities.maxVertexAttribs = static_cast<uint32_t>(maxAttribs);
|
|
|
|
|
|
2026-03-16 16:07:12 +08:00
|
|
|
m_initialized = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OpenGLDevice::Shutdown() {
|
|
|
|
|
if (m_ownsWindow && m_window) {
|
|
|
|
|
glfwDestroyWindow(m_window);
|
|
|
|
|
}
|
|
|
|
|
m_window = nullptr;
|
|
|
|
|
m_initialized = false;
|
|
|
|
|
m_ownsWindow = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OpenGLDevice::SwapBuffers() {
|
|
|
|
|
if (m_window) {
|
|
|
|
|
glfwSwapBuffers(m_window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool OpenGLDevice::PollEvents() {
|
|
|
|
|
glfwPollEvents();
|
|
|
|
|
return !glfwWindowShouldClose(m_window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OpenGLDevice::SetShouldClose(bool shouldClose) {
|
|
|
|
|
if (m_window) {
|
|
|
|
|
glfwSetWindowShouldClose(m_window, shouldClose ? GLFW_TRUE : GLFW_FALSE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool OpenGLDevice::ShouldClose() const {
|
|
|
|
|
return m_window && glfwWindowShouldClose(m_window) == GLFW_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 17:45:01 +08:00
|
|
|
RHIBuffer* OpenGLDevice::CreateBuffer(const BufferDesc& desc) {
|
2026-03-18 02:29:12 +08:00
|
|
|
auto* buffer = new OpenGLBuffer();
|
|
|
|
|
OpenGLBufferType bufferType = OpenGLBufferType::Vertex;
|
|
|
|
|
|
|
|
|
|
switch (desc.usage) {
|
|
|
|
|
case ResourceUsage::IndexBuffer:
|
|
|
|
|
bufferType = OpenGLBufferType::Index;
|
|
|
|
|
break;
|
|
|
|
|
case ResourceUsage::ConstantBuffer:
|
|
|
|
|
bufferType = OpenGLBufferType::Uniform;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
bufferType = OpenGLBufferType::Vertex;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer->Initialize(bufferType, desc.size, nullptr, desc.cpuAccess == CPUAccess::Write);
|
|
|
|
|
return buffer;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RHITexture* OpenGLDevice::CreateTexture(const TextureDesc& desc) {
|
2026-03-18 02:29:12 +08:00
|
|
|
auto* texture = new OpenGLTexture();
|
|
|
|
|
|
|
|
|
|
OpenGLTextureType type = OpenGLTextureType::Texture2D;
|
|
|
|
|
switch (desc.type) {
|
|
|
|
|
case TextureType::Texture1D:
|
|
|
|
|
type = OpenGLTextureType::Texture1D;
|
|
|
|
|
break;
|
|
|
|
|
case TextureType::Texture3D:
|
|
|
|
|
type = OpenGLTextureType::Texture3D;
|
|
|
|
|
break;
|
|
|
|
|
case TextureType::TextureCube:
|
|
|
|
|
type = OpenGLTextureType::TextureCube;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
type = OpenGLTextureType::Texture2D;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OpenGLFormat format = OpenGLFormat::RGBA8;
|
|
|
|
|
texture->Initialize(type, desc.width, desc.height, desc.depth, desc.mipLevels, format, nullptr);
|
|
|
|
|
return texture;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RHISwapChain* OpenGLDevice::CreateSwapChain(const SwapChainDesc& desc) {
|
2026-03-18 02:29:12 +08:00
|
|
|
auto* swapChain = new OpenGLSwapChain();
|
|
|
|
|
if (m_window) {
|
|
|
|
|
swapChain->Initialize(m_window, desc.width, desc.height);
|
|
|
|
|
}
|
|
|
|
|
return swapChain;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RHICommandList* OpenGLDevice::CreateCommandList(const CommandListDesc& desc) {
|
2026-03-18 02:29:12 +08:00
|
|
|
auto* cmdList = new OpenGLCommandList();
|
|
|
|
|
return cmdList;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RHICommandQueue* OpenGLDevice::CreateCommandQueue(const CommandQueueDesc& desc) {
|
2026-03-18 02:29:12 +08:00
|
|
|
auto* queue = new OpenGLCommandQueue();
|
|
|
|
|
return queue;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RHIShader* OpenGLDevice::CompileShader(const ShaderCompileDesc& desc) {
|
2026-03-18 02:29:12 +08:00
|
|
|
auto* shader = new OpenGLShader();
|
|
|
|
|
if (desc.sourceData && desc.sourceSize > 0) {
|
|
|
|
|
shader->Compile(static_cast<const char*>(desc.sourceData), desc.sourceSize);
|
|
|
|
|
} else if (desc.filePath) {
|
|
|
|
|
shader->CompileFromFile(desc.filePath);
|
|
|
|
|
}
|
|
|
|
|
return shader;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RHIPipelineState* OpenGLDevice::CreatePipelineState(const PipelineStateDesc& desc) {
|
2026-03-18 02:29:12 +08:00
|
|
|
auto* pso = new OpenGLPipelineState();
|
|
|
|
|
return pso;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RHIFence* OpenGLDevice::CreateFence(const FenceDesc& desc) {
|
2026-03-18 02:29:12 +08:00
|
|
|
auto* fence = new OpenGLFence();
|
|
|
|
|
fence->Initialize(desc.initialValue > 0);
|
|
|
|
|
return fence;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RHISampler* OpenGLDevice::CreateSampler(const SamplerDesc& desc) {
|
2026-03-18 02:29:12 +08:00
|
|
|
auto* sampler = new OpenGLSampler();
|
|
|
|
|
OpenGLSamplerDesc samplerDesc = {};
|
|
|
|
|
sampler->Initialize(samplerDesc);
|
|
|
|
|
return sampler;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const RHICapabilities& OpenGLDevice::GetCapabilities() const {
|
|
|
|
|
return m_capabilities;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const RHIDeviceInfo& OpenGLDevice::GetDeviceInfo() const {
|
2026-03-17 19:35:51 +08:00
|
|
|
return m_deviceInfo;
|
2026-03-17 17:45:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void* OpenGLDevice::GetNativeDevice() {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void* OpenGLDevice::GetNativeHandle() const {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 16:07:12 +08:00
|
|
|
} // namespace RHI
|
|
|
|
|
} // namespace XCEngine
|