Create GL context abstraction layer

This commit is contained in:
Doug Johnson 2016-05-12 01:32:18 -06:00
parent 69e818555d
commit ce10d9bd02
3 changed files with 26 additions and 292 deletions

View File

@ -23,6 +23,10 @@ extern "C" GLEWContext* glewGetContext();
// required. // required.
typedef struct WGLEWContextStruct WGLEWContext; typedef struct WGLEWContextStruct WGLEWContext;
extern "C" WGLEWContext* wglewGetContext(); extern "C" WGLEWContext* wglewGetContext();
#endif // XE_PLATFORM_WIN32 #elif XE_PLATFORM_LINUX
typedef struct GLXEWContextStruct GLXEWContext;
extern "C" GLXEWContext* glxewGetContext();
#endif
#endif // XENIA_UI_GL_GL_H_ #endif // XENIA_UI_GL_GL_H_

View File

@ -17,13 +17,10 @@
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/platform_win.h"
#include "xenia/base/profiling.h" #include "xenia/base/profiling.h"
#include "xenia/ui/gl/gl_immediate_drawer.h" #include "xenia/ui/gl/gl_immediate_drawer.h"
#include "xenia/ui/window.h" #include "xenia/ui/window.h"
// TODO(benvanik): move win32 code to _win?
#include "third_party/GL/wglew.h"
DEFINE_bool(thread_safe_gl, false, DEFINE_bool(thread_safe_gl, false,
"Only allow one GL context to be active at a time."); "Only allow one GL context to be active at a time.");
@ -43,14 +40,10 @@ namespace xe {
namespace ui { namespace ui {
namespace gl { namespace gl {
static std::recursive_mutex global_gl_mutex_;
thread_local GLEWContext* tls_glew_context_ = nullptr; std::recursive_mutex GLContext::global_gl_mutex_;
thread_local WGLEWContext* tls_wglew_context_ = nullptr;
extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; }
extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; }
void FatalGLError(std::string error) { void GLContext::FatalGLError(std::string error) {
xe::FatalError( xe::FatalError(
error + error +
"\nEnsure you have the latest drivers for your GPU and that it supports " "\nEnsure you have the latest drivers for your GPU and that it supports "
@ -58,223 +51,11 @@ void FatalGLError(std::string error) {
"of supported GPUs."); "of supported GPUs.");
} }
std::unique_ptr<GLContext> GLContext::Create(GraphicsProvider* provider,
Window* target_window,
GLContext* share_context) {
auto context =
std::unique_ptr<GLContext>(new GLContext(provider, target_window));
if (!context->Initialize(share_context)) {
return nullptr;
}
context->AssertExtensionsPresent();
return context;
}
GLContext::GLContext(GraphicsProvider* provider, Window* target_window) GLContext::GLContext(GraphicsProvider* provider, Window* target_window)
: GraphicsContext(provider, target_window) { : GraphicsContext(provider, target_window) { }
glew_context_.reset(new GLEWContext());
wglew_context_.reset(new WGLEWContext());
}
GLContext::~GLContext() {
MakeCurrent();
blitter_.Shutdown();
immediate_drawer_.reset();
ClearCurrent();
if (glrc_) {
wglDeleteContext(glrc_);
}
if (dc_) {
ReleaseDC(HWND(target_window_->native_handle()), dc_);
}
}
bool GLContext::Initialize(GLContext* share_context) {
dc_ = GetDC(HWND(target_window_->native_handle()));
PIXELFORMATDESCRIPTOR pfd = {0};
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 32;
pfd.iLayerType = PFD_MAIN_PLANE;
int pixel_format = ChoosePixelFormat(dc_, &pfd);
if (!pixel_format) {
FatalGLError("Unable to choose pixel format.");
return false;
}
if (!SetPixelFormat(dc_, pixel_format, &pfd)) {
FatalGLError("Unable to set pixel format.");
return false;
}
HGLRC temp_context = wglCreateContext(dc_);
if (!temp_context) {
FatalGLError("Unable to create temporary GL context.");
return false;
}
wglMakeCurrent(dc_, temp_context);
tls_glew_context_ = glew_context_.get();
tls_wglew_context_ = wglew_context_.get();
if (glewInit() != GLEW_OK) {
FatalGLError("Unable to initialize GLEW.");
return false;
}
if (wglewInit() != GLEW_OK) {
FatalGLError("Unable to initialize WGLEW.");
return false;
}
if (!WGLEW_ARB_create_context) {
FatalGLError("WGL_ARG_create_context not supported by GL ICD.");
return false;
}
if (GLEW_ARB_robustness) {
robust_access_supported_ = true;
}
int context_flags = 0;
if (FLAGS_gl_debug) {
context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
}
if (robust_access_supported_) {
context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
}
int attrib_list[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB,
4,
WGL_CONTEXT_MINOR_VERSION_ARB,
5,
WGL_CONTEXT_FLAGS_ARB,
context_flags,
WGL_CONTEXT_PROFILE_MASK_ARB,
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
robust_access_supported_ ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0,
0};
glrc_ = wglCreateContextAttribsARB(
dc_, share_context ? share_context->glrc_ : nullptr, attrib_list);
wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(temp_context);
if (!glrc_) {
FatalGLError("Unable to create real GL context.");
return false;
}
if (!MakeCurrent()) {
FatalGLError("Could not make real GL context current.");
return false;
}
XELOGI("Successfully created OpenGL context:");
XELOGI(" GL_VENDOR: %s", glGetString(GL_VENDOR));
XELOGI(" GL_VERSION: %s", glGetString(GL_VERSION));
XELOGI(" GL_RENDERER: %s", glGetString(GL_RENDERER));
XELOGI(" GL_SHADING_LANGUAGE_VERSION: %s",
glGetString(GL_SHADING_LANGUAGE_VERSION));
while (glGetError()) {
// Clearing errors.
}
SetupDebugging();
if (!blitter_.Initialize()) {
FatalGLError("Unable to initialize blitter.");
ClearCurrent();
return false;
}
immediate_drawer_ = std::make_unique<GLImmediateDrawer>(this);
ClearCurrent();
return true;
}
std::unique_ptr<GLContext> GLContext::CreateOffscreen(
GraphicsProvider* provider, GLContext* parent_context) {
assert_not_null(parent_context->glrc_);
HGLRC new_glrc = nullptr;
{
GraphicsContextLock context_lock(parent_context);
int context_flags = 0;
if (FLAGS_gl_debug) {
context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
}
bool robust_access_supported = parent_context->robust_access_supported_;
if (robust_access_supported) {
context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
}
int attrib_list[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB,
4,
WGL_CONTEXT_MINOR_VERSION_ARB,
5,
WGL_CONTEXT_FLAGS_ARB,
context_flags,
WGL_CONTEXT_PROFILE_MASK_ARB,
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
robust_access_supported ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0,
0};
new_glrc = wglCreateContextAttribsARB(parent_context->dc_,
parent_context->glrc_, attrib_list);
if (!new_glrc) {
FatalGLError("Could not create shared context.");
return nullptr;
}
}
auto new_context = std::unique_ptr<GLContext>(
new GLContext(provider, parent_context->target_window_));
new_context->glrc_ = new_glrc;
new_context->dc_ =
GetDC(HWND(parent_context->target_window_->native_handle()));
new_context->robust_access_supported_ =
parent_context->robust_access_supported_;
if (!new_context->MakeCurrent()) {
FatalGLError("Could not make new GL context current.");
return nullptr;
}
if (!glGetString(GL_EXTENSIONS)) {
new_context->ClearCurrent();
FatalGLError("New GL context did not have extensions.");
return nullptr;
}
if (glewInit() != GLEW_OK) {
new_context->ClearCurrent();
FatalGLError("Unable to initialize GLEW on shared context.");
return nullptr;
}
if (wglewInit() != GLEW_OK) {
new_context->ClearCurrent();
FatalGLError("Unable to initialize WGLEW on shared context.");
return nullptr;
}
new_context->SetupDebugging();
if (!new_context->blitter_.Initialize()) {
FatalGLError("Unable to initialize blitter on shared context.");
return nullptr;
}
new_context->ClearCurrent();
return new_context;
}
void GLContext::AssertExtensionsPresent() { void GLContext::AssertExtensionsPresent() {
if (!MakeCurrent()) { if (!MakeCurrent()) {
@ -436,39 +217,7 @@ ImmediateDrawer* GLContext::immediate_drawer() {
return immediate_drawer_.get(); return immediate_drawer_.get();
} }
bool GLContext::is_current() {
return tls_glew_context_ == glew_context_.get();
}
bool GLContext::MakeCurrent() {
SCOPE_profile_cpu_f("gpu");
if (FLAGS_thread_safe_gl) {
global_gl_mutex_.lock();
}
if (!wglMakeCurrent(dc_, glrc_)) {
if (FLAGS_thread_safe_gl) {
global_gl_mutex_.unlock();
}
FatalGLError("Unable to make GL context current.");
return false;
}
tls_glew_context_ = glew_context_.get();
tls_wglew_context_ = wglew_context_.get();
return true;
}
void GLContext::ClearCurrent() {
if (!FLAGS_disable_gl_context_reset) {
wglMakeCurrent(nullptr, nullptr);
}
tls_glew_context_ = nullptr;
tls_wglew_context_ = nullptr;
if (FLAGS_thread_safe_gl) {
global_gl_mutex_.unlock();
}
}
bool GLContext::WasLost() { bool GLContext::WasLost() {
if (!robust_access_supported_) { if (!robust_access_supported_) {
@ -484,7 +233,7 @@ bool GLContext::WasLost() {
if (status != GL_NO_ERROR) { if (status != GL_NO_ERROR) {
// Graphics card reset. // Graphics card reset.
XELOGE("============= TDR detected on context %p! Context %s =============", XELOGE("============= TDR detected on context %p! Context %s =============",
glrc_, status == GL_GUILTY_CONTEXT_RESET ? "guilty" : "innocent"); handle(), status == GL_GUILTY_CONTEXT_RESET ? "guilty" : "innocent");
context_lost_ = true; context_lost_ = true;
return true; return true;
} }
@ -492,24 +241,6 @@ bool GLContext::WasLost() {
return false; return false;
} }
void GLContext::BeginSwap() {
SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLContext::BeginSwap");
float clear_color[] = {238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.0f};
if (FLAGS_random_clear_color) {
clear_color[0] =
rand() / static_cast<float>(RAND_MAX); // NOLINT(runtime/threadsafe_fn)
clear_color[1] = 1.0f;
clear_color[2] = 0.0f;
clear_color[3] = 1.0f;
}
glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
}
void GLContext::EndSwap() {
SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLContext::EndSwap");
SwapBuffers(dc_);
}
std::unique_ptr<RawImage> GLContext::Capture() { std::unique_ptr<RawImage> GLContext::Capture() {
GraphicsContextLock lock(this); GraphicsContextLock lock(this);

View File

@ -13,6 +13,7 @@
#include <gflags/gflags.h> #include <gflags/gflags.h>
#include <memory> #include <memory>
#include <mutex>
#include "xenia/ui/gl/blitter.h" #include "xenia/ui/gl/blitter.h"
#include "xenia/ui/gl/gl.h" #include "xenia/ui/gl/gl.h"
@ -44,11 +45,25 @@ class GLContext : public GraphicsContext {
void BeginSwap() override; void BeginSwap() override;
void EndSwap() override; void EndSwap() override;
std::unique_ptr<RawImage> Capture() override; std::unique_ptr<RawImage> Capture() override;
Blitter* blitter() { return &blitter_; } Blitter* blitter() { return &blitter_; }
protected:
Blitter blitter_;
std::unique_ptr<GLImmediateDrawer> immediate_drawer_;
static std::recursive_mutex global_gl_mutex_;
bool context_lost_ = false;
bool robust_access_supported_ = false;
static void FatalGLError(std::string error);
virtual bool Initialize(GLContext* share_context) = 0;
virtual void* handle() = 0;
GLContext(GraphicsProvider* provider, Window* target_window);
void SetupDebugging();
void AssertExtensionsPresent();
void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar* message);
private: private:
friend class GLProvider; friend class GLProvider;
@ -59,31 +74,15 @@ class GLContext : public GraphicsContext {
GLContext* parent_context); GLContext* parent_context);
private: private:
GLContext(GraphicsProvider* provider, Window* target_window);
bool Initialize(GLContext* share_context);
void AssertExtensionsPresent();
void SetupDebugging();
void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar* message);
static void GLAPIENTRY DebugMessageThunk(GLenum source, GLenum type, static void GLAPIENTRY DebugMessageThunk(GLenum source, GLenum type,
GLuint id, GLenum severity, GLuint id, GLenum severity,
GLsizei length, GLsizei length,
const GLchar* message, const GLchar* message,
GLvoid* user_param); GLvoid* user_param);
HDC dc_ = nullptr;
HGLRC glrc_ = nullptr;
std::unique_ptr<GLEWContext> glew_context_;
std::unique_ptr<WGLEWContext> wglew_context_;
Blitter blitter_;
std::unique_ptr<GLImmediateDrawer> immediate_drawer_;
bool context_lost_ = false;
bool robust_access_supported_ = false;
}; };
} // namespace gl } // namespace gl