Files
FlaxEngine/Source/Engine/GraphicsDevice/OpenGL/GPUDeviceOGL.cpp
2020-12-07 23:40:54 +01:00

316 lines
7.5 KiB
C++

// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "Engine/Graphics/Config.h"
#if GRAPHICS_API_OPENGL
#include "GPUDeviceOGL.h"
#include "GPUResourcesFactoryOGL.h"
#include "GPULimitsOGL.h"
#include "ContextOGL.h"
#include "Win32/Win32ContextOGL.h"
#include "Engine/Core/Log.h"
#include "Engine/Render2D/Render2D.h"
#include "Engine/Graphics/RenderTask.h"
#include <regex>
GPUDeviceOGL::BlendDesc GPUDeviceOGL::BlendModes[5] =
{
// Opaque rendering (default)
{
false,
GL_ONE, GL_ZERO, GL_FUNC_ADD,
GL_ONE, GL_ZERO, GL_FUNC_ADD,
},
// Additive rendering
{
true,
GL_SRC_ALPHA, GL_ONE, GL_FUNC_ADD,
GL_SRC_ALPHA, GL_ONE, GL_FUNC_ADD,
},
// Alpha blended rendering
{
true,
GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_FUNC_ADD,
GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_FUNC_ADD,
},
// Add color rendering
{
true,
GL_ONE, GL_ONE, GL_FUNC_ADD,
GL_ONE, GL_ONE, GL_FUNC_ADD,
},
// Multiply output color with texture color
{
true,
GL_ZERO, GL_SRC_COLOR, GL_FUNC_ADD,
GL_ZERO, GL_SRC_ALPHA, GL_FUNC_ADD,
},
};
#define DEFINE_GL_ENTRYPOINTS(Type,Func) Type Func = NULL;
ENUM_GL_ENTRYPOINTS_ALL(DEFINE_GL_ENTRYPOINTS);
GPUDeviceOGL::GPUDeviceOGL(RendererType type, ShaderProfile profile, AdapterOGL* adapter, GPULimits* limits)
: GPUDevice(type, profile, limits, New<GPUResourcesFactoryOGL>(this))
, _adapter(adapter)
{
}
GPUDeviceOGL* GPUDeviceOGL::Create()
{
#define CHECK_NULL(obj) if (obj == nullptr) { return nullptr; }
bool GRunningUnderRenderDoc = false;
// Create a dummy context so that wglCreateContextAttribsARB can be initialized
Win32ContextOGL::Data DummyContext;
Win32ContextOGL::CreateDummyGLWindow(&DummyContext);
DummyContext.OpenGLContext = wglCreateContext(DummyContext.DeviceContext);
CHECK_NULL(DummyContext.OpenGLContext);
ASSERT(DummyContext.OpenGLContext);
Win32ContextOGL::ContextMakeCurrent(DummyContext.DeviceContext, DummyContext.OpenGLContext);
Win32ContextOGL::wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)GetGLFuncAddress("wglCreateContextAttribsARB");
CHECK_NULL(DummyContext.OpenGLContext);
Win32ContextOGL::ContextMakeCurrent(NULL, NULL);
wglDeleteContext(DummyContext.OpenGLContext);
#if GRAPHICS_API_OPENGLES
todo_support-opengles
#else
// Try to create OpenGL 4.4 context
Win32ContextOGL::PlatformCreateOpenGLContextCore(&DummyContext, 4, 4, NULL);
if (DummyContext.OpenGLContext == nullptr)
{
// Try to create OpenGL 4.1 context
Win32ContextOGL::PlatformCreateOpenGLContextCore(&DummyContext, 4, 1, NULL);
if (DummyContext.OpenGLContext == nullptr)
{
LOG(Error, "OpenGL 4.1 is not supported by the driver.");
return nullptr;
}
}
#endif
Win32ContextOGL::ContextMakeCurrent(DummyContext.DeviceContext, DummyContext.OpenGLContext);
// Get all OpenGL functions from the OpenGL DLL
{
// Retrieve the OpenGL DLL
void* OpenGLDLL = Platform::GetDllHandle(TEXT("opengl32.dll"));
if (!OpenGLDLL)
{
LOG(Error, "Couldn't load opengl32.dll");
return nullptr;
}
// Initialize entry points required from opengl32.dll
#define GET_GL_ENTRYPOINTS_DLL(Type,Func) Func = (Type)Platform::GetDllExport(OpenGLDLL, #Func);
ENUM_GL_ENTRYPOINTS_DLL(GET_GL_ENTRYPOINTS_DLL);
// Release the OpenGL DLL
Platform::FreeDllHandle(OpenGLDLL);
// Initialize all entry points required by Flax
#define GET_GL_ENTRYPOINTS(Type,Func) Func = (Type)wglGetProcAddress(#Func);
ENUM_GL_ENTRYPOINTS(GET_GL_ENTRYPOINTS);
ENUM_GL_ENTRYPOINTS_OPTIONAL(GET_GL_ENTRYPOINTS);
// Check that all of the entry points have been initialized
bool isMissing = false;
#define CHECK_GL_ENTRYPOINTS(Type,Func) if (Func == NULL) { isMissing = true; LOG(Warning, "Failed to find entry point for {0}", TEXT(#Func)); }
ENUM_GL_ENTRYPOINTS_DLL(CHECK_GL_ENTRYPOINTS);
ENUM_GL_ENTRYPOINTS(CHECK_GL_ENTRYPOINTS);
if (isMissing)
{
LOG(Error, "Failed to find all OpenGL entry points.");
return nullptr;
}
}
// Create adapter
auto adapter = New<AdapterOGL>();
if (adapter->Init(DummyContext.DeviceContext))
{
Delete(adapter);
LOG(Error, "Failed to init OpenGL adapter.");
return nullptr;
}
// Create device
GPUDeviceOGL* device = nullptr;
#if GRAPHICS_API_OPENGLES
{
todo_support_opengles
//device = New<GPUDeviceOGLES3>(adapter);
}
#else
{
if (adapter->Version >= 440)
device = New<GPUDeviceOGL4_4>(adapter);
else
device = New<GPUDeviceOGL4_1>(adapter);
}
#endif
if (device->Init())
{
LOG(Warning, "Graphics Device init failed");
Delete(device);
return nullptr;
}
return device;
#undef CHECK_NULL
}
bool GPUDeviceOGL::Init()
{
// Base
if (GPUDevice::Init())
return true;
_state = DeviceState::Created;
// Init device limits
if (Limits->Init())
{
LOG(Warning, "Cannot initialize device limits.");
return true;
}
// Create main context
_mainContext = New<GPUContextOGL>(this);
// TODO: create vertex buffer for the quad drawing?
// Finished
_state = DeviceState::Inited;
return false;
}
bool GPUDeviceOGL::CanDraw()
{
return GPUDevice::CanDraw() && ContextOGL::IsReady();
}
GPUDeviceOGL::~GPUDeviceOGL()
{
// Ensure to be disposed
GPUDeviceOGL::Dispose();
}
#if GPU_OGL_USE_DEBUG_LAYER
void OpenGlErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
{
if (type != GL_DEBUG_TYPE_PERFORMANCE && type != GL_DEBUG_TYPE_OTHER)
{
LOG(Warning, "OpenGL error: {0}", (const char*)message);
}
}
#endif
void GPUDeviceOGL::InitWithMainContext()
{
#if GPU_OGL_USE_DEBUG_LAYER
if (_adapter->HasExtension("GL_ARB_debug_output"))
{
glDebugMessageCallbackARB(&OpenGlErrorCallback, 0);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
#endif
// Init some OpenGL states
glFrontFace(GL_CW);
// Intel HD4000 under <= 10.8.4 requires GL_DITHER disabled or dithering will occur on any channel < 8bits.
// No other driver does this but we don't need GL_DITHER on anyway.
glDisable(GL_DITHER);
// Render targets with sRGB flag should do sRGB conversion like in D3D11
glEnable(GL_FRAMEBUFFER_SRGB);
// Engine always expects seamless cubemap, so enable it if available
if (GetLimits()->SupportsSeamlessCubemap)
{
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
#if PLATFORM_WINDOWS || PLATFORM_LINUX
if (GetLimits()->SupportsClipControl)
{
glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE);
}
#endif
}
void GPUDeviceOGL::Dispose()
{
GPUDeviceLock lock(this);
// Check if has been disposed already
if (_state == DeviceState::Disposed)
return;
// Set current state
_state = DeviceState::Disposing;
// Wait for rendering end
WaitForGPU();
// Pre dispose
preDispose();
// Clear device resources
FBOCache.Dispose();
VAOCache.Dispose();
// Clear OpenGL stuff
SAFE_DELETE(_mainContext);
SAFE_DELETE(_adapter);
// Base
GPUDevice::Dispose();
// Set current state
_state = DeviceState::Disposed;
}
void GPUDeviceOGL::WaitForGPU()
{
// Note: in OpenGL driver manages CPU/GPU work synchronization and work submission
}
#if GRAPHICS_API_OPENGLES
GPUDeviceOGLES3::GPUDeviceOGLES3(AdapterOGL* adapter)
: GPUDeviceOGL(RendererType::OpenGLES3, ShaderProfile::Unknown, adapter, New<GPULimitsOGL>(this))
{
}
GPUDeviceOGLES3_1::GPUDeviceOGLES3_1(AdapterOGL* adapter)
: GPUDeviceOGL(RendererType::OpenGLES3_1, ShaderProfile::Unknown, adapter, New<GPULimitsOGL>(this))
{
}
#else
GPUDeviceOGL4_1::GPUDeviceOGL4_1(AdapterOGL* adapter)
: GPUDeviceOGL(RendererType::OpenGL4_1, ShaderProfile::GLSL_410, adapter, New<GPULimitsOGL>(this))
{
}
GPUDeviceOGL4_4::GPUDeviceOGL4_4(AdapterOGL* adapter)
: GPUDeviceOGL(RendererType::OpenGL4_4, ShaderProfile::GLSL_440, adapter, New<GPULimitsOGL>(this))
{
}
#endif
#endif