/** ****************************************************************************** * 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/microprofile_drawer.h" #include #include "xenia/base/math.h" #include "xenia/ui/window.h" namespace xe { namespace ui { const int kMaxVertices = 16 << 10; const int kFontTextureWidth = 1024; const int kFontTextureHeight = 9; const int kFontCharWidth = 5; const int kFontCharHeight = 8; const uint8_t kFontData[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x78, 0x38, 0x78, 0x7c, 0x7c, 0x3c, 0x44, 0x38, 0x04, 0x44, 0x40, 0x44, 0x44, 0x38, 0x78, 0x38, 0x78, 0x38, 0x7c, 0x44, 0x44, 0x44, 0x44, 0x44, 0x7c, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x18, 0x00, 0x40, 0x10, 0x08, 0x40, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x10, 0x38, 0x7c, 0x08, 0x7c, 0x1c, 0x7c, 0x38, 0x38, 0x10, 0x28, 0x28, 0x10, 0x00, 0x20, 0x10, 0x08, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x20, 0x38, 0x38, 0x70, 0x00, 0x1c, 0x10, 0x00, 0x1c, 0x10, 0x70, 0x30, 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, 0x28, 0x44, 0x44, 0x44, 0x40, 0x40, 0x40, 0x44, 0x10, 0x04, 0x48, 0x40, 0x6c, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x10, 0x44, 0x44, 0x44, 0x44, 0x44, 0x04, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x24, 0x00, 0x40, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x30, 0x44, 0x04, 0x18, 0x40, 0x20, 0x04, 0x44, 0x44, 0x10, 0x28, 0x28, 0x3c, 0x44, 0x50, 0x10, 0x10, 0x08, 0x54, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x10, 0x44, 0x44, 0x40, 0x40, 0x04, 0x28, 0x00, 0x30, 0x10, 0x18, 0x58, 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, 0x44, 0x44, 0x40, 0x44, 0x40, 0x40, 0x40, 0x44, 0x10, 0x04, 0x50, 0x40, 0x54, 0x64, 0x44, 0x44, 0x44, 0x44, 0x40, 0x10, 0x44, 0x44, 0x44, 0x28, 0x28, 0x08, 0x00, 0x38, 0x78, 0x3c, 0x3c, 0x38, 0x20, 0x38, 0x78, 0x30, 0x18, 0x44, 0x10, 0x6c, 0x78, 0x38, 0x78, 0x3c, 0x5c, 0x3c, 0x3c, 0x44, 0x44, 0x44, 0x44, 0x44, 0x7c, 0x00, 0x4c, 0x10, 0x04, 0x08, 0x28, 0x78, 0x40, 0x08, 0x44, 0x44, 0x10, 0x00, 0x7c, 0x50, 0x08, 0x50, 0x00, 0x20, 0x04, 0x38, 0x10, 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x10, 0x7c, 0x08, 0x08, 0x54, 0x40, 0x20, 0x04, 0x44, 0x00, 0x30, 0x10, 0x18, 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, 0x44, 0x78, 0x40, 0x44, 0x78, 0x78, 0x40, 0x7c, 0x10, 0x04, 0x60, 0x40, 0x54, 0x54, 0x44, 0x78, 0x44, 0x78, 0x38, 0x10, 0x44, 0x44, 0x54, 0x10, 0x10, 0x10, 0x00, 0x04, 0x44, 0x40, 0x44, 0x44, 0x78, 0x44, 0x44, 0x10, 0x08, 0x48, 0x10, 0x54, 0x44, 0x44, 0x44, 0x44, 0x60, 0x40, 0x10, 0x44, 0x44, 0x44, 0x28, 0x44, 0x08, 0x00, 0x54, 0x10, 0x18, 0x18, 0x48, 0x04, 0x78, 0x10, 0x38, 0x3c, 0x10, 0x00, 0x28, 0x38, 0x10, 0x20, 0x00, 0x20, 0x04, 0x10, 0x7c, 0x00, 0x7c, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x04, 0x10, 0x5c, 0x40, 0x10, 0x04, 0x00, 0x00, 0x60, 0x10, 0x0c, 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, 0x7c, 0x44, 0x40, 0x44, 0x40, 0x40, 0x4c, 0x44, 0x10, 0x04, 0x50, 0x40, 0x44, 0x4c, 0x44, 0x40, 0x54, 0x50, 0x04, 0x10, 0x44, 0x44, 0x54, 0x28, 0x10, 0x20, 0x00, 0x3c, 0x44, 0x40, 0x44, 0x7c, 0x20, 0x44, 0x44, 0x10, 0x08, 0x70, 0x10, 0x54, 0x44, 0x44, 0x44, 0x44, 0x40, 0x38, 0x10, 0x44, 0x44, 0x54, 0x10, 0x44, 0x10, 0x00, 0x64, 0x10, 0x20, 0x04, 0x7c, 0x04, 0x44, 0x20, 0x44, 0x04, 0x10, 0x00, 0x7c, 0x14, 0x20, 0x54, 0x00, 0x20, 0x04, 0x38, 0x10, 0x10, 0x00, 0x00, 0x20, 0x10, 0x10, 0x10, 0x7c, 0x08, 0x10, 0x58, 0x40, 0x08, 0x04, 0x00, 0x00, 0x30, 0x10, 0x18, 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, 0x44, 0x44, 0x44, 0x44, 0x40, 0x40, 0x44, 0x44, 0x10, 0x44, 0x48, 0x40, 0x44, 0x44, 0x44, 0x40, 0x48, 0x48, 0x44, 0x10, 0x44, 0x28, 0x6c, 0x44, 0x10, 0x40, 0x00, 0x44, 0x44, 0x40, 0x44, 0x40, 0x20, 0x3c, 0x44, 0x10, 0x08, 0x48, 0x10, 0x54, 0x44, 0x44, 0x44, 0x44, 0x40, 0x04, 0x12, 0x4c, 0x28, 0x54, 0x28, 0x3c, 0x20, 0x00, 0x44, 0x10, 0x40, 0x44, 0x08, 0x44, 0x44, 0x20, 0x44, 0x08, 0x00, 0x00, 0x28, 0x78, 0x44, 0x48, 0x00, 0x10, 0x08, 0x54, 0x10, 0x10, 0x00, 0x00, 0x40, 0x00, 0x10, 0x08, 0x00, 0x10, 0x00, 0x40, 0x40, 0x04, 0x04, 0x00, 0x00, 0x30, 0x10, 0x18, 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, 0x44, 0x78, 0x38, 0x78, 0x7c, 0x40, 0x3c, 0x44, 0x38, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x38, 0x40, 0x34, 0x44, 0x38, 0x10, 0x38, 0x10, 0x44, 0x44, 0x10, 0x7c, 0x00, 0x3c, 0x78, 0x3c, 0x3c, 0x3c, 0x20, 0x04, 0x44, 0x38, 0x48, 0x44, 0x38, 0x44, 0x44, 0x38, 0x78, 0x3c, 0x40, 0x78, 0x0c, 0x34, 0x10, 0x6c, 0x44, 0x04, 0x7c, 0x00, 0x38, 0x38, 0x7c, 0x38, 0x08, 0x38, 0x38, 0x20, 0x38, 0x70, 0x10, 0x00, 0x28, 0x10, 0x00, 0x34, 0x00, 0x08, 0x10, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x20, 0x04, 0x00, 0x20, 0x10, 0x3c, 0x70, 0x00, 0x1c, 0x00, 0x7c, 0x1c, 0x10, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; MicroprofileDrawer::MicroprofileDrawer(xe::ui::Window* window) : window_(window), graphics_context_(window->context()), vertices_(kMaxVertices) { 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; } for (uint32_t i = 'A'; i <= 'Z'; ++i) { font_description_.char_offsets[i] = (i - 'A') * 8 + 1; } for (uint32_t i = 'a'; i <= 'z'; ++i) { font_description_.char_offsets[i] = (i - 'a') * 8 + 217; } for (uint32_t i = '0'; i <= '9'; ++i) { font_description_.char_offsets[i] = (i - '0') * 8 + 433; } for (uint32_t i = '!'; i <= '/'; ++i) { font_description_.char_offsets[i] = (i - '!') * 8 + 513; } for (uint32_t i = ':'; i <= '@'; ++i) { font_description_.char_offsets[i] = (i - ':') * 8 + 625 + 8; } for (uint32_t i = '['; i <= '_'; ++i) { font_description_.char_offsets[i] = (i - '[') * 8 + 681 + 8; } for (uint32_t i = '{'; i <= '~'; ++i) { font_description_.char_offsets[i] = (i - '{') * 8 + 721 + 8; } // Unpack font bitmap into an RGBA texture. const int kUnpackedSize = kFontTextureWidth * kFontTextureHeight * 4; uint32_t unpacked[kUnpackedSize]; int idx = 0; int end = kFontTextureWidth * kFontTextureHeight / 8; for (int i = 0; i < end; i++) { uint8_t b = kFontData[i]; for (int j = 0; j < 8; ++j) { unpacked[idx++] = b & 0x80 ? 0xFFFFFFFFu : 0; b <<= 1; } } font_texture_ = graphics_context_->immediate_drawer()->CreateTexture( kFontTextureWidth, kFontTextureHeight, ImmediateTextureFilter::kNearest, false, reinterpret_cast(unpacked)); } MicroprofileDrawer::~MicroprofileDrawer() = default; void MicroprofileDrawer::Begin() { graphics_context_->immediate_drawer()->Begin(window_->scaled_width(), window_->scaled_height()); } void MicroprofileDrawer::End() { Flush(); graphics_context_->immediate_drawer()->End(); } ImmediateVertex* MicroprofileDrawer::BeginVertices( ImmediatePrimitiveType primitive_type, int count) { if (vertex_count_ + count > vertices_.size() || primitive_type != current_primitive_type_) { Flush(); } current_primitive_type_ = primitive_type; auto ptr = vertices_.data() + vertex_count_; vertex_count_ += count; return ptr; } void MicroprofileDrawer::EndVertices() {} void MicroprofileDrawer::Flush() { auto drawer = graphics_context_->immediate_drawer(); if (!vertex_count_) { return; } ImmediateDrawBatch batch; batch.vertices = vertices_.data(); batch.vertex_count = vertex_count_; drawer->BeginDrawBatch(batch); ImmediateDraw draw; draw.primitive_type = current_primitive_type_; draw.count = vertex_count_; draw.texture_handle = font_texture_->handle; draw.restrict_texture_samples = true; drawer->Draw(draw); drawer->EndDrawBatch(); vertex_count_ = 0; } #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); Q0(v, x, static_cast(x0)); Q0(v, y, static_cast(y0)); Q0(v, color, color); Q0(v, u, 2.0f); Q0(v, v, 2.0f); Q1(v, x, static_cast(x1)); Q1(v, y, static_cast(y0)); Q1(v, color, color); Q1(v, u, 2.0f); Q1(v, v, 2.0f); Q2(v, x, static_cast(x1)); Q2(v, y, static_cast(y1)); Q2(v, color, color); Q2(v, u, 2.0f); Q2(v, v, 2.0f); Q3(v, x, static_cast(x0)); Q3(v, y, static_cast(y1)); Q3(v, color, color); Q3(v, u, 2.0f); Q3(v, v, 2.0f); } else { uint32_t r = 0xff & (color >> 16); uint32_t g = 0xff & (color >> 8); uint32_t b = 0xff & color; uint32_t nMax = std::max(std::max(std::max(r, g), b), 30u); uint32_t nMin = std::min(std::min(std::min(r, g), b), 180u); uint32_t r0 = 0xff & ((r + nMax) / 2); uint32_t g0 = 0xff & ((g + nMax) / 2); uint32_t b0 = 0xff & ((b + nMax) / 2); uint32_t r1 = 0xff & ((r + nMin) / 2); uint32_t g1 = 0xff & ((g + nMin) / 2); uint32_t b1 = 0xff & ((b + nMin) / 2); uint32_t color0 = (r0 << 0) | (g0 << 8) | (b0 << 16) | (0xff000000 & color); uint32_t color1 = (r1 << 0) | (g1 << 8) | (b1 << 16) | (0xff000000 & color); Q0(v, x, static_cast(x0)); Q0(v, y, static_cast(y0)); Q0(v, color, color0); Q0(v, u, 2.0f); Q0(v, v, 2.0f); Q1(v, x, static_cast(x1)); Q1(v, y, static_cast(y0)); Q1(v, color, color0); Q1(v, u, 3.0f); Q1(v, v, 2.0f); Q2(v, x, static_cast(x1)); Q2(v, y, static_cast(y1)); Q2(v, color, color1); Q2(v, u, 3.0f); Q2(v, v, 3.0f); Q3(v, x, static_cast(x0)); Q3(v, y, static_cast(y1)); Q3(v, color, color1); Q3(v, u, 2.0f); Q3(v, v, 3.0f); } EndVertices(); } void MicroprofileDrawer::DrawLine2D(uint32_t count, float* vertices, uint32_t color) { if (!count || !vertices) { return; } 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) { v[0].x = vertices[i * 2]; v[0].y = vertices[i * 2 + 1]; v[0].color = color; v[0].u = 2.0f; v[0].v = 2.0f; v[1].x = vertices[(i + 1) * 2]; v[1].y = vertices[(i + 1) * 2 + 1]; v[1].color = color; v[1].u = 2.0f; v[1].v = 2.0f; v += 2; } EndVertices(); } void MicroprofileDrawer::DrawText(int x, int y, uint32_t color, const char* text, int text_length) { if (!text_length) { return; } const float fOffsetU = kFontCharWidth / static_cast(kFontTextureWidth); float fX = static_cast(x); float fY = static_cast(y); float fY2 = fY + (kFontCharHeight + 1); auto v = BeginVertices(ImmediatePrimitiveType::kTriangles, 6 * text_length); const char* pStr = text; color = 0xff000000 | ((color & 0xff) << 16) | (color & 0xff00) | ((color >> 16) & 0xff); for (size_t j = 0; j < text_length; ++j) { int16_t char_offset = font_description_.char_offsets[*pStr++]; float fOffset = char_offset / 1024.0f; Q0(v, x, fX); Q0(v, y, fY); Q0(v, color, color); Q0(v, u, fOffset); Q0(v, v, 0.0f); 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 + kFontCharWidth); Q2(v, y, fY2); Q2(v, color, color); Q2(v, u, fOffset + fOffsetU); Q2(v, v, 1.0f); Q3(v, x, fX); Q3(v, y, fY2); Q3(v, color, color); Q3(v, u, fOffset); Q3(v, v, 1.0f); fX += kFontCharWidth + 1; v += 6; } EndVertices(); } } // namespace ui } // namespace xe