Massive refactoring of xenia::ui and GL swap behavior.

This seems to dramatically improve most games (especially with
--vsync=false), though it may cause swap issues with others.
New code should be easier to port, and enables elemental-forms to be
drawn for any emulator UI.
This commit is contained in:
Ben Vanik
2015-07-12 22:03:47 -07:00
parent e69c7b00a8
commit 15c17459be
61 changed files with 1994 additions and 2623 deletions

View File

@@ -11,7 +11,6 @@
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
namespace xe {
namespace ui {

View File

@@ -28,7 +28,7 @@ GL4ElementalRenderer::GL4Bitmap::GL4Bitmap(GLContext* context,
: context_(context), renderer_(renderer) {}
GL4ElementalRenderer::GL4Bitmap::~GL4Bitmap() {
GLContextLock lock(context_);
GraphicsContextLock lock(context_);
// Must flush and unbind before we delete the texture.
renderer_->FlushBitmap(this);
@@ -69,7 +69,7 @@ GL4ElementalRenderer::GL4ElementalRenderer(GLContext* context)
vertex_buffer_(max_vertex_batch_size() * sizeof(Vertex)) {}
GL4ElementalRenderer::~GL4ElementalRenderer() {
GLContextLock lock(context_);
GraphicsContextLock lock(context_);
vertex_buffer_.Shutdown();
glDeleteVertexArrays(1, &vao_);
glDeleteProgram(program_);
@@ -77,6 +77,7 @@ GL4ElementalRenderer::~GL4ElementalRenderer() {
std::unique_ptr<GL4ElementalRenderer> GL4ElementalRenderer::Create(
GLContext* context) {
GraphicsContextLock lock(context);
auto renderer = std::make_unique<GL4ElementalRenderer>(context);
if (!renderer->Initialize()) {
XELOGE("Failed to initialize TurboBadger GL4 renderer");

View File

@@ -16,6 +16,9 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/profiling.h"
#include "xenia/ui/gl/gl_profiler_display.h"
#include "xenia/ui/gl/gl4_elemental_renderer.h"
#include "xenia/ui/window.h"
DEFINE_bool(thread_safe_gl, false,
"Only allow one GL context to be active at a time.");
@@ -40,11 +43,20 @@ thread_local WGLEWContext* tls_wglew_context_ = nullptr;
extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; }
extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; }
GLContext::GLContext() : hwnd_(nullptr), dc_(nullptr), glrc_(nullptr) {}
std::unique_ptr<GLContext> GLContext::Create(Window* target_window) {
auto context = std::unique_ptr<GLContext>(new GLContext(target_window));
if (!context->Initialize(target_window)) {
return nullptr;
}
context->AssertExtensionsPresent();
return context;
}
GLContext::GLContext(HWND hwnd, HGLRC glrc)
: hwnd_(hwnd), dc_(nullptr), glrc_(glrc) {
dc_ = GetDC(hwnd);
GLContext::GLContext(Window* target_window) : GraphicsContext(target_window) {}
GLContext::GLContext(Window* target_window, HGLRC glrc)
: GraphicsContext(target_window), glrc_(glrc) {
dc_ = GetDC(HWND(target_window_->native_handle()));
}
GLContext::~GLContext() {
@@ -55,13 +67,13 @@ GLContext::~GLContext() {
wglDeleteContext(glrc_);
}
if (dc_) {
ReleaseDC(hwnd_, dc_);
ReleaseDC(HWND(target_window_->native_handle()), dc_);
}
}
bool GLContext::Initialize(HWND hwnd) {
hwnd_ = hwnd;
dc_ = GetDC(hwnd);
bool GLContext::Initialize(Window* target_window) {
target_window_ = target_window;
dc_ = GetDC(HWND(target_window_->native_handle()));
PIXELFORMATDESCRIPTOR pfd = {0};
pfd.nSize = sizeof(pfd);
@@ -152,12 +164,12 @@ bool GLContext::Initialize(HWND hwnd) {
return true;
}
std::unique_ptr<GLContext> GLContext::CreateShared() {
std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
assert_not_null(glrc_);
HGLRC new_glrc = nullptr;
{
GLContextLock context_lock(this);
GraphicsContextLock context_lock(this);
int context_flags = 0;
// int profile = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
@@ -178,7 +190,8 @@ std::unique_ptr<GLContext> GLContext::CreateShared() {
}
}
auto new_context = std::make_unique<GLContext>(hwnd_, new_glrc);
auto new_context =
std::unique_ptr<GLContext>(new GLContext(target_window_, new_glrc));
if (!new_context->MakeCurrent()) {
XELOGE("Could not make new GL context current");
return nullptr;
@@ -205,7 +218,7 @@ std::unique_ptr<GLContext> GLContext::CreateShared() {
new_context->ClearCurrent();
return new_context;
return std::unique_ptr<GraphicsContext>(new_context.release());
}
void FatalGLError(std::string error) {
@@ -369,6 +382,14 @@ void GLContext::SetupDebugging() {
this);
}
std::unique_ptr<ProfilerDisplay> GLContext::CreateProfilerDisplay() {
return std::make_unique<GLProfilerDisplay>(target_window_);
}
std::unique_ptr<el::graphics::Renderer> GLContext::CreateElementalRenderer() {
return GL4ElementalRenderer::Create(this);
}
bool GLContext::MakeCurrent() {
SCOPE_profile_cpu_f("gpu");
if (FLAGS_thread_safe_gl) {
@@ -399,6 +420,17 @@ void GLContext::ClearCurrent() {
}
}
void GLContext::BeginSwap() {
SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLContext::BeginSwap");
float clear_color[] = {rand() / (float)RAND_MAX, 1.0f, 0, 1.0f};
glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
}
void GLContext::EndSwap() {
SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLContext::EndSwap");
SwapBuffers(dc());
}
} // namespace gl
} // namespace ui
} // namespace xe

View File

@@ -16,6 +16,7 @@
#include "xenia/ui/gl/blitter.h"
#include "xenia/ui/gl/gl.h"
#include "xenia/ui/graphics_context.h"
DECLARE_bool(thread_safe_gl);
@@ -23,25 +24,33 @@ namespace xe {
namespace ui {
namespace gl {
class GLContext {
class GLContext : public GraphicsContext {
public:
GLContext();
GLContext(HWND hwnd, HGLRC glrc);
~GLContext();
static std::unique_ptr<GLContext> Create(Window* target_window);
bool Initialize(HWND hwnd);
void AssertExtensionsPresent();
~GLContext() override;
HDC dc() const { return dc_; }
std::unique_ptr<GLContext> CreateShared();
std::unique_ptr<GraphicsContext> CreateShared() override;
std::unique_ptr<ProfilerDisplay> CreateProfilerDisplay() override;
std::unique_ptr<el::graphics::Renderer> CreateElementalRenderer() override;
bool MakeCurrent();
void ClearCurrent();
bool MakeCurrent() override;
void ClearCurrent() override;
void BeginSwap() override;
void EndSwap() override;
Blitter* blitter() { return &blitter_; }
private:
GLContext(Window* target_window);
GLContext(Window* target_window, HGLRC glrc);
bool Initialize(Window* target_window);
void AssertExtensionsPresent();
void SetupDebugging();
void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar* message);
@@ -49,9 +58,8 @@ class GLContext {
DebugMessageThunk(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar* message, GLvoid* user_param);
HWND hwnd_;
HDC dc_;
HGLRC glrc_;
HDC dc_ = nullptr;
HGLRC glrc_ = nullptr;
GLEWContext glew_context_;
WGLEWContext wglew_context_;
@@ -59,16 +67,6 @@ class GLContext {
Blitter blitter_;
};
struct GLContextLock {
GLContextLock(GLContext* context) : context_(context) {
context_->MakeCurrent();
}
~GLContextLock() { context_->ClearCurrent(); }
private:
GLContext* context_;
};
} // namespace gl
} // namespace ui
} // namespace xe

View File

@@ -132,47 +132,56 @@ const uint8_t profiler_font[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
GLProfilerDisplay::GLProfilerDisplay(xe::ui::gl::WGLControl* control)
: control_(control),
program_(0),
vao_(0),
font_texture_(0),
font_handle_(0),
GLProfilerDisplay::GLProfilerDisplay(xe::ui::Window* window)
: window_(window),
vertex_buffer_(MICROPROFILE_MAX_VERTICES * sizeof(Vertex) * 10,
sizeof(Vertex)),
draw_command_count_(0) {
sizeof(Vertex)) {
if (!SetupFont() || !SetupState() || !SetupShaders()) {
// Hrm.
assert_always();
}
window_->on_painted.AddListener([this](UIEvent& e) { Profiler::Present(); });
// Pass through mouse events.
control->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
Profiler::OnMouseDown(e.button() == xe::ui::MouseEvent::Button::kLeft,
e.button() == xe::ui::MouseEvent::Button::kRight);
e.set_handled(true);
window_->on_mouse_down.AddListener([](xe::ui::MouseEvent& e) {
if (Profiler::is_enabled()) {
Profiler::OnMouseDown(e.button() == xe::ui::MouseEvent::Button::kLeft,
e.button() == xe::ui::MouseEvent::Button::kRight);
e.set_handled(true);
}
});
control->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
Profiler::OnMouseUp();
e.set_handled(true);
window_->on_mouse_up.AddListener([](xe::ui::MouseEvent& e) {
if (Profiler::is_enabled()) {
Profiler::OnMouseUp();
e.set_handled(true);
}
});
control->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
Profiler::OnMouseMove(e.x(), e.y());
e.set_handled(true);
window_->on_mouse_move.AddListener([](xe::ui::MouseEvent& e) {
if (Profiler::is_enabled()) {
Profiler::OnMouseMove(e.x(), e.y());
e.set_handled(true);
}
});
control->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
Profiler::OnMouseWheel(e.x(), e.y(), -e.dy());
e.set_handled(true);
window_->on_mouse_wheel.AddListener([](xe::ui::MouseEvent& e) {
if (Profiler::is_enabled()) {
Profiler::OnMouseWheel(e.x(), e.y(), -e.dy());
e.set_handled(true);
}
});
// Watch for toggle/mode keys and such.
control->on_key_down.AddListener([](xe::ui::KeyEvent& e) {
Profiler::OnKeyDown(e.key_code());
// e.set_handled(true);
window_->on_key_down.AddListener([](xe::ui::KeyEvent& e) {
if (Profiler::is_enabled()) {
Profiler::OnKeyDown(e.key_code());
e.set_handled(true);
}
});
control->on_key_up.AddListener([](xe::ui::KeyEvent& e) {
Profiler::OnKeyUp(e.key_code());
// e.set_handled(true);
window_->on_key_up.AddListener([](xe::ui::KeyEvent& e) {
if (Profiler::is_enabled()) {
Profiler::OnKeyUp(e.key_code());
e.set_handled(true);
}
});
}
@@ -336,9 +345,9 @@ GLProfilerDisplay::~GLProfilerDisplay() {
glDeleteProgram(program_);
}
uint32_t GLProfilerDisplay::width() const { return control_->width(); }
uint32_t GLProfilerDisplay::width() const { return window_->width(); }
uint32_t GLProfilerDisplay::height() const { return control_->height(); }
uint32_t GLProfilerDisplay::height() const { return window_->height(); }
void GLProfilerDisplay::Begin() {
glEnablei(GL_BLEND, 0);
@@ -347,7 +356,7 @@ void GLProfilerDisplay::Begin() {
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
glViewport(0, 0, control_->width(), control_->height());
glViewport(0, 0, width(), height());
float left = 0.0f;
float right = float(width());

View File

@@ -12,8 +12,7 @@
#include "xenia/profiling.h"
#include "xenia/ui/gl/circular_buffer.h"
#include "xenia/ui/gl/gl_context.h"
#include "xenia/ui/gl/wgl_control.h"
#include "xenia/ui/window.h"
namespace xe {
namespace ui {
@@ -21,7 +20,7 @@ namespace gl {
class GLProfilerDisplay : public ProfilerDisplay {
public:
GLProfilerDisplay(xe::ui::gl::WGLControl* control);
GLProfilerDisplay(xe::ui::Window* window);
virtual ~GLProfilerDisplay();
uint32_t width() const override;
@@ -54,11 +53,11 @@ class GLProfilerDisplay : public ProfilerDisplay {
void EndVertices(GLenum prim_type);
void Flush();
xe::ui::gl::WGLControl* control_;
GLuint program_;
GLuint vao_;
GLuint font_texture_;
GLuint64 font_handle_;
xe::ui::Window* window_ = nullptr;
GLuint program_ = 0;
GLuint vao_ = 0;
GLuint font_texture_ = 0;
GLuint64 font_handle_ = 0;
CircularBuffer vertex_buffer_;
static const size_t kMaxCommands = 32;
@@ -67,13 +66,13 @@ class GLProfilerDisplay : public ProfilerDisplay {
size_t vertex_offset;
size_t vertex_count;
} draw_commands_[kMaxCommands];
uint32_t draw_command_count_;
uint32_t draw_command_count_ = 0;
CircularBuffer::Allocation current_allocation_;
struct {
uint16_t char_offsets[256];
} font_description_;
} font_description_ = {0};
};
} // namespace gl

View File

@@ -1,125 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/gl/wgl_control.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/profiling.h"
namespace xe {
namespace ui {
namespace gl {
WGLControl::WGLControl(Loop* loop)
: xe::ui::win32::Win32Control(Flags::kFlagOwnPaint), loop_(loop) {}
WGLControl::~WGLControl() = default;
bool WGLControl::Create() {
HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = Win32Control::WndProcThunk;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = nullptr;
wcex.hIconSm = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = L"XeniaWglClass";
if (!RegisterClassEx(&wcex)) {
XELOGE("WGL RegisterClassEx failed");
return false;
}
// Create window.
DWORD window_style = WS_CHILD | WS_VISIBLE | SS_NOTIFY;
DWORD window_ex_style = 0;
hwnd_ =
CreateWindowEx(window_ex_style, L"XeniaWglClass", L"Xenia", window_style,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
parent_hwnd(), nullptr, hInstance, this);
if (!hwnd_) {
XELOGE("WGL CreateWindow failed");
return false;
}
if (!context_.Initialize(hwnd_)) {
XEFATAL(
"Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
"you have the latest drivers for your GPU and that it supports OpenGL "
"4.5. See http://xenia.jp/faq/ for more information.");
return false;
}
context_.AssertExtensionsPresent();
SetFocus(hwnd_);
OnCreate();
return true;
}
void WGLControl::OnLayout(UIEvent& e) { Control::ResizeToFill(); }
LRESULT WGLControl::WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
switch (message) {
case WM_PAINT: {
invalidated_ = false;
ValidateRect(hWnd, nullptr);
SCOPE_profile_cpu_i("gpu", "xe::gpu::gl4::WGLControl::WM_PAINT");
{
GLContextLock context_lock(&context_);
float clear_color[] = {rand() / (float)RAND_MAX, 1.0f, 0, 1.0f};
glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
if (current_paint_callback_) {
current_paint_callback_();
current_paint_callback_ = nullptr;
}
UIEvent e(this);
OnPaint(e);
// TODO(benvanik): profiler present.
Profiler::Present();
}
{
SCOPE_profile_cpu_i("gpu", "xe::gpu::gl4::WGLControl::SwapBuffers");
SwapBuffers(context_.dc());
}
return 0;
} break;
}
return Win32Control::WndProc(hWnd, message, wParam, lParam);
}
void WGLControl::SynchronousRepaint(std::function<void()> paint_callback) {
SCOPE_profile_cpu_f("gpu");
// We may already have a pending paint from a previous request when we
// were minimized. We just overwrite it.
current_paint_callback_ = std::move(paint_callback);
// This will not return until the WM_PAINT has completed.
// Note, if we are minimized this won't do anything.
RedrawWindow(hwnd(), nullptr, nullptr,
RDW_INTERNALPAINT | RDW_UPDATENOW | RDW_ALLCHILDREN);
}
} // namespace gl
} // namespace ui
} // namespace xe

View File

@@ -1,51 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_GL_WGL_CONTROL_H_
#define XENIA_UI_GL_WGL_CONTROL_H_
#include <functional>
#include "xenia/base/threading.h"
#include "xenia/ui/gl/gl_context.h"
#include "xenia/ui/loop.h"
#include "xenia/ui/win32/win32_control.h"
namespace xe {
namespace ui {
namespace gl {
class WGLControl : public xe::ui::win32::Win32Control {
public:
WGLControl(Loop* loop);
~WGLControl() override;
GLContext* context() { return &context_; }
void SynchronousRepaint(std::function<void()> paint_callback);
protected:
bool Create() override;
void OnLayout(UIEvent& e) override;
LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) override;
private:
Loop* loop_;
GLContext context_;
std::function<void()> current_paint_callback_;
};
} // namespace gl
} // namespace ui
} // namespace xe
#endif // XENIA_UI_GL_WGL_CONTROL_H_

View File

@@ -1,140 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/gl/wgl_elemental_control.h"
#include <memory>
#include "el/util/math.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/profiling.h"
#include "xenia/ui/gl/circular_buffer.h"
#include "xenia/ui/gl/gl_context.h"
#include "xenia/ui/gl/gl.h"
#include "xenia/ui/gl/gl4_elemental_renderer.h"
namespace xe {
namespace ui {
namespace gl {
WGLElementalControl::WGLElementalControl(Loop* loop)
: ElementalControl(loop, Flags::kFlagOwnPaint), loop_(loop) {}
WGLElementalControl::~WGLElementalControl() = default;
bool WGLElementalControl::Create() {
HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = Win32Control::WndProcThunk;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = nullptr;
wcex.hIconSm = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = L"XeniaWglElementalClass";
if (!RegisterClassEx(&wcex)) {
XELOGE("WGL RegisterClassEx failed");
return false;
}
// Create window.
DWORD window_style = WS_CHILD | WS_VISIBLE | SS_NOTIFY;
DWORD window_ex_style = 0;
hwnd_ =
CreateWindowEx(window_ex_style, L"XeniaWglElementalClass", L"Xenia",
window_style, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, parent_hwnd(), nullptr, hInstance, this);
if (!hwnd_) {
XELOGE("WGL CreateWindow failed");
return false;
}
if (!context_.Initialize(hwnd_)) {
XEFATAL(
"Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
"you have the latest drivers for your GPU and that it supports OpenGL "
"4.5. See http://xenia.jp/faq/ for more information.");
return false;
}
context_.AssertExtensionsPresent();
SetFocus(hwnd_);
return super::Create();
}
std::unique_ptr<el::graphics::Renderer> WGLElementalControl::CreateRenderer() {
return GL4ElementalRenderer::Create(&context_);
}
void WGLElementalControl::OnLayout(UIEvent& e) {
Control::ResizeToFill();
super::OnLayout(e);
}
LRESULT WGLElementalControl::WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
switch (message) {
case WM_PAINT: {
invalidated_ = false;
ValidateRect(hWnd, nullptr);
SCOPE_profile_cpu_i("gpu", "xe::ui::gl::WGLElementalControl::WM_PAINT");
{
GLContextLock context_lock(&context_);
float clear_color[] = {rand() / (float)RAND_MAX, 1.0f, 0, 1.0f};
glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
if (current_paint_callback_) {
current_paint_callback_();
current_paint_callback_ = nullptr;
}
UIEvent e(this);
OnPaint(e);
// TODO(benvanik): profiler present.
Profiler::Present();
}
{
SCOPE_profile_cpu_i("gpu",
"xe::ui::gl::WGLElementalControl::SwapBuffers");
SwapBuffers(context_.dc());
}
return 0;
} break;
}
return Win32Control::WndProc(hWnd, message, wParam, lParam);
}
void WGLElementalControl::SynchronousRepaint(
std::function<void()> paint_callback) {
SCOPE_profile_cpu_f("gpu");
// We may already have a pending paint from a previous request when we
// were minimized. We just overwrite it.
current_paint_callback_ = std::move(paint_callback);
// This will not return until the WM_PAINT has completed.
// Note, if we are minimized this won't do anything.
RedrawWindow(hwnd(), nullptr, nullptr,
RDW_INTERNALPAINT | RDW_UPDATENOW | RDW_ALLCHILDREN);
}
} // namespace gl
} // namespace ui
} // namespace xe

View File

@@ -1,55 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_
#define XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_
#include <functional>
#include "xenia/base/threading.h"
#include "xenia/ui/elemental_control.h"
#include "xenia/ui/gl/gl_context.h"
#include "xenia/ui/loop.h"
namespace xe {
namespace ui {
namespace gl {
class WGLElementalControl : public ElementalControl {
public:
WGLElementalControl(Loop* loop);
~WGLElementalControl() override;
GLContext* context() { return &context_; }
void SynchronousRepaint(std::function<void()> paint_callback);
protected:
using super = ElementalControl;
std::unique_ptr<el::graphics::Renderer> CreateRenderer() override;
bool Create() override;
void OnLayout(UIEvent& e) override;
LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) override;
private:
Loop* loop_;
GLContext context_;
std::function<void()> current_paint_callback_;
};
} // namespace gl
} // namespace ui
} // namespace xe
#endif // XENIA_UI_GL_WGL_ELEMENTAL_CONTROL_H_