diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 733f021be..52acd61f2 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -26,6 +26,7 @@ #include "xenia/kernel/xam/xam_module.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_module.h" #include "xenia/memory.h" +#include "xenia/profiling.h" #include "xenia/vfs/devices/disc_image_device.h" #include "xenia/vfs/devices/host_path_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. display_window_->loop()->PostSynchronous([this]() { xe::ui::GraphicsContextLock context_lock(display_window_->context()); - auto profiler_display = display_window_->context()->CreateProfilerDisplay(); - Profiler::set_display(std::move(profiler_display)); + Profiler::set_window(display_window_); }); return result; diff --git a/src/xenia/gpu/gl4/trace_viewer_main.cc b/src/xenia/gpu/gl4/trace_viewer_main.cc index c7c4d5e67..f27103d90 100644 --- a/src/xenia/gpu/gl4/trace_viewer_main.cc +++ b/src/xenia/gpu/gl4/trace_viewer_main.cc @@ -2236,7 +2236,7 @@ int trace_viewer_main(const std::vector& args) { window->set_title(std::wstring(L"Xenia GPU Trace Viewer: ") + file_name); auto graphics_system = emulator->graphics_system(); - Profiler::set_display(nullptr); + Profiler::set_window(nullptr); TracePlayer player(loop.get(), emulator->graphics_system()); if (!player.Open(abs_path)) { diff --git a/src/xenia/profiling.cc b/src/xenia/profiling.cc index 0b35d6f2d..f55d535ac 100644 --- a/src/xenia/profiling.cc +++ b/src/xenia/profiling.cc @@ -29,17 +29,25 @@ #define MICROPROFILE_MAX_THREADS 128 #include "third_party/microprofile/microprofile.h" +#include "xenia/base/assert.h" #include "xenia/profiling.h" +#include "xenia/ui/window.h" #if XE_OPTION_PROFILING #include "third_party/microprofile/microprofileui.h" #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."); namespace xe { -std::unique_ptr Profiler::display_ = nullptr; +ui::Window* Profiler::window_ = nullptr; +std::unique_ptr Profiler::drawer_ = nullptr; #if XE_OPTION_PROFILING @@ -86,7 +94,8 @@ void Profiler::Dump() { } void Profiler::Shutdown() { - display_.reset(); + drawer_.reset(); + window_ = nullptr; MicroProfileShutdown(); } @@ -165,20 +174,74 @@ void Profiler::TogglePause() {} #endif // XE_OPTION_PROFILING_UI -void Profiler::set_display(std::unique_ptr display) { - display_ = std::move(display); +void Profiler::set_window(ui::Window* window) { + assert_null(window_); + if (!window) { + return; + } + window_ = window; + drawer_ = std::make_unique(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() { SCOPE_profile_cpu_f("internal"); MicroProfileFlip(); #if XE_OPTION_PROFILING_UI - if (!display_) { + if (!window_ || !drawer_) { return; } - display_->Begin(); - MicroProfileDraw(display_->width(), display_->height()); - display_->End(); + drawer_->Begin(); + MicroProfileDraw(window_->width(), window_->height()); + drawer_->End(); #endif // XE_OPTION_PROFILING_UI } @@ -198,7 +261,7 @@ void Profiler::OnMouseDown(bool left_button, bool right_button) {} void Profiler::OnMouseUp() {} void Profiler::OnMouseMove(int x, int y) {} void Profiler::OnMouseWheel(int x, int y, int dy) {} -void Profiler::set_display(std::unique_ptr display) {} +void Profiler::set_display(std::unique_ptr display) {} void Profiler::Present() {} #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, MicroProfileBoxType type) { - auto display = xe::Profiler::display(); - if (!display) { + auto drawer = xe::Profiler::drawer(); + if (!drawer) { return; } - display->DrawBox(nX, nY, nX1, nY1, nColor, - static_cast(type)); + drawer->DrawBox(nX, nY, nX1, nY1, nColor, + static_cast(type)); } void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices, uint32_t nColor) { - auto display = xe::Profiler::display(); - if (!display) { + auto drawer = xe::Profiler::drawer(); + if (!drawer) { return; } - display->DrawLine2D(nVertices, pVertices, nColor); + drawer->DrawLine2D(nVertices, pVertices, nColor); } void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText, uint32_t nLen) { - auto display = xe::Profiler::display(); - if (!display) { + auto drawer = xe::Profiler::drawer(); + if (!drawer) { return; } - display->DrawText(nX, nY, nColor, pText, nLen); + drawer->DrawText(nX, nY, nColor, pText, nLen); } #endif // XE_OPTION_PROFILING_UI diff --git a/src/xenia/profiling.h b/src/xenia/profiling.h index 5dac7ea76..46c6ae2b9 100644 --- a/src/xenia/profiling.h +++ b/src/xenia/profiling.h @@ -27,6 +27,13 @@ #include #endif // XE_OPTION_PROFILING +namespace xe { +namespace ui { +class MicroprofileDrawer; +class Window; +} // namespace ui +} // namespace xe + namespace xe { #if XE_OPTION_PROFILING @@ -125,32 +132,6 @@ namespace xe { #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 { public: static bool is_enabled(); @@ -181,15 +162,16 @@ class Profiler { static void ToggleDisplay(); static void TogglePause(); + // Initializes input and drawing with the given display. + static void set_window(ui::Window* window); // Gets the current display, if any. - static ProfilerDisplay* display() { return display_.get(); } - // Initializes drawing with the given display. - static void set_display(std::unique_ptr display); + static ui::MicroprofileDrawer* drawer() { return drawer_.get(); } // Presents the profiler to the bound display, if any. static void Present(); private: - static std::unique_ptr display_; + static ui::Window* window_; + static std::unique_ptr drawer_; }; } // namespace xe diff --git a/src/xenia/ui/gl/gl_context.cc b/src/xenia/ui/gl/gl_context.cc index 5ba6fb97f..192468e98 100644 --- a/src/xenia/ui/gl/gl_context.cc +++ b/src/xenia/ui/gl/gl_context.cc @@ -19,7 +19,7 @@ #include "xenia/base/math.h" #include "xenia/profiling.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" // TODO(benvanik): move win32 code to _win? @@ -84,6 +84,7 @@ GLContext::GLContext(Window* target_window, HGLRC glrc) GLContext::~GLContext() { MakeCurrent(); blitter_.Shutdown(); + immediate_drawer_.reset(); ClearCurrent(); if (glrc_) { wglDeleteContext(glrc_); @@ -186,6 +187,8 @@ bool GLContext::Initialize(Window* target_window, GLContext* share_context) { return false; } + immediate_drawer_ = std::make_unique(this); + ClearCurrent(); return true; @@ -249,6 +252,8 @@ std::unique_ptr GLContext::CreateShared() { return nullptr; } + new_context->immediate_drawer_ = std::make_unique(this); + new_context->ClearCurrent(); return std::unique_ptr(new_context.release()); @@ -408,14 +413,14 @@ void GLContext::SetupDebugging() { this); } -std::unique_ptr GLContext::CreateProfilerDisplay() { - return std::make_unique(target_window_); -} - std::unique_ptr GLContext::CreateElementalRenderer() { return GL4ElementalRenderer::Create(this); } +ImmediateDrawer* GLContext::immediate_drawer() { + return immediate_drawer_.get(); +} + bool GLContext::is_current() { return tls_glew_context_ == glew_context_.get(); } diff --git a/src/xenia/ui/gl/gl_context.h b/src/xenia/ui/gl/gl_context.h index a78f045d1..c33d3315d 100644 --- a/src/xenia/ui/gl/gl_context.h +++ b/src/xenia/ui/gl/gl_context.h @@ -28,6 +28,8 @@ namespace xe { namespace ui { namespace gl { +class GLImmediateDrawer; + class GLContext : public GraphicsContext { public: static std::unique_ptr Create(Window* target_window, @@ -38,9 +40,10 @@ class GLContext : public GraphicsContext { HDC dc() const { return dc_; } std::unique_ptr CreateShared() override; - std::unique_ptr CreateProfilerDisplay() override; std::unique_ptr CreateElementalRenderer() override; + ImmediateDrawer* immediate_drawer() override; + bool is_current() override; bool MakeCurrent() override; void ClearCurrent() override; @@ -73,6 +76,7 @@ class GLContext : public GraphicsContext { std::unique_ptr wglew_context_; Blitter blitter_; + std::unique_ptr immediate_drawer_; }; } // namespace gl diff --git a/src/xenia/ui/gl/gl_immediate_drawer.cc b/src/xenia/ui/gl/gl_immediate_drawer.cc new file mode 100644 index 000000000..a84e7d1d9 --- /dev/null +++ b/src/xenia/ui/gl/gl_immediate_drawer.cc @@ -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 + +#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(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 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(width, height, filter, repeat); + if (data) { + glTextureSubImage2D(static_cast(texture->handle), 0, 0, 0, width, + height, GL_RGBA, GL_UNSIGNED_BYTE, data); + } + return std::unique_ptr(texture.release()); +} + +void GLImmediateDrawer::UpdateTexture(ImmediateTexture* texture, + const uint8_t* data) { + GraphicsContextLock lock(graphics_context_); + glTextureSubImage2D(static_cast(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(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 diff --git a/src/xenia/ui/gl/gl_immediate_drawer.h b/src/xenia/ui/gl/gl_immediate_drawer.h new file mode 100644 index 000000000..ec384c548 --- /dev/null +++ b/src/xenia/ui/gl/gl_immediate_drawer.h @@ -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 + +#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 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_ diff --git a/src/xenia/ui/gl/gl_profiler_display.h b/src/xenia/ui/gl/gl_profiler_display.h deleted file mode 100644 index 0653781fc..000000000 --- a/src/xenia/ui/gl/gl_profiler_display.h +++ /dev/null @@ -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_ diff --git a/src/xenia/ui/graphics_context.h b/src/xenia/ui/graphics_context.h index 1997d19dc..a79007f3c 100644 --- a/src/xenia/ui/graphics_context.h +++ b/src/xenia/ui/graphics_context.h @@ -13,11 +13,11 @@ #include #include "el/graphics/renderer.h" -#include "xenia/profiling.h" namespace xe { namespace ui { +class ImmediateDrawer; class Window; class GraphicsContext { @@ -27,9 +27,10 @@ class GraphicsContext { Window* target_window() const { return target_window_; } virtual std::unique_ptr CreateShared() = 0; - virtual std::unique_ptr CreateProfilerDisplay() = 0; virtual std::unique_ptr CreateElementalRenderer() = 0; + virtual ImmediateDrawer* immediate_drawer() = 0; + virtual bool is_current() = 0; virtual bool MakeCurrent() = 0; virtual void ClearCurrent() = 0; diff --git a/src/xenia/ui/immediate_drawer.h b/src/xenia/ui/immediate_drawer.h new file mode 100644 index 000000000..baf4613ee --- /dev/null +++ b/src/xenia/ui/immediate_drawer.h @@ -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 + +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 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_ diff --git a/src/xenia/ui/gl/gl_profiler_display.cc b/src/xenia/ui/microprofile_drawer.cc similarity index 56% rename from src/xenia/ui/gl/gl_profiler_display.cc rename to src/xenia/ui/microprofile_drawer.cc index 04903df20..64406e058 100644 --- a/src/xenia/ui/gl/gl_profiler_display.cc +++ b/src/xenia/ui/microprofile_drawer.cc @@ -7,37 +7,24 @@ ****************************************************************************** */ -#include "xenia/ui/gl/gl_profiler_display.h" +#include "xenia/ui/microprofile_drawer.h" #include -#include -#include "third_party/microprofile/microprofile.h" -#include "third_party/microprofile/microprofileui.h" - -#include "xenia/base/assert.h" #include "xenia/base/math.h" +#include "xenia/ui/window.h" namespace xe { namespace ui { -namespace gl { -#define MICROPROFILE_MAX_VERTICES (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 kMaxVertices = 16 << 10; -const int FONT_TEX_X = 1024; -const int FONT_TEX_Y = 9; +const int kFontTextureWidth = 1024; +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, @@ -136,66 +123,14 @@ const uint8_t profiler_font[] = { 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), - vertex_buffer_(MICROPROFILE_MAX_VERTICES * sizeof(Vertex) * 10, - sizeof(Vertex)) { - if (!SetupFont() || !SetupState() || !SetupShaders()) { - // 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(); - } - }); + graphics_context_(window->context()), + vertices_(kMaxVertices) { + SetupFont(); } -bool GLProfilerDisplay::SetupFont() { +void MicroprofileDrawer::SetupFont() { // Setup font lookup table. for (uint32_t i = 0; i < xe::countof(font_description_.char_offsets); ++i) { font_description_.char_offsets[i] = 206; @@ -223,223 +158,74 @@ bool GLProfilerDisplay::SetupFont() { } // 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]; int idx = 0; - int end = FONT_TEX_X * FONT_TEX_Y / 8; + int end = kFontTextureWidth * kFontTextureHeight / 8; 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) { unpacked[idx++] = b & 0x80 ? 0xFFFFFFFFu : 0; b <<= 1; } } - glCreateTextures(GL_TEXTURE_2D, 1, &font_texture_); - glTextureParameteri(font_texture_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTextureParameteri(font_texture_, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - 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; + font_texture_ = graphics_context_->immediate_drawer()->CreateTexture( + kFontTextureWidth, kFontTextureHeight, ImmediateTextureFilter::kNearest, + false, reinterpret_cast(unpacked)); } -bool GLProfilerDisplay::SetupState() { - if (!vertex_buffer_.Initialize()) { - return false; - } - return true; +MicroprofileDrawer::~MicroprofileDrawer() { font_texture_.reset(); } + +void MicroprofileDrawer::Begin() { + graphics_context_->immediate_drawer()->Begin(window_->width(), + window_->height()); } -bool GLProfilerDisplay::SetupShaders() { - 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(width()); - float bottom = static_cast(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() { +void MicroprofileDrawer::End() { Flush(); - glUseProgram(0); - glBindVertexArray(0); + graphics_context_->immediate_drawer()->End(); } -GLProfilerDisplay::Vertex* GLProfilerDisplay::BeginVertices(size_t count) { - if (draw_command_count_ + 1 > kMaxCommands) { +ImmediateVertex* MicroprofileDrawer::BeginVertices( + ImmediatePrimitiveType primitive_type, int count) { + if (vertex_count_ + count > vertices_.size() || + primitive_type != current_primitive_type_) { Flush(); } - size_t total_length = sizeof(Vertex) * count; - if (!vertex_buffer_.CanAcquire(total_length)) { - Flush(); - } - current_allocation_ = vertex_buffer_.Acquire(total_length); - return reinterpret_cast(current_allocation_.host_ptr); + current_primitive_type_ = primitive_type; + auto ptr = vertices_.data() + vertex_count_; + vertex_count_ += count; + return ptr; } -void GLProfilerDisplay::EndVertices(GLenum prim_type) { - size_t vertex_count = current_allocation_.length / sizeof(Vertex); +void MicroprofileDrawer::EndVertices() {} - if (draw_command_count_ && - draw_commands_[draw_command_count_ - 1].prim_type == prim_type) { - // 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_) { +void MicroprofileDrawer::Flush() { + if (!vertex_count_) { return; } - vertex_buffer_.Flush(); - for (size_t i = 0; i < draw_command_count_; ++i) { - glDrawArrays(draw_commands_[i].prim_type, - GLint(draw_commands_[i].vertex_offset), - GLsizei(draw_commands_[i].vertex_count)); - } - draw_command_count_ = 0; - // TODO(benvanik): don't finish here. - vertex_buffer_.WaitUntilClean(); + ImmediateDrawBatch batch; + batch.primitive_type = current_primitive_type_; + batch.vertices = vertices_.data(); + batch.vertex_count = vertex_count_; + batch.texture_handle = font_texture_->handle; + graphics_context_->immediate_drawer()->Draw(batch); + vertex_count_ = 0; } -void GLProfilerDisplay::DrawBox(int x0, int y0, int x1, int y1, uint32_t color, - BoxType type) { - auto v = BeginVertices(6); +#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 + +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) { 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, v, 3.0f); } - EndVertices(GL_TRIANGLES); + EndVertices(); } -void GLProfilerDisplay::DrawLine2D(uint32_t count, float* vertices, - uint32_t color) { +void MicroprofileDrawer::DrawLine2D(uint32_t count, float* vertices, + uint32_t color) { if (!count || !vertices) { return; } - auto v = BeginVertices(2 * (count - 1)); + auto v = BeginVertices(ImmediatePrimitiveType::kLines, 2 * (count - 1)); color = 0xff000000 | ((color & 0xff) << 16) | (color & 0xff00ff00) | ((color >> 16) & 0xff); 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 += 2; } - EndVertices(GL_LINES); + EndVertices(); } -void GLProfilerDisplay::DrawText(int x, int y, uint32_t color, const char* text, - size_t text_length) { +void MicroprofileDrawer::DrawText(int x, int y, uint32_t color, + const char* text, int text_length) { if (!text_length) { return; } - const float fOffsetU = 5.0f / 1024.0f; + const float fOffsetU = kFontCharWidth / static_cast(kFontTextureWidth); float fX = static_cast(x); float fY = static_cast(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; color = 0xff000000 | ((color & 0xff) << 16) | (color & 0xff00) | ((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, v, 0.0f); - Q1(v, x, fX + MICROPROFILE_TEXT_WIDTH); + Q1(v, x, fX + kFontCharWidth); Q1(v, y, fY); Q1(v, color, color); Q1(v, u, fOffset + fOffsetU); Q1(v, v, 0.0f); - Q2(v, x, fX + MICROPROFILE_TEXT_WIDTH); + Q2(v, x, fX + kFontCharWidth); Q2(v, y, fY2); Q2(v, color, color); 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, v, 1.0f); - fX += MICROPROFILE_TEXT_WIDTH + 1; + fX += kFontCharWidth + 1; v += 6; } - EndVertices(GL_TRIANGLES); + EndVertices(); } -} // namespace gl } // namespace ui } // namespace xe diff --git a/src/xenia/ui/microprofile_drawer.h b/src/xenia/ui/microprofile_drawer.h new file mode 100644 index 000000000..1f285e1bc --- /dev/null +++ b/src/xenia/ui/microprofile_drawer.h @@ -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 +#include + +#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 vertices_; + int vertex_count_ = 0; + ImmediatePrimitiveType current_primitive_type_; + + std::unique_ptr font_texture_; + struct { + uint16_t char_offsets[256]; + } font_description_ = {{0}}; +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_MICROPROFILE_DRAWER_H_