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/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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
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)) {
|
||||
|
|
|
@ -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<ProfilerDisplay> Profiler::display_ = nullptr;
|
||||
ui::Window* Profiler::window_ = nullptr;
|
||||
std::unique_ptr<ui::MicroprofileDrawer> 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<ProfilerDisplay> display) {
|
||||
display_ = std::move(display);
|
||||
void Profiler::set_window(ui::Window* window) {
|
||||
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() {
|
||||
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<ProfilerDisplay> display) {}
|
||||
void Profiler::set_display(std::unique_ptr<ui::MicroprofilerDrawer> 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<xe::ProfilerDisplay::BoxType>(type));
|
||||
drawer->DrawBox(nX, nY, nX1, nY1, nColor,
|
||||
static_cast<xe::ui::MicroprofileDrawer::BoxType>(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
|
||||
|
|
|
@ -27,6 +27,13 @@
|
|||
#include <microprofile/microprofile.h>
|
||||
#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<ProfilerDisplay> 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<ProfilerDisplay> display_;
|
||||
static ui::Window* window_;
|
||||
static std::unique_ptr<ui::MicroprofileDrawer> drawer_;
|
||||
};
|
||||
|
||||
} // namespace xe
|
||||
|
|
|
@ -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<GLImmediateDrawer>(this);
|
||||
|
||||
ClearCurrent();
|
||||
|
||||
return true;
|
||||
|
@ -249,6 +252,8 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
new_context->immediate_drawer_ = std::make_unique<GLImmediateDrawer>(this);
|
||||
|
||||
new_context->ClearCurrent();
|
||||
|
||||
return std::unique_ptr<GraphicsContext>(new_context.release());
|
||||
|
@ -408,14 +413,14 @@ void GLContext::SetupDebugging() {
|
|||
this);
|
||||
}
|
||||
|
||||
std::unique_ptr<ProfilerDisplay> GLContext::CreateProfilerDisplay() {
|
||||
return std::make_unique<GLProfilerDisplay>(target_window_);
|
||||
}
|
||||
|
||||
std::unique_ptr<el::graphics::Renderer> GLContext::CreateElementalRenderer() {
|
||||
return GL4ElementalRenderer::Create(this);
|
||||
}
|
||||
|
||||
ImmediateDrawer* GLContext::immediate_drawer() {
|
||||
return immediate_drawer_.get();
|
||||
}
|
||||
|
||||
bool GLContext::is_current() {
|
||||
return tls_glew_context_ == glew_context_.get();
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ namespace xe {
|
|||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
class GLImmediateDrawer;
|
||||
|
||||
class GLContext : public GraphicsContext {
|
||||
public:
|
||||
static std::unique_ptr<GLContext> Create(Window* target_window,
|
||||
|
@ -38,9 +40,10 @@ class GLContext : public GraphicsContext {
|
|||
HDC dc() const { return dc_; }
|
||||
|
||||
std::unique_ptr<GraphicsContext> CreateShared() override;
|
||||
std::unique_ptr<ProfilerDisplay> CreateProfilerDisplay() override;
|
||||
std::unique_ptr<el::graphics::Renderer> 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<WGLEWContext> wglew_context_;
|
||||
|
||||
Blitter blitter_;
|
||||
std::unique_ptr<GLImmediateDrawer> immediate_drawer_;
|
||||
};
|
||||
|
||||
} // 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 "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<GraphicsContext> CreateShared() = 0;
|
||||
virtual std::unique_ptr<ProfilerDisplay> CreateProfilerDisplay() = 0;
|
||||
virtual std::unique_ptr<el::graphics::Renderer> CreateElementalRenderer() = 0;
|
||||
|
||||
virtual ImmediateDrawer* immediate_drawer() = 0;
|
||||
|
||||
virtual bool is_current() = 0;
|
||||
virtual bool MakeCurrent() = 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 <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/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<uint8_t*>(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<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() {
|
||||
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<Vertex*>(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<float>(kFontTextureWidth);
|
||||
float fX = static_cast<float>(x);
|
||||
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;
|
||||
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
|
|
@ -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