Adding generic immediate mode drawing API and migrating microprofile.
Future changes will move elemental-forms and imgui to the common API as well.
This commit is contained in:
parent
dc04baeb3f
commit
c631b965d7
|
@ -26,6 +26,7 @@
|
||||||
#include "xenia/kernel/xam/xam_module.h"
|
#include "xenia/kernel/xam/xam_module.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
#include "xenia/profiling.h"
|
||||||
#include "xenia/vfs/devices/disc_image_device.h"
|
#include "xenia/vfs/devices/disc_image_device.h"
|
||||||
#include "xenia/vfs/devices/host_path_device.h"
|
#include "xenia/vfs/devices/host_path_device.h"
|
||||||
#include "xenia/vfs/devices/stfs_container_device.h"
|
#include "xenia/vfs/devices/stfs_container_device.h"
|
||||||
|
@ -164,8 +165,7 @@ X_STATUS Emulator::Setup(ui::Window* display_window) {
|
||||||
// Finish initializing the display.
|
// Finish initializing the display.
|
||||||
display_window_->loop()->PostSynchronous([this]() {
|
display_window_->loop()->PostSynchronous([this]() {
|
||||||
xe::ui::GraphicsContextLock context_lock(display_window_->context());
|
xe::ui::GraphicsContextLock context_lock(display_window_->context());
|
||||||
auto profiler_display = display_window_->context()->CreateProfilerDisplay();
|
Profiler::set_window(display_window_);
|
||||||
Profiler::set_display(std::move(profiler_display));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -2236,7 +2236,7 @@ int trace_viewer_main(const std::vector<std::wstring>& args) {
|
||||||
window->set_title(std::wstring(L"Xenia GPU Trace Viewer: ") + file_name);
|
window->set_title(std::wstring(L"Xenia GPU Trace Viewer: ") + file_name);
|
||||||
|
|
||||||
auto graphics_system = emulator->graphics_system();
|
auto graphics_system = emulator->graphics_system();
|
||||||
Profiler::set_display(nullptr);
|
Profiler::set_window(nullptr);
|
||||||
|
|
||||||
TracePlayer player(loop.get(), emulator->graphics_system());
|
TracePlayer player(loop.get(), emulator->graphics_system());
|
||||||
if (!player.Open(abs_path)) {
|
if (!player.Open(abs_path)) {
|
||||||
|
|
|
@ -29,17 +29,25 @@
|
||||||
#define MICROPROFILE_MAX_THREADS 128
|
#define MICROPROFILE_MAX_THREADS 128
|
||||||
#include "third_party/microprofile/microprofile.h"
|
#include "third_party/microprofile/microprofile.h"
|
||||||
|
|
||||||
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/profiling.h"
|
#include "xenia/profiling.h"
|
||||||
|
#include "xenia/ui/window.h"
|
||||||
|
|
||||||
#if XE_OPTION_PROFILING
|
#if XE_OPTION_PROFILING
|
||||||
#include "third_party/microprofile/microprofileui.h"
|
#include "third_party/microprofile/microprofileui.h"
|
||||||
#endif // XE_OPTION_PROFILING
|
#endif // XE_OPTION_PROFILING
|
||||||
|
|
||||||
|
#if XE_OPTION_PROFILING_UI
|
||||||
|
#undef DrawText
|
||||||
|
#include "xenia/ui/microprofile_drawer.h"
|
||||||
|
#endif // XE_OPTION_PROFILING_UI
|
||||||
|
|
||||||
DEFINE_bool(show_profiler, false, "Show profiling UI by default.");
|
DEFINE_bool(show_profiler, false, "Show profiling UI by default.");
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
std::unique_ptr<ProfilerDisplay> Profiler::display_ = nullptr;
|
ui::Window* Profiler::window_ = nullptr;
|
||||||
|
std::unique_ptr<ui::MicroprofileDrawer> Profiler::drawer_ = nullptr;
|
||||||
|
|
||||||
#if XE_OPTION_PROFILING
|
#if XE_OPTION_PROFILING
|
||||||
|
|
||||||
|
@ -86,7 +94,8 @@ void Profiler::Dump() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profiler::Shutdown() {
|
void Profiler::Shutdown() {
|
||||||
display_.reset();
|
drawer_.reset();
|
||||||
|
window_ = nullptr;
|
||||||
MicroProfileShutdown();
|
MicroProfileShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,20 +174,74 @@ void Profiler::TogglePause() {}
|
||||||
|
|
||||||
#endif // XE_OPTION_PROFILING_UI
|
#endif // XE_OPTION_PROFILING_UI
|
||||||
|
|
||||||
void Profiler::set_display(std::unique_ptr<ProfilerDisplay> display) {
|
void Profiler::set_window(ui::Window* window) {
|
||||||
display_ = std::move(display);
|
assert_null(window_);
|
||||||
|
if (!window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window_ = window;
|
||||||
|
drawer_ = std::make_unique<ui::MicroprofileDrawer>(window);
|
||||||
|
|
||||||
|
window_->on_painted.AddListener([](ui::UIEvent* e) { Profiler::Present(); });
|
||||||
|
|
||||||
|
// Pass through mouse events.
|
||||||
|
window_->on_mouse_down.AddListener([](ui::MouseEvent* e) {
|
||||||
|
if (Profiler::is_visible()) {
|
||||||
|
Profiler::OnMouseDown(e->button() == ui::MouseEvent::Button::kLeft,
|
||||||
|
e->button() == ui::MouseEvent::Button::kRight);
|
||||||
|
e->set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window_->on_mouse_up.AddListener([](ui::MouseEvent* e) {
|
||||||
|
if (Profiler::is_visible()) {
|
||||||
|
Profiler::OnMouseUp();
|
||||||
|
e->set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window_->on_mouse_move.AddListener([](ui::MouseEvent* e) {
|
||||||
|
if (Profiler::is_visible()) {
|
||||||
|
Profiler::OnMouseMove(e->x(), e->y());
|
||||||
|
e->set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window_->on_mouse_wheel.AddListener([](ui::MouseEvent* e) {
|
||||||
|
if (Profiler::is_visible()) {
|
||||||
|
Profiler::OnMouseWheel(e->x(), e->y(), -e->dy());
|
||||||
|
e->set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Watch for toggle/mode keys and such.
|
||||||
|
window_->on_key_down.AddListener([](ui::KeyEvent* e) {
|
||||||
|
if (Profiler::is_visible()) {
|
||||||
|
Profiler::OnKeyDown(e->key_code());
|
||||||
|
e->set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window_->on_key_up.AddListener([](ui::KeyEvent* e) {
|
||||||
|
if (Profiler::is_visible()) {
|
||||||
|
Profiler::OnKeyUp(e->key_code());
|
||||||
|
e->set_handled(true);
|
||||||
|
window_->Invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profiler::Present() {
|
void Profiler::Present() {
|
||||||
SCOPE_profile_cpu_f("internal");
|
SCOPE_profile_cpu_f("internal");
|
||||||
MicroProfileFlip();
|
MicroProfileFlip();
|
||||||
#if XE_OPTION_PROFILING_UI
|
#if XE_OPTION_PROFILING_UI
|
||||||
if (!display_) {
|
if (!window_ || !drawer_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
display_->Begin();
|
drawer_->Begin();
|
||||||
MicroProfileDraw(display_->width(), display_->height());
|
MicroProfileDraw(window_->width(), window_->height());
|
||||||
display_->End();
|
drawer_->End();
|
||||||
#endif // XE_OPTION_PROFILING_UI
|
#endif // XE_OPTION_PROFILING_UI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +261,7 @@ void Profiler::OnMouseDown(bool left_button, bool right_button) {}
|
||||||
void Profiler::OnMouseUp() {}
|
void Profiler::OnMouseUp() {}
|
||||||
void Profiler::OnMouseMove(int x, int y) {}
|
void Profiler::OnMouseMove(int x, int y) {}
|
||||||
void Profiler::OnMouseWheel(int x, int y, int dy) {}
|
void Profiler::OnMouseWheel(int x, int y, int dy) {}
|
||||||
void Profiler::set_display(std::unique_ptr<ProfilerDisplay> display) {}
|
void Profiler::set_display(std::unique_ptr<ui::MicroprofilerDrawer> display) {}
|
||||||
void Profiler::Present() {}
|
void Profiler::Present() {}
|
||||||
|
|
||||||
#endif // XE_OPTION_PROFILING
|
#endif // XE_OPTION_PROFILING
|
||||||
|
@ -219,30 +282,30 @@ const char* MicroProfileGetThreadName() { return "TODO: get thread name!"; }
|
||||||
|
|
||||||
void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, uint32_t nColor,
|
void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, uint32_t nColor,
|
||||||
MicroProfileBoxType type) {
|
MicroProfileBoxType type) {
|
||||||
auto display = xe::Profiler::display();
|
auto drawer = xe::Profiler::drawer();
|
||||||
if (!display) {
|
if (!drawer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
display->DrawBox(nX, nY, nX1, nY1, nColor,
|
drawer->DrawBox(nX, nY, nX1, nY1, nColor,
|
||||||
static_cast<xe::ProfilerDisplay::BoxType>(type));
|
static_cast<xe::ui::MicroprofileDrawer::BoxType>(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices,
|
void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices,
|
||||||
uint32_t nColor) {
|
uint32_t nColor) {
|
||||||
auto display = xe::Profiler::display();
|
auto drawer = xe::Profiler::drawer();
|
||||||
if (!display) {
|
if (!drawer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
display->DrawLine2D(nVertices, pVertices, nColor);
|
drawer->DrawLine2D(nVertices, pVertices, nColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText,
|
void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText,
|
||||||
uint32_t nLen) {
|
uint32_t nLen) {
|
||||||
auto display = xe::Profiler::display();
|
auto drawer = xe::Profiler::drawer();
|
||||||
if (!display) {
|
if (!drawer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
display->DrawText(nX, nY, nColor, pText, nLen);
|
drawer->DrawText(nX, nY, nColor, pText, nLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // XE_OPTION_PROFILING_UI
|
#endif // XE_OPTION_PROFILING_UI
|
||||||
|
|
|
@ -27,6 +27,13 @@
|
||||||
#include <microprofile/microprofile.h>
|
#include <microprofile/microprofile.h>
|
||||||
#endif // XE_OPTION_PROFILING
|
#endif // XE_OPTION_PROFILING
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
class MicroprofileDrawer;
|
||||||
|
class Window;
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
#if XE_OPTION_PROFILING
|
#if XE_OPTION_PROFILING
|
||||||
|
@ -125,32 +132,6 @@ namespace xe {
|
||||||
|
|
||||||
#endif // XE_OPTION_PROFILING
|
#endif // XE_OPTION_PROFILING
|
||||||
|
|
||||||
class ProfilerDisplay {
|
|
||||||
public:
|
|
||||||
enum class BoxType {
|
|
||||||
#if XE_OPTION_PROFILING
|
|
||||||
kBar = MicroProfileBoxTypeBar,
|
|
||||||
kFlat = MicroProfileBoxTypeFlat,
|
|
||||||
#else
|
|
||||||
kBar,
|
|
||||||
kFlat,
|
|
||||||
#endif // XE_OPTION_PROFILING
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual uint32_t width() const = 0;
|
|
||||||
virtual uint32_t height() const = 0;
|
|
||||||
|
|
||||||
// TODO(benvanik): GPU timestamping.
|
|
||||||
|
|
||||||
virtual void Begin() = 0;
|
|
||||||
virtual void End() = 0;
|
|
||||||
virtual void DrawBox(int x0, int y0, int x1, int y1, uint32_t color,
|
|
||||||
BoxType type) = 0;
|
|
||||||
virtual void DrawLine2D(uint32_t count, float* vertices, uint32_t color) = 0;
|
|
||||||
virtual void DrawText(int x, int y, uint32_t color, const char* text,
|
|
||||||
size_t text_length) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Profiler {
|
class Profiler {
|
||||||
public:
|
public:
|
||||||
static bool is_enabled();
|
static bool is_enabled();
|
||||||
|
@ -181,15 +162,16 @@ class Profiler {
|
||||||
static void ToggleDisplay();
|
static void ToggleDisplay();
|
||||||
static void TogglePause();
|
static void TogglePause();
|
||||||
|
|
||||||
|
// Initializes input and drawing with the given display.
|
||||||
|
static void set_window(ui::Window* window);
|
||||||
// Gets the current display, if any.
|
// Gets the current display, if any.
|
||||||
static ProfilerDisplay* display() { return display_.get(); }
|
static ui::MicroprofileDrawer* drawer() { return drawer_.get(); }
|
||||||
// Initializes drawing with the given display.
|
|
||||||
static void set_display(std::unique_ptr<ProfilerDisplay> display);
|
|
||||||
// Presents the profiler to the bound display, if any.
|
// Presents the profiler to the bound display, if any.
|
||||||
static void Present();
|
static void Present();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::unique_ptr<ProfilerDisplay> display_;
|
static ui::Window* window_;
|
||||||
|
static std::unique_ptr<ui::MicroprofileDrawer> drawer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/profiling.h"
|
#include "xenia/profiling.h"
|
||||||
#include "xenia/ui/gl/gl4_elemental_renderer.h"
|
#include "xenia/ui/gl/gl4_elemental_renderer.h"
|
||||||
#include "xenia/ui/gl/gl_profiler_display.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?
|
// TODO(benvanik): move win32 code to _win?
|
||||||
|
@ -84,6 +84,7 @@ GLContext::GLContext(Window* target_window, HGLRC glrc)
|
||||||
GLContext::~GLContext() {
|
GLContext::~GLContext() {
|
||||||
MakeCurrent();
|
MakeCurrent();
|
||||||
blitter_.Shutdown();
|
blitter_.Shutdown();
|
||||||
|
immediate_drawer_.reset();
|
||||||
ClearCurrent();
|
ClearCurrent();
|
||||||
if (glrc_) {
|
if (glrc_) {
|
||||||
wglDeleteContext(glrc_);
|
wglDeleteContext(glrc_);
|
||||||
|
@ -186,6 +187,8 @@ bool GLContext::Initialize(Window* target_window, GLContext* share_context) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
immediate_drawer_ = std::make_unique<GLImmediateDrawer>(this);
|
||||||
|
|
||||||
ClearCurrent();
|
ClearCurrent();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -249,6 +252,8 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_context->immediate_drawer_ = std::make_unique<GLImmediateDrawer>(this);
|
||||||
|
|
||||||
new_context->ClearCurrent();
|
new_context->ClearCurrent();
|
||||||
|
|
||||||
return std::unique_ptr<GraphicsContext>(new_context.release());
|
return std::unique_ptr<GraphicsContext>(new_context.release());
|
||||||
|
@ -408,14 +413,14 @@ void GLContext::SetupDebugging() {
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ProfilerDisplay> GLContext::CreateProfilerDisplay() {
|
|
||||||
return std::make_unique<GLProfilerDisplay>(target_window_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<el::graphics::Renderer> GLContext::CreateElementalRenderer() {
|
std::unique_ptr<el::graphics::Renderer> GLContext::CreateElementalRenderer() {
|
||||||
return GL4ElementalRenderer::Create(this);
|
return GL4ElementalRenderer::Create(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImmediateDrawer* GLContext::immediate_drawer() {
|
||||||
|
return immediate_drawer_.get();
|
||||||
|
}
|
||||||
|
|
||||||
bool GLContext::is_current() {
|
bool GLContext::is_current() {
|
||||||
return tls_glew_context_ == glew_context_.get();
|
return tls_glew_context_ == glew_context_.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace gl {
|
namespace gl {
|
||||||
|
|
||||||
|
class GLImmediateDrawer;
|
||||||
|
|
||||||
class GLContext : public GraphicsContext {
|
class GLContext : public GraphicsContext {
|
||||||
public:
|
public:
|
||||||
static std::unique_ptr<GLContext> Create(Window* target_window,
|
static std::unique_ptr<GLContext> Create(Window* target_window,
|
||||||
|
@ -38,9 +40,10 @@ class GLContext : public GraphicsContext {
|
||||||
HDC dc() const { return dc_; }
|
HDC dc() const { return dc_; }
|
||||||
|
|
||||||
std::unique_ptr<GraphicsContext> CreateShared() override;
|
std::unique_ptr<GraphicsContext> CreateShared() override;
|
||||||
std::unique_ptr<ProfilerDisplay> CreateProfilerDisplay() override;
|
|
||||||
std::unique_ptr<el::graphics::Renderer> CreateElementalRenderer() override;
|
std::unique_ptr<el::graphics::Renderer> CreateElementalRenderer() override;
|
||||||
|
|
||||||
|
ImmediateDrawer* immediate_drawer() override;
|
||||||
|
|
||||||
bool is_current() override;
|
bool is_current() override;
|
||||||
bool MakeCurrent() override;
|
bool MakeCurrent() override;
|
||||||
void ClearCurrent() override;
|
void ClearCurrent() override;
|
||||||
|
@ -73,6 +76,7 @@ class GLContext : public GraphicsContext {
|
||||||
std::unique_ptr<WGLEWContext> wglew_context_;
|
std::unique_ptr<WGLEWContext> wglew_context_;
|
||||||
|
|
||||||
Blitter blitter_;
|
Blitter blitter_;
|
||||||
|
std::unique_ptr<GLImmediateDrawer> immediate_drawer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace gl
|
} // namespace gl
|
||||||
|
|
|
@ -0,0 +1,262 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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/gl_immediate_drawer.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "xenia/ui/graphics_context.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
namespace gl {
|
||||||
|
|
||||||
|
constexpr uint32_t kMaxDrawVertices = 64 * 1024;
|
||||||
|
constexpr uint32_t kMaxDrawIndices = 64 * 1024;
|
||||||
|
|
||||||
|
class GLImmediateTexture : public ImmediateTexture {
|
||||||
|
public:
|
||||||
|
GLImmediateTexture(uint32_t width, uint32_t height,
|
||||||
|
ImmediateTextureFilter filter, bool repeat)
|
||||||
|
: ImmediateTexture(width, height) {
|
||||||
|
GLuint handle;
|
||||||
|
glCreateTextures(GL_TEXTURE_2D, 1, &handle);
|
||||||
|
|
||||||
|
GLenum gl_filter = GL_NEAREST;
|
||||||
|
switch (filter) {
|
||||||
|
case ImmediateTextureFilter::kNearest:
|
||||||
|
gl_filter = GL_NEAREST;
|
||||||
|
break;
|
||||||
|
case ImmediateTextureFilter::kLinear:
|
||||||
|
gl_filter = GL_LINEAR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
glTextureParameteri(handle, GL_TEXTURE_MIN_FILTER, gl_filter);
|
||||||
|
glTextureParameteri(handle, GL_TEXTURE_MAG_FILTER, gl_filter);
|
||||||
|
|
||||||
|
glTextureParameteri(handle, GL_TEXTURE_WRAP_S,
|
||||||
|
repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
||||||
|
glTextureParameteri(handle, GL_TEXTURE_WRAP_T,
|
||||||
|
repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
glTextureStorage2D(handle, 1, GL_RGBA8, width, height);
|
||||||
|
|
||||||
|
this->handle = static_cast<uintptr_t>(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
~GLImmediateTexture() override {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GLImmediateDrawer::GLImmediateDrawer(GraphicsContext* graphics_context)
|
||||||
|
: ImmediateDrawer(graphics_context) {
|
||||||
|
glCreateBuffers(1, &vertex_buffer_);
|
||||||
|
glNamedBufferStorage(vertex_buffer_,
|
||||||
|
kMaxDrawVertices * sizeof(ImmediateVertex), nullptr,
|
||||||
|
GL_DYNAMIC_STORAGE_BIT);
|
||||||
|
glCreateBuffers(1, &index_buffer_);
|
||||||
|
glNamedBufferStorage(index_buffer_, kMaxDrawIndices * sizeof(uint16_t),
|
||||||
|
nullptr, GL_DYNAMIC_STORAGE_BIT);
|
||||||
|
|
||||||
|
glCreateVertexArrays(1, &vao_);
|
||||||
|
glEnableVertexArrayAttrib(vao_, 0);
|
||||||
|
glVertexArrayAttribBinding(vao_, 0, 0);
|
||||||
|
glVertexArrayAttribFormat(vao_, 0, 2, GL_FLOAT, GL_FALSE,
|
||||||
|
offsetof(ImmediateVertex, x));
|
||||||
|
glEnableVertexArrayAttrib(vao_, 1);
|
||||||
|
glVertexArrayAttribBinding(vao_, 1, 0);
|
||||||
|
glVertexArrayAttribFormat(vao_, 1, 2, GL_FLOAT, GL_FALSE,
|
||||||
|
offsetof(ImmediateVertex, u));
|
||||||
|
glEnableVertexArrayAttrib(vao_, 2);
|
||||||
|
glVertexArrayAttribBinding(vao_, 2, 0);
|
||||||
|
glVertexArrayAttribFormat(vao_, 2, 4, GL_UNSIGNED_BYTE, GL_TRUE,
|
||||||
|
offsetof(ImmediateVertex, color));
|
||||||
|
glVertexArrayVertexBuffer(vao_, 0, vertex_buffer_, 0,
|
||||||
|
sizeof(ImmediateVertex));
|
||||||
|
|
||||||
|
InitializeShaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
GLImmediateDrawer::~GLImmediateDrawer() {
|
||||||
|
GraphicsContextLock lock(graphics_context_);
|
||||||
|
glDeleteBuffers(1, &vertex_buffer_);
|
||||||
|
glDeleteBuffers(1, &index_buffer_);
|
||||||
|
glDeleteVertexArrays(1, &vao_);
|
||||||
|
glDeleteProgram(program_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLImmediateDrawer::InitializeShaders() {
|
||||||
|
const std::string header =
|
||||||
|
R"(
|
||||||
|
#version 450
|
||||||
|
#extension GL_ARB_explicit_uniform_location : require
|
||||||
|
#extension GL_ARB_shading_language_420pack : require
|
||||||
|
precision highp float;
|
||||||
|
layout(std140, column_major) uniform;
|
||||||
|
layout(std430, column_major) buffer;
|
||||||
|
)";
|
||||||
|
const std::string vertex_shader_source = header +
|
||||||
|
R"(
|
||||||
|
layout(location = 0) uniform mat4 projection_matrix;
|
||||||
|
layout(location = 0) in vec2 in_pos;
|
||||||
|
layout(location = 1) in vec2 in_uv;
|
||||||
|
layout(location = 2) in vec4 in_color;
|
||||||
|
layout(location = 0) out vec2 vtx_uv;
|
||||||
|
layout(location = 1) out vec4 vtx_color;
|
||||||
|
void main() {
|
||||||
|
gl_Position = projection_matrix * vec4(in_pos.xy, 0.0, 1.0);
|
||||||
|
vtx_uv = in_uv;
|
||||||
|
vtx_color = in_color;
|
||||||
|
})";
|
||||||
|
const std::string fragment_shader_source = header +
|
||||||
|
R"(
|
||||||
|
layout(location = 1) uniform sampler2D texture_sampler;
|
||||||
|
layout(location = 0) in vec2 vtx_uv;
|
||||||
|
layout(location = 1) in vec4 vtx_color;
|
||||||
|
layout(location = 0) out vec4 out_color;
|
||||||
|
void main() {
|
||||||
|
out_color = vtx_color;
|
||||||
|
if (vtx_uv.x <= 1.0) {
|
||||||
|
vec4 tex_color = texture(texture_sampler, vtx_uv);
|
||||||
|
out_color *= tex_color;
|
||||||
|
// TODO(benvanik): microprofiler shadows.
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
|
||||||
|
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
const char* vertex_shader_source_ptr = vertex_shader_source.c_str();
|
||||||
|
GLint vertex_shader_source_length = GLint(vertex_shader_source.size());
|
||||||
|
glShaderSource(vertex_shader, 1, &vertex_shader_source_ptr,
|
||||||
|
&vertex_shader_source_length);
|
||||||
|
glCompileShader(vertex_shader);
|
||||||
|
|
||||||
|
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
const char* fragment_shader_source_ptr = fragment_shader_source.c_str();
|
||||||
|
GLint fragment_shader_source_length = GLint(fragment_shader_source.size());
|
||||||
|
glShaderSource(fragment_shader, 1, &fragment_shader_source_ptr,
|
||||||
|
&fragment_shader_source_length);
|
||||||
|
glCompileShader(fragment_shader);
|
||||||
|
|
||||||
|
program_ = glCreateProgram();
|
||||||
|
glAttachShader(program_, vertex_shader);
|
||||||
|
glAttachShader(program_, fragment_shader);
|
||||||
|
glLinkProgram(program_);
|
||||||
|
glDeleteShader(vertex_shader);
|
||||||
|
glDeleteShader(fragment_shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ImmediateTexture> GLImmediateDrawer::CreateTexture(
|
||||||
|
uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat,
|
||||||
|
const uint8_t* data) {
|
||||||
|
GraphicsContextLock lock(graphics_context_);
|
||||||
|
auto texture =
|
||||||
|
std::make_unique<GLImmediateTexture>(width, height, filter, repeat);
|
||||||
|
if (data) {
|
||||||
|
glTextureSubImage2D(static_cast<GLuint>(texture->handle), 0, 0, 0, width,
|
||||||
|
height, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||||
|
}
|
||||||
|
return std::unique_ptr<ImmediateTexture>(texture.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLImmediateDrawer::UpdateTexture(ImmediateTexture* texture,
|
||||||
|
const uint8_t* data) {
|
||||||
|
GraphicsContextLock lock(graphics_context_);
|
||||||
|
glTextureSubImage2D(static_cast<GLuint>(texture->handle), 0, 0, 0,
|
||||||
|
texture->width, texture->height, GL_RGBA,
|
||||||
|
GL_UNSIGNED_BYTE, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLImmediateDrawer::Begin(int render_target_width,
|
||||||
|
int render_target_height) {
|
||||||
|
was_current_ = graphics_context_->is_current();
|
||||||
|
if (!was_current_) {
|
||||||
|
graphics_context_->MakeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup render state.
|
||||||
|
glEnablei(GL_BLEND, 0);
|
||||||
|
glBlendEquationi(0, GL_FUNC_ADD);
|
||||||
|
glBlendFunci(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
// Prepare drawing resources.
|
||||||
|
glUseProgram(program_);
|
||||||
|
glBindVertexArray(vao_);
|
||||||
|
|
||||||
|
// Setup orthographic projection matrix and viewport.
|
||||||
|
const float ortho_projection[4][4] = {
|
||||||
|
{2.0f / render_target_width, 0.0f, 0.0f, 0.0f},
|
||||||
|
{0.0f, 2.0f / -render_target_height, 0.0f, 0.0f},
|
||||||
|
{0.0f, 0.0f, -1.0f, 0.0f},
|
||||||
|
{-1.0f, 1.0f, 0.0f, 1.0f},
|
||||||
|
};
|
||||||
|
glProgramUniformMatrix4fv(program_, 0, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||||
|
glViewport(0, 0, render_target_width, render_target_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLImmediateDrawer::Draw(const ImmediateDrawBatch& batch) {
|
||||||
|
glNamedBufferSubData(vertex_buffer_, 0,
|
||||||
|
batch.vertex_count * sizeof(ImmediateVertex),
|
||||||
|
batch.vertices);
|
||||||
|
if (batch.indices) {
|
||||||
|
glNamedBufferSubData(index_buffer_, 0, batch.index_count * sizeof(uint16_t),
|
||||||
|
batch.indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batch.scissor) {
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
glScissor(batch.scissor_rect[0], batch.scissor_rect[1],
|
||||||
|
batch.scissor_rect[2], batch.scissor_rect[3]);
|
||||||
|
} else {
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batch.texture_handle) {
|
||||||
|
glBindTextureUnit(0, static_cast<GLuint>(batch.texture_handle));
|
||||||
|
} else {
|
||||||
|
glBindTextureUnit(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum mode = GL_TRIANGLES;
|
||||||
|
switch (batch.primitive_type) {
|
||||||
|
case ImmediatePrimitiveType::kLines:
|
||||||
|
mode = GL_LINES;
|
||||||
|
break;
|
||||||
|
case ImmediatePrimitiveType::kTriangles:
|
||||||
|
mode = GL_TRIANGLES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batch.indices) {
|
||||||
|
glDrawElements(mode, batch.index_count, GL_UNSIGNED_SHORT, nullptr);
|
||||||
|
} else {
|
||||||
|
glDrawArrays(mode, 0, batch.vertex_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
glFlush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLImmediateDrawer::End() {
|
||||||
|
// Restore modified state.
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glBindTextureUnit(0, 0);
|
||||||
|
glUseProgram(0);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
if (!was_current_) {
|
||||||
|
graphics_context_->ClearCurrent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gl
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_UI_GL_GL_IMMEDIATE_DRAWER_H_
|
||||||
|
#define XENIA_UI_GL_GL_IMMEDIATE_DRAWER_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "xenia/ui/gl/gl.h"
|
||||||
|
#include "xenia/ui/immediate_drawer.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
namespace gl {
|
||||||
|
|
||||||
|
class GLImmediateDrawer : public ImmediateDrawer {
|
||||||
|
public:
|
||||||
|
GLImmediateDrawer(GraphicsContext* graphics_context);
|
||||||
|
~GLImmediateDrawer() override;
|
||||||
|
|
||||||
|
std::unique_ptr<ImmediateTexture> CreateTexture(uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
ImmediateTextureFilter filter,
|
||||||
|
bool repeat,
|
||||||
|
const uint8_t* data) override;
|
||||||
|
void UpdateTexture(ImmediateTexture* texture, const uint8_t* data) override;
|
||||||
|
|
||||||
|
void Begin(int render_target_width, int render_target_height) override;
|
||||||
|
void Draw(const ImmediateDrawBatch& batch) override;
|
||||||
|
void End() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void InitializeShaders();
|
||||||
|
|
||||||
|
GLuint program_ = 0;
|
||||||
|
GLuint vao_ = 0;
|
||||||
|
GLuint vertex_buffer_ = 0;
|
||||||
|
GLuint index_buffer_ = 0;
|
||||||
|
|
||||||
|
bool was_current_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gl
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif // XENIA_UI_GL_GL_IMMEDIATE_DRAWER_H_
|
|
@ -1,82 +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. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef XENIA_UI_GL_GL_PROFILER_DISPLAY_H_
|
|
||||||
#define XENIA_UI_GL_GL_PROFILER_DISPLAY_H_
|
|
||||||
|
|
||||||
#include "xenia/profiling.h"
|
|
||||||
#include "xenia/ui/gl/circular_buffer.h"
|
|
||||||
#include "xenia/ui/window.h"
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace ui {
|
|
||||||
namespace gl {
|
|
||||||
|
|
||||||
class GLProfilerDisplay : public ProfilerDisplay {
|
|
||||||
public:
|
|
||||||
explicit GLProfilerDisplay(xe::ui::Window* window);
|
|
||||||
virtual ~GLProfilerDisplay();
|
|
||||||
|
|
||||||
uint32_t width() const override;
|
|
||||||
uint32_t height() const override;
|
|
||||||
|
|
||||||
// TODO(benvanik): GPU timestamping.
|
|
||||||
|
|
||||||
void Begin() override;
|
|
||||||
void End() override;
|
|
||||||
void DrawBox(int x0, int y0, int x1, int y1, uint32_t color,
|
|
||||||
BoxType type) override;
|
|
||||||
void DrawLine2D(uint32_t count, float* vertices, uint32_t color) override;
|
|
||||||
void DrawText(int x, int y, uint32_t color, const char* text,
|
|
||||||
size_t text_length) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Vertex {
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
uint32_t color;
|
|
||||||
float u;
|
|
||||||
float v;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool SetupFont();
|
|
||||||
bool SetupState();
|
|
||||||
bool SetupShaders();
|
|
||||||
|
|
||||||
Vertex* BeginVertices(size_t count);
|
|
||||||
void EndVertices(GLenum prim_type);
|
|
||||||
void Flush();
|
|
||||||
|
|
||||||
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;
|
|
||||||
struct {
|
|
||||||
GLenum prim_type;
|
|
||||||
size_t vertex_offset;
|
|
||||||
size_t vertex_count;
|
|
||||||
} draw_commands_[kMaxCommands];
|
|
||||||
uint32_t draw_command_count_ = 0;
|
|
||||||
|
|
||||||
CircularBuffer::Allocation current_allocation_;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
uint16_t char_offsets[256];
|
|
||||||
} font_description_ = {{0}};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace gl
|
|
||||||
} // namespace ui
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
#endif // XENIA_UI_GL_GL_PROFILER_DISPLAY_H_
|
|
|
@ -13,11 +13,11 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "el/graphics/renderer.h"
|
#include "el/graphics/renderer.h"
|
||||||
#include "xenia/profiling.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
|
class ImmediateDrawer;
|
||||||
class Window;
|
class Window;
|
||||||
|
|
||||||
class GraphicsContext {
|
class GraphicsContext {
|
||||||
|
@ -27,9 +27,10 @@ class GraphicsContext {
|
||||||
Window* target_window() const { return target_window_; }
|
Window* target_window() const { return target_window_; }
|
||||||
|
|
||||||
virtual std::unique_ptr<GraphicsContext> CreateShared() = 0;
|
virtual std::unique_ptr<GraphicsContext> CreateShared() = 0;
|
||||||
virtual std::unique_ptr<ProfilerDisplay> CreateProfilerDisplay() = 0;
|
|
||||||
virtual std::unique_ptr<el::graphics::Renderer> CreateElementalRenderer() = 0;
|
virtual std::unique_ptr<el::graphics::Renderer> CreateElementalRenderer() = 0;
|
||||||
|
|
||||||
|
virtual ImmediateDrawer* immediate_drawer() = 0;
|
||||||
|
|
||||||
virtual bool is_current() = 0;
|
virtual bool is_current() = 0;
|
||||||
virtual bool MakeCurrent() = 0;
|
virtual bool MakeCurrent() = 0;
|
||||||
virtual void ClearCurrent() = 0;
|
virtual void ClearCurrent() = 0;
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_UI_IMMEDIATE_DRAWER_H_
|
||||||
|
#define XENIA_UI_IMMEDIATE_DRAWER_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
class GraphicsContext;
|
||||||
|
|
||||||
|
// Describes the filter applied when sampling textures.
|
||||||
|
enum class ImmediateTextureFilter {
|
||||||
|
kNearest,
|
||||||
|
kLinear,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simple texture compatible with the immediate renderer.
|
||||||
|
class ImmediateTexture {
|
||||||
|
public:
|
||||||
|
virtual ~ImmediateTexture() = default;
|
||||||
|
|
||||||
|
// Texture width, in pixels.
|
||||||
|
uint32_t width;
|
||||||
|
// Texture height, in pixels.
|
||||||
|
uint32_t height;
|
||||||
|
// Internal handle. Can be passed with the ImmediateDrawBatch.
|
||||||
|
uintptr_t handle;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ImmediateTexture(uint32_t width, uint32_t height)
|
||||||
|
: width(width), height(height) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Describes the primitive type used by a draw call.
|
||||||
|
enum class ImmediatePrimitiveType {
|
||||||
|
kLines,
|
||||||
|
kTriangles,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simple vertex used by the immediate mode drawer.
|
||||||
|
struct ImmediateVertex {
|
||||||
|
float x, y;
|
||||||
|
float u, v;
|
||||||
|
uint32_t color;
|
||||||
|
};
|
||||||
|
|
||||||
|
// All parameters required to draw an immediate-mode batch of vertices.
|
||||||
|
struct ImmediateDrawBatch {
|
||||||
|
// Primitive type the vertices/indices represent.
|
||||||
|
ImmediatePrimitiveType primitive_type = ImmediatePrimitiveType::kTriangles;
|
||||||
|
|
||||||
|
// Vertices to draw.
|
||||||
|
const ImmediateVertex* vertices = nullptr;
|
||||||
|
int vertex_count = 0;
|
||||||
|
|
||||||
|
// Optional index buffer indices.
|
||||||
|
const uint16_t* indices = nullptr;
|
||||||
|
int index_count = 0;
|
||||||
|
|
||||||
|
// Texture used when drawing, or nullptr if color only.
|
||||||
|
// This is most commonly the handle of an ImmediateTexture.
|
||||||
|
uintptr_t texture_handle = 0;
|
||||||
|
|
||||||
|
// True to enable scissoring using the region defined by scissor_rect.
|
||||||
|
bool scissor = false;
|
||||||
|
// Scissoring region in framebuffer pixels as (x, y, w, h).
|
||||||
|
int scissor_rect[4] = {0};
|
||||||
|
};
|
||||||
|
|
||||||
|
class ImmediateDrawer {
|
||||||
|
public:
|
||||||
|
virtual ~ImmediateDrawer() = default;
|
||||||
|
|
||||||
|
// Creates a new texture with the given attributes and optionally updates
|
||||||
|
// initial data.
|
||||||
|
virtual std::unique_ptr<ImmediateTexture> CreateTexture(
|
||||||
|
uint32_t width, uint32_t height, ImmediateTextureFilter filter,
|
||||||
|
bool repeat, const uint8_t* data = nullptr) = 0;
|
||||||
|
// Uploads data to the given texture, replacing the current contents.
|
||||||
|
virtual void UpdateTexture(ImmediateTexture* texture,
|
||||||
|
const uint8_t* data) = 0;
|
||||||
|
|
||||||
|
// Begins drawing in immediate mode using the given projection matrix.
|
||||||
|
virtual void Begin(int render_target_width, int render_target_height) = 0;
|
||||||
|
// Issues an immediate mode draw batch.
|
||||||
|
virtual void Draw(const ImmediateDrawBatch& batch) = 0;
|
||||||
|
// Ends drawing in immediate mode and flushes contents.
|
||||||
|
virtual void End() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ImmediateDrawer(GraphicsContext* graphics_context)
|
||||||
|
: graphics_context_(graphics_context) {}
|
||||||
|
|
||||||
|
GraphicsContext* graphics_context_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif // XENIA_UI_IMMEDIATE_DRAWER_H_
|
|
@ -7,37 +7,24 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/ui/gl/gl_profiler_display.h"
|
#include "xenia/ui/microprofile_drawer.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "third_party/microprofile/microprofile.h"
|
|
||||||
#include "third_party/microprofile/microprofileui.h"
|
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
|
#include "xenia/ui/window.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace gl {
|
|
||||||
|
|
||||||
#define MICROPROFILE_MAX_VERTICES (16 << 10)
|
const int kMaxVertices = 16 << 10;
|
||||||
#define MICROPROFILE_NUM_QUERIES (8 << 10)
|
|
||||||
#define MAX_FONT_CHARS 256
|
|
||||||
#define Q0(d, member, v) d[0].member = v
|
|
||||||
#define Q1(d, member, v) \
|
|
||||||
d[1].member = v; \
|
|
||||||
d[3].member = v
|
|
||||||
#define Q2(d, member, v) d[4].member = v
|
|
||||||
#define Q3(d, member, v) \
|
|
||||||
d[2].member = v; \
|
|
||||||
d[5].member = v
|
|
||||||
|
|
||||||
const int FONT_TEX_X = 1024;
|
const int kFontTextureWidth = 1024;
|
||||||
const int FONT_TEX_Y = 9;
|
const int kFontTextureHeight = 9;
|
||||||
|
const int kFontCharWidth = 5;
|
||||||
|
const int kFontCharHeight = 8;
|
||||||
|
|
||||||
const uint8_t profiler_font[] = {
|
const uint8_t kFontData[] = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
@ -136,66 +123,14 @@ const uint8_t profiler_font[] = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
GLProfilerDisplay::GLProfilerDisplay(xe::ui::Window* window)
|
MicroprofileDrawer::MicroprofileDrawer(xe::ui::Window* window)
|
||||||
: window_(window),
|
: window_(window),
|
||||||
vertex_buffer_(MICROPROFILE_MAX_VERTICES * sizeof(Vertex) * 10,
|
graphics_context_(window->context()),
|
||||||
sizeof(Vertex)) {
|
vertices_(kMaxVertices) {
|
||||||
if (!SetupFont() || !SetupState() || !SetupShaders()) {
|
SetupFont();
|
||||||
// Hrm.
|
|
||||||
assert_always();
|
|
||||||
}
|
|
||||||
|
|
||||||
window_->on_painted.AddListener([this](UIEvent* e) { Profiler::Present(); });
|
|
||||||
|
|
||||||
// Pass through mouse events.
|
|
||||||
window_->on_mouse_down.AddListener([this](MouseEvent* e) {
|
|
||||||
if (Profiler::is_visible()) {
|
|
||||||
Profiler::OnMouseDown(e->button() == MouseEvent::Button::kLeft,
|
|
||||||
e->button() == MouseEvent::Button::kRight);
|
|
||||||
e->set_handled(true);
|
|
||||||
window_->Invalidate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window_->on_mouse_up.AddListener([this](MouseEvent* e) {
|
|
||||||
if (Profiler::is_visible()) {
|
|
||||||
Profiler::OnMouseUp();
|
|
||||||
e->set_handled(true);
|
|
||||||
window_->Invalidate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window_->on_mouse_move.AddListener([this](MouseEvent* e) {
|
|
||||||
if (Profiler::is_visible()) {
|
|
||||||
Profiler::OnMouseMove(e->x(), e->y());
|
|
||||||
e->set_handled(true);
|
|
||||||
window_->Invalidate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window_->on_mouse_wheel.AddListener([this](MouseEvent* e) {
|
|
||||||
if (Profiler::is_visible()) {
|
|
||||||
Profiler::OnMouseWheel(e->x(), e->y(), -e->dy());
|
|
||||||
e->set_handled(true);
|
|
||||||
window_->Invalidate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Watch for toggle/mode keys and such.
|
|
||||||
window_->on_key_down.AddListener([this](KeyEvent* e) {
|
|
||||||
if (Profiler::is_visible()) {
|
|
||||||
Profiler::OnKeyDown(e->key_code());
|
|
||||||
e->set_handled(true);
|
|
||||||
window_->Invalidate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window_->on_key_up.AddListener([this](KeyEvent* e) {
|
|
||||||
if (Profiler::is_visible()) {
|
|
||||||
Profiler::OnKeyUp(e->key_code());
|
|
||||||
e->set_handled(true);
|
|
||||||
window_->Invalidate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLProfilerDisplay::SetupFont() {
|
void MicroprofileDrawer::SetupFont() {
|
||||||
// Setup font lookup table.
|
// Setup font lookup table.
|
||||||
for (uint32_t i = 0; i < xe::countof(font_description_.char_offsets); ++i) {
|
for (uint32_t i = 0; i < xe::countof(font_description_.char_offsets); ++i) {
|
||||||
font_description_.char_offsets[i] = 206;
|
font_description_.char_offsets[i] = 206;
|
||||||
|
@ -223,223 +158,74 @@ bool GLProfilerDisplay::SetupFont() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack font bitmap into an RGBA texture.
|
// Unpack font bitmap into an RGBA texture.
|
||||||
const int UNPACKED_SIZE = FONT_TEX_X * FONT_TEX_Y * 4;
|
const int UNPACKED_SIZE = kFontTextureWidth * kFontTextureHeight * 4;
|
||||||
uint32_t unpacked[UNPACKED_SIZE];
|
uint32_t unpacked[UNPACKED_SIZE];
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
int end = FONT_TEX_X * FONT_TEX_Y / 8;
|
int end = kFontTextureWidth * kFontTextureHeight / 8;
|
||||||
for (int i = 0; i < end; i++) {
|
for (int i = 0; i < end; i++) {
|
||||||
uint8_t b = profiler_font[i];
|
uint8_t b = kFontData[i];
|
||||||
for (int j = 0; j < 8; ++j) {
|
for (int j = 0; j < 8; ++j) {
|
||||||
unpacked[idx++] = b & 0x80 ? 0xFFFFFFFFu : 0;
|
unpacked[idx++] = b & 0x80 ? 0xFFFFFFFFu : 0;
|
||||||
b <<= 1;
|
b <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glCreateTextures(GL_TEXTURE_2D, 1, &font_texture_);
|
font_texture_ = graphics_context_->immediate_drawer()->CreateTexture(
|
||||||
glTextureParameteri(font_texture_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
kFontTextureWidth, kFontTextureHeight, ImmediateTextureFilter::kNearest,
|
||||||
glTextureParameteri(font_texture_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
false, reinterpret_cast<uint8_t*>(unpacked));
|
||||||
glTextureParameteri(font_texture_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTextureParameteri(font_texture_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glTextureStorage2D(font_texture_, 1, GL_RGBA8, FONT_TEX_X, FONT_TEX_Y);
|
|
||||||
glTextureSubImage2D(font_texture_, 0, 0, 0, FONT_TEX_X, FONT_TEX_Y, GL_RGBA,
|
|
||||||
GL_UNSIGNED_BYTE, unpacked);
|
|
||||||
|
|
||||||
font_handle_ = glGetTextureHandleARB(font_texture_);
|
|
||||||
glMakeTextureHandleResidentARB(font_handle_);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLProfilerDisplay::SetupState() {
|
MicroprofileDrawer::~MicroprofileDrawer() { font_texture_.reset(); }
|
||||||
if (!vertex_buffer_.Initialize()) {
|
|
||||||
return false;
|
void MicroprofileDrawer::Begin() {
|
||||||
}
|
graphics_context_->immediate_drawer()->Begin(window_->width(),
|
||||||
return true;
|
window_->height());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLProfilerDisplay::SetupShaders() {
|
void MicroprofileDrawer::End() {
|
||||||
const std::string header =
|
|
||||||
R"(
|
|
||||||
#version 450
|
|
||||||
#extension GL_ARB_bindless_texture : require
|
|
||||||
#extension GL_ARB_explicit_uniform_location : require
|
|
||||||
#extension GL_ARB_shading_language_420pack : require
|
|
||||||
precision highp float;
|
|
||||||
precision highp int;
|
|
||||||
layout(std140, column_major) uniform;
|
|
||||||
layout(std430, column_major) buffer;
|
|
||||||
)";
|
|
||||||
const std::string vertex_shader_source = header +
|
|
||||||
R"(
|
|
||||||
layout(location = 0) uniform mat4 projection_matrix;
|
|
||||||
layout(location = 0) in vec2 in_pos;
|
|
||||||
layout(location = 1) in vec4 in_color;
|
|
||||||
layout(location = 2) in vec2 in_uv;
|
|
||||||
layout(location = 0) out vec4 vtx_color;
|
|
||||||
layout(location = 1) out vec2 vtx_uv;
|
|
||||||
void main() {
|
|
||||||
gl_Position = projection_matrix * vec4(in_pos.xy, 0.0, 1.0);
|
|
||||||
vtx_color = in_color;
|
|
||||||
vtx_uv = in_uv;
|
|
||||||
})";
|
|
||||||
const std::string fragment_shader_source = header +
|
|
||||||
R"(
|
|
||||||
layout(location = 1, bindless_sampler) uniform sampler2D font_texture;
|
|
||||||
layout(location = 2) uniform float font_height;
|
|
||||||
layout(location = 0) in vec4 vtx_color;
|
|
||||||
layout(location = 1) in vec2 vtx_uv;
|
|
||||||
layout(location = 0) out vec4 oC;
|
|
||||||
void main() {
|
|
||||||
if (vtx_uv.x > 1.0) {
|
|
||||||
oC = vtx_color;
|
|
||||||
} else {
|
|
||||||
vec4 color = texture(font_texture, vtx_uv);
|
|
||||||
oC = color.rgba * vtx_color;
|
|
||||||
if (color.a < 0.5) {
|
|
||||||
vec4 c1 = texture(font_texture, vtx_uv + vec2(0.0, font_height));
|
|
||||||
oC = vec4(0, 0, 0, c1.a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})";
|
|
||||||
|
|
||||||
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
|
||||||
const char* vertex_shader_source_ptr = vertex_shader_source.c_str();
|
|
||||||
GLint vertex_shader_source_length = GLint(vertex_shader_source.size());
|
|
||||||
glShaderSource(vertex_shader, 1, &vertex_shader_source_ptr,
|
|
||||||
&vertex_shader_source_length);
|
|
||||||
glCompileShader(vertex_shader);
|
|
||||||
|
|
||||||
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
const char* fragment_shader_source_ptr = fragment_shader_source.c_str();
|
|
||||||
GLint fragment_shader_source_length = GLint(fragment_shader_source.size());
|
|
||||||
glShaderSource(fragment_shader, 1, &fragment_shader_source_ptr,
|
|
||||||
&fragment_shader_source_length);
|
|
||||||
glCompileShader(fragment_shader);
|
|
||||||
|
|
||||||
program_ = glCreateProgram();
|
|
||||||
glAttachShader(program_, vertex_shader);
|
|
||||||
glAttachShader(program_, fragment_shader);
|
|
||||||
glLinkProgram(program_);
|
|
||||||
glDeleteShader(vertex_shader);
|
|
||||||
glDeleteShader(fragment_shader);
|
|
||||||
|
|
||||||
glProgramUniformHandleui64ARB(program_, 1, font_handle_);
|
|
||||||
glProgramUniform1f(program_, 2, 1.0f / FONT_TEX_Y);
|
|
||||||
|
|
||||||
glCreateVertexArrays(1, &vao_);
|
|
||||||
glEnableVertexArrayAttrib(vao_, 0);
|
|
||||||
glVertexArrayAttribBinding(vao_, 0, 0);
|
|
||||||
glVertexArrayAttribFormat(vao_, 0, 2, GL_FLOAT, GL_FALSE,
|
|
||||||
offsetof(Vertex, x));
|
|
||||||
glEnableVertexArrayAttrib(vao_, 1);
|
|
||||||
glVertexArrayAttribBinding(vao_, 1, 0);
|
|
||||||
glVertexArrayAttribFormat(vao_, 1, 4, GL_UNSIGNED_BYTE, GL_TRUE,
|
|
||||||
offsetof(Vertex, color));
|
|
||||||
glEnableVertexArrayAttrib(vao_, 2);
|
|
||||||
glVertexArrayAttribBinding(vao_, 2, 0);
|
|
||||||
glVertexArrayAttribFormat(vao_, 2, 2, GL_FLOAT, GL_FALSE,
|
|
||||||
offsetof(Vertex, u));
|
|
||||||
glVertexArrayVertexBuffer(vao_, 0, vertex_buffer_.handle(), 0,
|
|
||||||
sizeof(Vertex));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLProfilerDisplay::~GLProfilerDisplay() {
|
|
||||||
vertex_buffer_.Shutdown();
|
|
||||||
glMakeTextureHandleNonResidentARB(font_handle_);
|
|
||||||
glDeleteTextures(1, &font_texture_);
|
|
||||||
glDeleteVertexArrays(1, &vao_);
|
|
||||||
glDeleteProgram(program_);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t GLProfilerDisplay::width() const { return window_->width(); }
|
|
||||||
|
|
||||||
uint32_t GLProfilerDisplay::height() const { return window_->height(); }
|
|
||||||
|
|
||||||
void GLProfilerDisplay::Begin() {
|
|
||||||
glEnablei(GL_BLEND, 0);
|
|
||||||
glBlendFunci(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
glDisable(GL_STENCIL_TEST);
|
|
||||||
glDisable(GL_SCISSOR_TEST);
|
|
||||||
|
|
||||||
glViewport(0, 0, width(), height());
|
|
||||||
|
|
||||||
float left = 0.0f;
|
|
||||||
float right = static_cast<float>(width());
|
|
||||||
float bottom = static_cast<float>(height());
|
|
||||||
float top = 0.0f;
|
|
||||||
float z_near = -1.0f;
|
|
||||||
float z_far = 1.0f;
|
|
||||||
float projection[16] = {0};
|
|
||||||
projection[0] = 2.0f / (right - left);
|
|
||||||
projection[5] = 2.0f / (top - bottom);
|
|
||||||
projection[10] = -2.0f / (z_far - z_near);
|
|
||||||
projection[12] = -(right + left) / (right - left);
|
|
||||||
projection[13] = -(top + bottom) / (top - bottom);
|
|
||||||
projection[14] = -(z_far + z_near) / (z_far - z_near);
|
|
||||||
projection[15] = 1.0f;
|
|
||||||
glProgramUniformMatrix4fv(program_, 0, 1, GL_FALSE, projection);
|
|
||||||
|
|
||||||
glUseProgram(program_);
|
|
||||||
glBindVertexArray(vao_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLProfilerDisplay::End() {
|
|
||||||
Flush();
|
Flush();
|
||||||
glUseProgram(0);
|
graphics_context_->immediate_drawer()->End();
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLProfilerDisplay::Vertex* GLProfilerDisplay::BeginVertices(size_t count) {
|
ImmediateVertex* MicroprofileDrawer::BeginVertices(
|
||||||
if (draw_command_count_ + 1 > kMaxCommands) {
|
ImmediatePrimitiveType primitive_type, int count) {
|
||||||
|
if (vertex_count_ + count > vertices_.size() ||
|
||||||
|
primitive_type != current_primitive_type_) {
|
||||||
Flush();
|
Flush();
|
||||||
}
|
}
|
||||||
size_t total_length = sizeof(Vertex) * count;
|
current_primitive_type_ = primitive_type;
|
||||||
if (!vertex_buffer_.CanAcquire(total_length)) {
|
auto ptr = vertices_.data() + vertex_count_;
|
||||||
Flush();
|
vertex_count_ += count;
|
||||||
}
|
return ptr;
|
||||||
current_allocation_ = vertex_buffer_.Acquire(total_length);
|
|
||||||
return reinterpret_cast<Vertex*>(current_allocation_.host_ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLProfilerDisplay::EndVertices(GLenum prim_type) {
|
void MicroprofileDrawer::EndVertices() {}
|
||||||
size_t vertex_count = current_allocation_.length / sizeof(Vertex);
|
|
||||||
|
|
||||||
if (draw_command_count_ &&
|
void MicroprofileDrawer::Flush() {
|
||||||
draw_commands_[draw_command_count_ - 1].prim_type == prim_type) {
|
if (!vertex_count_) {
|
||||||
// Coalesce.
|
|
||||||
auto& prev_command = draw_commands_[draw_command_count_ - 1];
|
|
||||||
prev_command.vertex_count += vertex_count;
|
|
||||||
} else {
|
|
||||||
auto& command = draw_commands_[draw_command_count_++];
|
|
||||||
command.prim_type = prim_type;
|
|
||||||
command.vertex_offset = current_allocation_.offset / sizeof(Vertex);
|
|
||||||
command.vertex_count = vertex_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
vertex_buffer_.Commit(std::move(current_allocation_));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLProfilerDisplay::Flush() {
|
|
||||||
if (!draw_command_count_) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
vertex_buffer_.Flush();
|
ImmediateDrawBatch batch;
|
||||||
for (size_t i = 0; i < draw_command_count_; ++i) {
|
batch.primitive_type = current_primitive_type_;
|
||||||
glDrawArrays(draw_commands_[i].prim_type,
|
batch.vertices = vertices_.data();
|
||||||
GLint(draw_commands_[i].vertex_offset),
|
batch.vertex_count = vertex_count_;
|
||||||
GLsizei(draw_commands_[i].vertex_count));
|
batch.texture_handle = font_texture_->handle;
|
||||||
}
|
graphics_context_->immediate_drawer()->Draw(batch);
|
||||||
draw_command_count_ = 0;
|
vertex_count_ = 0;
|
||||||
// TODO(benvanik): don't finish here.
|
|
||||||
vertex_buffer_.WaitUntilClean();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLProfilerDisplay::DrawBox(int x0, int y0, int x1, int y1, uint32_t color,
|
#define Q0(d, member, v) d[0].member = v
|
||||||
BoxType type) {
|
#define Q1(d, member, v) \
|
||||||
auto v = BeginVertices(6);
|
d[1].member = v; \
|
||||||
|
d[3].member = v
|
||||||
|
#define Q2(d, member, v) d[4].member = v
|
||||||
|
#define Q3(d, member, v) \
|
||||||
|
d[2].member = v; \
|
||||||
|
d[5].member = v
|
||||||
|
|
||||||
|
void MicroprofileDrawer::DrawBox(int x0, int y0, int x1, int y1, uint32_t color,
|
||||||
|
BoxType type) {
|
||||||
|
auto v = BeginVertices(ImmediatePrimitiveType::kTriangles, 6);
|
||||||
if (type == BoxType::kFlat) {
|
if (type == BoxType::kFlat) {
|
||||||
color =
|
color =
|
||||||
((color & 0xff) << 16) | ((color >> 16) & 0xff) | (0xff00ff00 & color);
|
((color & 0xff) << 16) | ((color >> 16) & 0xff) | (0xff00ff00 & color);
|
||||||
|
@ -500,15 +286,15 @@ void GLProfilerDisplay::DrawBox(int x0, int y0, int x1, int y1, uint32_t color,
|
||||||
Q3(v, u, 2.0f);
|
Q3(v, u, 2.0f);
|
||||||
Q3(v, v, 3.0f);
|
Q3(v, v, 3.0f);
|
||||||
}
|
}
|
||||||
EndVertices(GL_TRIANGLES);
|
EndVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLProfilerDisplay::DrawLine2D(uint32_t count, float* vertices,
|
void MicroprofileDrawer::DrawLine2D(uint32_t count, float* vertices,
|
||||||
uint32_t color) {
|
uint32_t color) {
|
||||||
if (!count || !vertices) {
|
if (!count || !vertices) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto v = BeginVertices(2 * (count - 1));
|
auto v = BeginVertices(ImmediatePrimitiveType::kLines, 2 * (count - 1));
|
||||||
color = 0xff000000 | ((color & 0xff) << 16) | (color & 0xff00ff00) |
|
color = 0xff000000 | ((color & 0xff) << 16) | (color & 0xff00ff00) |
|
||||||
((color >> 16) & 0xff);
|
((color >> 16) & 0xff);
|
||||||
for (uint32_t i = 0; i < count - 1; ++i) {
|
for (uint32_t i = 0; i < count - 1; ++i) {
|
||||||
|
@ -524,21 +310,21 @@ void GLProfilerDisplay::DrawLine2D(uint32_t count, float* vertices,
|
||||||
v[1].v = 2.0f;
|
v[1].v = 2.0f;
|
||||||
v += 2;
|
v += 2;
|
||||||
}
|
}
|
||||||
EndVertices(GL_LINES);
|
EndVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLProfilerDisplay::DrawText(int x, int y, uint32_t color, const char* text,
|
void MicroprofileDrawer::DrawText(int x, int y, uint32_t color,
|
||||||
size_t text_length) {
|
const char* text, int text_length) {
|
||||||
if (!text_length) {
|
if (!text_length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float fOffsetU = 5.0f / 1024.0f;
|
const float fOffsetU = kFontCharWidth / static_cast<float>(kFontTextureWidth);
|
||||||
float fX = static_cast<float>(x);
|
float fX = static_cast<float>(x);
|
||||||
float fY = static_cast<float>(y);
|
float fY = static_cast<float>(y);
|
||||||
float fY2 = fY + (MICROPROFILE_TEXT_HEIGHT + 1);
|
float fY2 = fY + (kFontCharHeight + 1);
|
||||||
|
|
||||||
auto v = BeginVertices(6 * text_length);
|
auto v = BeginVertices(ImmediatePrimitiveType::kTriangles, 6 * text_length);
|
||||||
const char* pStr = text;
|
const char* pStr = text;
|
||||||
color = 0xff000000 | ((color & 0xff) << 16) | (color & 0xff00) |
|
color = 0xff000000 | ((color & 0xff) << 16) | (color & 0xff00) |
|
||||||
((color >> 16) & 0xff);
|
((color >> 16) & 0xff);
|
||||||
|
@ -552,13 +338,13 @@ void GLProfilerDisplay::DrawText(int x, int y, uint32_t color, const char* text,
|
||||||
Q0(v, u, fOffset);
|
Q0(v, u, fOffset);
|
||||||
Q0(v, v, 0.0f);
|
Q0(v, v, 0.0f);
|
||||||
|
|
||||||
Q1(v, x, fX + MICROPROFILE_TEXT_WIDTH);
|
Q1(v, x, fX + kFontCharWidth);
|
||||||
Q1(v, y, fY);
|
Q1(v, y, fY);
|
||||||
Q1(v, color, color);
|
Q1(v, color, color);
|
||||||
Q1(v, u, fOffset + fOffsetU);
|
Q1(v, u, fOffset + fOffsetU);
|
||||||
Q1(v, v, 0.0f);
|
Q1(v, v, 0.0f);
|
||||||
|
|
||||||
Q2(v, x, fX + MICROPROFILE_TEXT_WIDTH);
|
Q2(v, x, fX + kFontCharWidth);
|
||||||
Q2(v, y, fY2);
|
Q2(v, y, fY2);
|
||||||
Q2(v, color, color);
|
Q2(v, color, color);
|
||||||
Q2(v, u, fOffset + fOffsetU);
|
Q2(v, u, fOffset + fOffsetU);
|
||||||
|
@ -570,13 +356,12 @@ void GLProfilerDisplay::DrawText(int x, int y, uint32_t color, const char* text,
|
||||||
Q3(v, u, fOffset);
|
Q3(v, u, fOffset);
|
||||||
Q3(v, v, 1.0f);
|
Q3(v, v, 1.0f);
|
||||||
|
|
||||||
fX += MICROPROFILE_TEXT_WIDTH + 1;
|
fX += kFontCharWidth + 1;
|
||||||
v += 6;
|
v += 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
EndVertices(GL_TRIANGLES);
|
EndVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace gl
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // namespace xe
|
|
@ -0,0 +1,65 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_UI_MICROPROFILE_DRAWER_H_
|
||||||
|
#define XENIA_UI_MICROPROFILE_DRAWER_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "xenia/ui/immediate_drawer.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
class GraphicsContext;
|
||||||
|
class Window;
|
||||||
|
|
||||||
|
class MicroprofileDrawer {
|
||||||
|
public:
|
||||||
|
enum class BoxType {
|
||||||
|
kBar = 0, // MicroProfileBoxTypeBar
|
||||||
|
kFlat = 1, // MicroProfileBoxTypeFlat
|
||||||
|
};
|
||||||
|
|
||||||
|
MicroprofileDrawer(Window* window);
|
||||||
|
~MicroprofileDrawer();
|
||||||
|
|
||||||
|
void Begin();
|
||||||
|
void End();
|
||||||
|
void DrawBox(int x0, int y0, int x1, int y1, uint32_t color, BoxType type);
|
||||||
|
void DrawLine2D(uint32_t count, float* vertices, uint32_t color);
|
||||||
|
void DrawText(int x, int y, uint32_t color, const char* text,
|
||||||
|
int text_length);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SetupFont();
|
||||||
|
|
||||||
|
ImmediateVertex* BeginVertices(ImmediatePrimitiveType primitive_type,
|
||||||
|
int count);
|
||||||
|
void EndVertices();
|
||||||
|
void Flush();
|
||||||
|
|
||||||
|
Window* window_ = nullptr;
|
||||||
|
GraphicsContext* graphics_context_ = nullptr;
|
||||||
|
|
||||||
|
std::vector<ImmediateVertex> vertices_;
|
||||||
|
int vertex_count_ = 0;
|
||||||
|
ImmediatePrimitiveType current_primitive_type_;
|
||||||
|
|
||||||
|
std::unique_ptr<ImmediateTexture> font_texture_;
|
||||||
|
struct {
|
||||||
|
uint16_t char_offsets[256];
|
||||||
|
} font_description_ = {{0}};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif // XENIA_UI_MICROPROFILE_DRAWER_H_
|
Loading…
Reference in New Issue