Mission Vishwakarma Vishwakarma Download Roadmap Pricing

User Interface

We follow the standard ribbon interface for UI. Further, supporting all indian scheduled languages is must. This will als enable us to support international languages in future. Please refer below actual code files for user interface design specifications. We have baseline strings say N numbers in English. Each string can have a corresponding language translation or if the language translation is empty string, than the corresponding english text shall be followed. All translations are stored in UserInterface-Text.h file.

Supporting all Indian languages is mostly a data organization + text shaping problem, not a rendering problem. Renderer will just draw glyphs; the system around it decides which string to show.

Translation Document

  1// Copyright (c) 2026-Present : Ram Shanker: All rights reserved.
  2#pragma once
  3
  4enum class UILanguage : uint8_t
  5{
  6    English = 0,
  7
  8    // 22 Indian scheduled languages
  9    /* Here is the list of the given languages arranged in descending order of the number of speakers.
 10    2011 Census of India data for total speakers, including both native/mother tongue and second-language speakers where reported,
 11    as this is the most comprehensive official source available). */
 12
 13    Hindi, // ~528–691 million total speakers; ~43.63% of India's population as native speakers alone)
 14    Bengali, // ~97–107 million
 15    Marathi, // ~83–99 million
 16    Telugu, // ~81–94 million
 17    Tamil, // ~69–76 million
 18    Gujarati, // ~55–60 million
 19    Urdu, // ~50–63 million
 20    Kannada, // ~43–58 million
 21    Odia, // ~37–42 million
 22    Malayalam, // ~34–35 million
 23    Punjabi, // ~33–36 million
 24    Assamese, // ~15–23 million
 25    Maithili, // ~13–14 million, based on ~1.12% share)
 26    Santali, // ~7.3–7.7 million
 27    Kashmiri, // ~6.8–7 million
 28    Nepali, // ~2.9–3 million
 29    Sindhi, // ~2.7–3 million
 30    Dogri, // ~2.6–2.8 million
 31    Konkani, // ~2.2–2.6 million
 32    Manipuri, // (Meitei) ~1.7–2 million
 33    Bodo, // ~1.4–1.6 million
 34    Sanskrit, // ~25,000 native speakers; higher if including those reporting knowledge, but still by far the smallest)
 35
 36    // Major global engineering languages. Population number by Grok citing 
 37    ChineseSimplified, // Both Chinese combined ~1.18–1.20 billion total speakers (mostly native)
 38    ChineseTraditional, //(Mandarin Chinese)
 39    Spanish, // ~558–560 million
 40    Portuguese, // ~264–270 million
 41    Russian, // ~253–260 million
 42    French, // ~312–330 million (some sources place it slightly above or near Arabic depending on L2 counting)
 43    Arabic, // ~335 million (Modern Standard Arabic + varieties; widely used in engineering contexts across the Middle East)
 44    Indonesian, // ~200–255 million
 45    German, // ~130–134 million
 46    Japanese, // ~125–126 million. Covers all of Katakana , Kanji and Hiragana symbols within same fonts.
 47    Vietnamese, // ~85–97 million
 48    Turkish, // ~80–90 million
 49    Persian, // (Farsi) — ~70–82 million. Farsi - Iran engineering market
 50    Korean, // ~80–85 million
 51    Italian, // ~65–90 million
 52    Thai, // ~60–70 million
 53    Polish, // ~45–50 million
 54    Ukrainian, // ~35–45 million
 55    Dutch, // ~25–30 million
 56    Filipino, // (Tagalog) ~80–90 million total (native ~25–30 million + significant L2 in Philippines)
 57    Swedish, // ~10–15 million
 58    Czech, // ~10–12 million
 59    Hungarian, // ~12–14 million
 60
 61    COUNT
 62};
 63
 64/*
 65ChatGPT analysis of population coverage by above 46 languages:
 66
 67| Metric                         | Result     |
 68| ------------------------------ | ---------- |
 69| World population coverage      | **90–94%** |
 70| Engineering workforce coverage | **97–99%** |
 71| India coverage                 | **~99%**   |
 72| Europe coverage                | **~95%**   |
 73| Americas coverage              | **~95%**   |
 74
 75All these 46 languages translate to 13 unique scripts. Unicode handles all of them well.
 76
 77| Script        | Languages                             |
 78| ------------- | ------------------------------------- |
 79| Latin         | English, German, French, Spanish, etc |
 80| Cyrillic      | Russian, Ukrainian, Bulgarian, etc    |
 81| Devanagari    | Hindi, Marathi, Nepali, Sanskrit etc  |
 82| Bengali       | Bengali, Assamese                     |
 83| Gurmukhi      | Punjabi                               |
 84| Gujarati      | Gujarati                              |
 85| Odia          | Odia                                  |
 86| Tamil         | Tamil                                 |
 87| Telugu        | Telugu                                |
 88| Kannada       | Kannada                               |
 89| Malayalam     | Malayalam                             |
 90| Meetei Mayek  | Manipuri (Meitei)                     |
 91| Ol Chiki      | Santali                               |
 92| Arabic script | Urdu, Arabic, Persian, Kashmiri       |
 93| Chinese Han   | Chinese + Japanese Kanji              |
 94| Japanese kana | Hiragana/Katakana                     |
 95| Hangul        | Korean                                |
 96| Thai          | Thai                                  |
 97
 98Professional CAD software language coverage (As per ChatGPT).
 99| Software   | Languages |
100| ---------- | --------- |
101| AutoCAD    | ~15       |
102| SolidWorks | ~13       |
103| Fusion360  | ~10       |
104| CATIA      | ~8        |
105All softwares listed below are copy right of respective software companies.
106
107HENCE OUR LANGUAGE LIST IS FROZEN ! ;)
108
109Estimated size overhead of bundling all the fonts:
110
111| Font                         | Typical Size |
112| ---------------------------- | ------------ |
113| Noto Sans (Latin + extended) | ~2 MB        |
114| Noto Sans Devanagari         | ~1.5 MB      |
115| Noto Sans Bengali            | ~1.3 MB      |
116| Noto Sans Gurmukhi           | ~0.9 MB      |
117| Noto Sans Gujarati           | ~1.0 MB      |
118| Noto Sans Oriya (Odia)       | ~1.1 MB      |
119| Noto Sans Tamil              | ~0.9 MB      |
120| Noto Sans Telugu             | ~1.2 MB      |
121| Noto Sans Kannada            | ~1.2 MB      |
122| Noto Sans Malayalam          | ~1.4 MB      |
123| Noto Sans Arabic             | ~1.2 MB      |
124| Noto Sans Thai               | ~0.7 MB      |
125
126Subtotal (non-CJK): ≈ 14–15 MB
127
128| Font                                   | Approx Size |
129| -------------------------------------- | ----------- |
130| Noto Sans CJK SC (Simplified Chinese)  | ~16–18 MB   |
131| Noto Sans CJK TC (Traditional Chinese) | ~16–18 MB   |
132| Noto Sans CJK JP (Japanese)            | ~16–18 MB   |
133| Noto Sans CJK KR (Korean)              | ~16–18 MB   |
134
135CJK 3 variants (SC + JP + KR): ≈ 48–54 MB
136
137Total: ≈ 65 MB , ~60% Compression expected in Installer. ≈ 40 MB. Acceptable.
138
139Runtime: Entire font files will not be loaded at runtime.
140They will be loaded on demand to minimize memory footprint.
141
142*/

User Interface Design Document and Implementation!

 1// Copyright (c) 2026-Present : Ram Shanker: All rights reserved.
 2#pragma once
 3
 4#define WIN32_LEAN_AND_MEAN
 5#define NOMINMAX   //
 6#include <windows.h>   // MUST be before d3d12.h
 7#include <d3d12.h>
 8#include <d3dx12.h>
 9#include <dxgi1_6.h>
10#include <wrl.h>
11#include <vector>
12#include <array>
13#include <unordered_map>
14#include <iostream>
15#include <atomic>
16
17#include "ConstantsApplication.h"
18#include "UserInterface.h" // It also includes "UserInterface-TextTranslations.h"
19
20// Do not #include "विश्वकर्मा.h" otherwise it will lead to circular dependency error. Declare this struct exist.
21struct SingleUIWindow; // Add this forward declaration:
22
23using Microsoft::WRL::ComPtr;
24
25struct DX12ResourcesUI { // GPU resources
26    std::array<ComPtr<ID3D12Resource>, UI_MAX_ATLAS_TEXTURES> uiAtlasTextures; // 1024×1024 or 2048×2048 RGBA (or R8 for alpha-only)
27    ComPtr<ID3D12Resource> uiVertexBuffer; // Dynamic upload buffer for vertices
28    ComPtr<ID3D12Resource> uiIndexBuffer;  // Dynamic upload buffer for indices
29
30    UINT8* pVertexDataBegin = nullptr; // Mapped pointer for immediate writing
31    UINT8* pIndexDataBegin = nullptr;
32    UINT8* pOrthoDataBegin = nullptr;
33
34    ComPtr<ID3D12PipelineState> uiPSO;
35    ComPtr<ID3D12RootSignature> uiRootSignature;
36    ComPtr<ID3D12Resource> uiOrthoConstantBuffer;
37    ComPtr<ID3D12DescriptorHeap> srvHeap;
38    ComPtr<ID3D12DescriptorHeap> samplerHeap;
39
40    uint32_t maxVertices = 65536;
41    uint32_t maxIndices = 65536 * 3;
42};
43
44struct UIDrawContext { // Draw context
45    UIVertex* vertexPtr;
46    uint16_t* indexPtr;
47    uint32_t vertexCount, indexCount;
48};
49
50// DirectX12 Immediate Mode UI System (Phase 4A). Tab Bar Rendering Only
51// External interfaces of User Interface sub module of the code.
52void InitUIResources(DX12ResourcesUI& uiRes, ID3D12Device* device);
53void CleanupUIResources(DX12ResourcesUI& uiRes);
54
55void PushRect(UIDrawContext& ctx, float x, float y, float w, float h, uint32_t color, DX12ResourcesUI& uiRes);
56void PushRoundedRectangle(UIDrawContext& ctx, float x, float y, float w, float h, float radiusPx,
57    uint32_t color, DX12ResourcesUI& uiRes);
58void PushTopRoundedRectangle(UIDrawContext& ctx, float x, float y, float w, float h, float radiusPx,
59    uint32_t color, DX12ResourcesUI& uiRes);
60void PushText(UIDrawContext& ctx, float x, float y, const char* text, uint32_t color, DX12ResourcesUI& uiRes);
61
62// Slots 0 and 1 are currently reserved for the mandatory English and Icon atlases.
63// Future script atlases can use slots [UI_FIRST_DYNAMIC_SCRIPT_ATLAS_SLOT, UI_MAX_ATLAS_TEXTURES).
64bool UploadUIAtlasTexture(DX12ResourcesUI& uiRes, ID3D12Device* device, uint32_t atlasSlot,
65    const AtlasBitmap& atlas);
66
67void PrecomputeTopRibbonLayout(UITopRibbonLayout& layout, float monitorDPIX, float monitorDPIY);
68
69void RenderUIOverlay(SingleUIWindow& window, ID3D12GraphicsCommandList* cmdList,
70    DX12ResourcesUI& uiRes, UITopRibbonLayout& topRibbonLayout,
71    float monitorDPIX, float monitorDPIY, const UIInput& input);
   1// Copyright (c) 2026-Present : Ram Shanker: All rights reserved.
   2
   3#include "UserInterface-DirectX12.h"
   4#include <algorithm>
   5#include <d3dcompiler.h>
   6#include "ShaderUIVertex.h"
   7#include "ShaderUIPixel.h"
   8#include "FontManager.h"
   9#include "..\build\NotoSansMSDF_Compiled.h"
  10#include <MemoryManagerGPU-DirectX12.h>
  11#include "विश्वकर्मा.h"
  12#include "TextureSaver.h"
  13#include "UserInterfaceTranslationCompiled.h"
  14#include <array>
  15#include <cmath>
  16extern शंकर gpu;
  17extern std::atomic<uint16_t*> publishedTabIndexes;
  18extern std::atomic<uint16_t>  publishedTabCount;
  19extern void PrintHResult(int);
  20std::atomic<uint32_t> actionWriteIndex;
  21// ASCII Character set.
  22std::string charset = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
  23std::atomic<uint64_t> atlasFence = 0;
  24
  25struct UIAtlasRegion {
  26    float uvMinX = 0.0f;
  27    float uvMinY = 0.0f;
  28    float uvMaxX = 0.0f;
  29    float uvMaxY = 0.0f;
  30};
  31
  32struct UIRoundedRectangleNineSlice {
  33    std::array<std::array<UIAtlasRegion, 3>, 3> regions{};
  34};
  35
  36struct UIIconAtlasMetadata {
  37    UIRoundedRectangleNineSlice roundedRectangle{};
  38    std::array<char32_t, 4> dummyIconCodepoints{ U'\uE100', U'\uE101', U'\uE102', U'\uE103' };
  39    std::vector<char32_t> mixedIconCodepoints{};
  40};
  41
  42static UIIconAtlasMetadata gIconAtlasMetadata{};
  43
  44static UIAtlasRegion MakeAtlasRegion(int x, int y, int w, int h, int atlasW, int atlasH) {
  45    UIAtlasRegion region{};
  46    region.uvMinX = (float)x / (float)atlasW;
  47    region.uvMinY = (float)y / (float)atlasH;
  48    region.uvMaxX = (float)(x + w) / (float)atlasW;
  49    region.uvMaxY = (float)(y + h) / (float)atlasH;
  50    return region;
  51}
  52
  53UIColors uiLightDefaultColors, uiActiveColors; // Initialized to default light theme colors.
  54
  55static void GenerateRoundedRectangleNineSlice(AtlasBitmap& atlas, int originX, int originY,
  56    int sourceSizePx, int sourceRadiusPx, UIRoundedRectangleNineSlice& outSlice) {
  57    const int atlasW = atlas.width;
  58    const int atlasH = atlas.height;
  59
  60    for (int y = 0; y < sourceSizePx; ++y) {
  61        for (int x = 0; x < sourceSizePx; ++x) {
  62            const float px = (float)x + 0.5f;
  63            const float py = (float)y + 0.5f;
  64            const float nearestX = std::clamp(px, (float)sourceRadiusPx,
  65                (float)(sourceSizePx - sourceRadiusPx));
  66            const float nearestY = std::clamp(py, (float)sourceRadiusPx,
  67                (float)(sourceSizePx - sourceRadiusPx));
  68            const float dx = px - nearestX;
  69            const float dy = py - nearestY;
  70            const float distance = std::sqrt(dx * dx + dy * dy);
  71            const float coverage = std::clamp((float)sourceRadiusPx + 0.5f - distance, 0.0f, 1.0f);
  72
  73            atlas.pixels[(originY + y) * atlasW + (originX + x)] = (uint8_t)std::round(coverage * 255.0f);
  74        }
  75    }
  76
  77    const int middle = sourceSizePx - 2 * sourceRadiusPx;
  78    const int widths[3] = { sourceRadiusPx, middle, sourceRadiusPx };
  79    const int heights[3] = { sourceRadiusPx, middle, sourceRadiusPx };
  80    int yCursor = originY;
  81    for (int row = 0; row < 3; ++row) {
  82        int xCursor = originX;
  83        for (int col = 0; col < 3; ++col) {
  84            outSlice.regions[row][col] =
  85                MakeAtlasRegion(xCursor, yCursor, widths[col], heights[row], atlasW, atlasH);
  86            xCursor += widths[col];
  87        }
  88        yCursor += heights[row];
  89    }
  90}
  91
  92static void FillRect(AtlasBitmap& atlas, int x, int y, int w, int h, uint8_t coverage) {
  93    for (int yy = y; yy < y + h; ++yy) {
  94        for (int xx = x; xx < x + w; ++xx) {
  95            atlas.pixels[yy * atlas.width + xx] = coverage;
  96        }
  97    }
  98}
  99
 100static bool IsPrivateUseCodepoint(char32_t codepoint) {
 101    return (codepoint >= 0xE000 && codepoint <= 0xF8FF) ||
 102        (codepoint >= 0xF0000 && codepoint <= 0xFFFFD) ||
 103        (codepoint >= 0x100000 && codepoint <= 0x10FFFD);
 104}
 105
 106static bool TryReserveIconCell(int iconIndex, int atlasW, int atlasH, int& outX, int& outY) {
 107    constexpr int iconCellSize = 24;
 108    constexpr int iconCellGap = 4;
 109    constexpr int iconStartY = 48;
 110    const int cellsPerRow = (atlasW + iconCellGap) / (iconCellSize + iconCellGap);
 111    if (cellsPerRow <= 0) return false;
 112
 113    outX = (iconIndex % cellsPerRow) * (iconCellSize + iconCellGap);
 114    outY = iconStartY + (iconIndex / cellsPerRow) * (iconCellSize + iconCellGap);
 115    return outX + iconCellSize <= atlasW && outY + iconCellSize <= atlasH;
 116}
 117
 118static void StoreIconCellGlyph(char32_t codepoint, int x, int y, int atlasW, int atlasH) {
 119    constexpr int iconCellSize = 24;
 120    Glyph glyph{};
 121    glyph.uvMinX = (float)x / atlasW;
 122    glyph.uvMinY = (float)y / atlasH;
 123    glyph.uvMaxX = (float)(x + iconCellSize) / atlasW;
 124    glyph.uvMaxY = (float)(y + iconCellSize) / atlasH;
 125    glyph.width = iconCellSize;
 126    glyph.height = iconCellSize;
 127    glyph.advanceX = iconCellSize;
 128    iconGlyphLookup[codepoint] = glyph;
 129    gIconAtlasMetadata.mixedIconCodepoints.push_back(codepoint);
 130}
 131
 132static AtlasBitmap BuildIconAtlas() {
 133    constexpr int atlasW = 256;
 134    constexpr int atlasH = 256;
 135    constexpr int iconCellSize = 24;
 136    constexpr int proceduralIconDrawSize = 20;
 137    AtlasBitmap atlas{};
 138    atlas.width = atlasW;
 139    atlas.height = atlasH;
 140    atlas.pixels.resize(atlasW * atlasH, 0);
 141
 142    // The source rounded rectangle is split into 9 texture regions at draw time.
 143    // Destination corners are resized to ~2 mm in screen space by PushRoundedRectangle.
 144    GenerateRoundedRectangleNineSlice(atlas, 0, 0, 32, 8, gIconAtlasMetadata.roundedRectangle);
 145
 146    iconGlyphLookup.clear();
 147    gIconAtlasMetadata.mixedIconCodepoints.clear();
 148
 149    std::array<int, 4> iconXs{};
 150    std::array<int, 4> iconYs{};
 151    for (int i = 0; i < 4; ++i) {
 152        if (!TryReserveIconCell(i, atlasW, atlasH, iconXs[i], iconYs[i])) continue;
 153        StoreIconCellGlyph(gIconAtlasMetadata.dummyIconCodepoints[i], iconXs[i], iconYs[i], atlasW, atlasH);
 154    }
 155
 156    // Dummy icon 0: plus
 157    FillRect(atlas, iconXs[0] + 10, iconYs[0] + 4, 4, 16, 255);
 158    FillRect(atlas, iconXs[0] + 4, iconYs[0] + 10, 16, 4, 255);
 159
 160    // Dummy icon 1: folder-like block
 161    FillRect(atlas, iconXs[1] + 4, iconYs[1] + 8, 16, 11, 255);
 162    FillRect(atlas, iconXs[1] + 6, iconYs[1] + 5, 7, 4, 255);
 163
 164    // Dummy icon 2: ring
 165    for (int y = 0; y < proceduralIconDrawSize; ++y) {
 166        for (int x = 0; x < proceduralIconDrawSize; ++x) {
 167            const float dx = (float)x + 0.5f - 10.0f;
 168            const float dy = (float)y + 0.5f - 10.0f;
 169            const float d = std::sqrt(dx * dx + dy * dy);
 170            if (d >= 5.0f && d <= 8.0f) {
 171                atlas.pixels[(iconYs[2] + 2 + y) * atlasW + (iconXs[2] + 2 + x)] = 255;
 172            }
 173        }
 174    }
 175
 176    // Dummy icon 3: 2x2 grid
 177    FillRect(atlas, iconXs[3] + 4, iconYs[3] + 4, 7, 7, 255);
 178    FillRect(atlas, iconXs[3] + 13, iconYs[3] + 4, 7, 7, 255);
 179    FillRect(atlas, iconXs[3] + 4, iconYs[3] + 13, 7, 7, 255);
 180    FillRect(atlas, iconXs[3] + 13, iconYs[3] + 13, 7, 7, 255);
 181
 182    if (ftIconFace) {
 183        FT_Set_Pixel_Sizes(ftIconFace, 0, proceduralIconDrawSize);
 184
 185        FT_UInt glyphIndex = 0;
 186        FT_ULong charCode = FT_Get_First_Char(ftIconFace, &glyphIndex);
 187        int iconIndex = (int)gIconAtlasMetadata.mixedIconCodepoints.size();
 188        while (glyphIndex != 0) {
 189            const char32_t codepoint = (char32_t)charCode;
 190            if (IsPrivateUseCodepoint(codepoint) &&
 191                std::find(gIconAtlasMetadata.dummyIconCodepoints.begin(),
 192                    gIconAtlasMetadata.dummyIconCodepoints.end(), codepoint) ==
 193                gIconAtlasMetadata.dummyIconCodepoints.end() &&
 194                FT_Load_Char(ftIconFace, charCode, FT_LOAD_RENDER) == 0) {
 195                int cellX = 0;
 196                int cellY = 0;
 197                if (!TryReserveIconCell(iconIndex, atlasW, atlasH, cellX, cellY)) break;
 198
 199                FT_GlyphSlot g = ftIconFace->glyph;
 200                const int bitmapX = cellX + std::max(0, (iconCellSize - (int)g->bitmap.width) / 2);
 201                const int bitmapY = cellY + std::max(0, (iconCellSize - (int)g->bitmap.rows) / 2);
 202                const int copyW = std::min((int)g->bitmap.width, iconCellSize);
 203                const int copyH = std::min((int)g->bitmap.rows, iconCellSize);
 204                for (int y = 0; y < copyH; ++y) {
 205                    for (int x = 0; x < copyW; ++x) {
 206                        atlas.pixels[(bitmapY + y) * atlasW + (bitmapX + x)] =
 207                            g->bitmap.buffer[y * g->bitmap.pitch + x];
 208                    }
 209                }
 210
 211                StoreIconCellGlyph(codepoint, cellX, cellY, atlasW, atlasH);
 212                ++iconIndex;
 213            }
 214
 215            charCode = FT_Get_Next_Char(ftIconFace, charCode, &glyphIndex);
 216        }
 217    }
 218
 219    return atlas;
 220}
 221
 222static AtlasBitmap BuildMSDFFontAtlas() {
 223    AtlasBitmap atlas{};
 224    atlas.width = NotoSansMSDF_Width;
 225    atlas.height = NotoSansMSDF_Height;
 226    atlas.bytesPerPixel = 4;
 227    atlas.pixels.assign(NotoSansMSDF_Pixels,
 228        NotoSansMSDF_Pixels + (size_t)atlas.width * (size_t)atlas.height * (size_t)atlas.bytesPerPixel);
 229
 230    glyphLookup.clear();
 231    for (const auto& entry : NotoSansMSDF_Glyphs) {
 232        const char32_t codepoint = entry.first;
 233        const MSDFGlyph& msdf = entry.second;
 234
 235        Glyph glyph{};
 236        glyph.uvMinX = msdf.atlasLeft / (float)atlas.width;
 237        glyph.uvMaxX = msdf.atlasRight / (float)atlas.width;
 238        glyph.uvMinY = ((float)atlas.height - msdf.atlasTop) / (float)atlas.height;
 239        glyph.uvMaxY = ((float)atlas.height - msdf.atlasBottom) / (float)atlas.height;
 240
 241        glyph.width = std::max(0, (int)std::ceil((msdf.planeRight - msdf.planeLeft) * NotoSansMSDF_Size));
 242        glyph.height = std::max(0, (int)std::ceil((msdf.planeTop - msdf.planeBottom) * NotoSansMSDF_Size));
 243        glyph.bearingX = (int)std::floor(msdf.planeLeft * NotoSansMSDF_Size);
 244        glyph.bearingY = (int)std::ceil(msdf.planeTop * NotoSansMSDF_Size);
 245        glyph.advanceX = std::max(0, (int)std::round(msdf.advance * NotoSansMSDF_Size));
 246
 247        glyphLookup[codepoint] = glyph;
 248    }
 249
 250    return atlas;
 251}
 252
 253static uint32_t StableRandomUIColour(uint32_t seed) {
 254    seed ^= seed >> 16;
 255    seed *= 0x7FEB352Du;
 256    seed ^= seed >> 15;
 257    seed *= 0x846CA68Bu;
 258    seed ^= seed >> 16;
 259
 260    uint32_t r = 80u + ((seed >> 0) & 0x7Fu);
 261    uint32_t g = 80u + ((seed >> 8) & 0x7Fu);
 262    uint32_t b = 80u + ((seed >> 16) & 0x7Fu);
 263    return 0xFF000000u | (b << 16) | (g << 8) | r;
 264}
 265
 266bool SubmitTextureUpload(const TextureUploadDesc& desc,
 267    ComPtr<ID3D12Resource>* outTex,  std::atomic<uint64_t>* fenceOut) {
 268
 269    uint32_t index = gUploadQueue.writeIndex.fetch_add(1, std::memory_order_relaxed);
 270    UploadRequest& req = gUploadQueue.requests[index % MAX_UPLOAD_REQUESTS];
 271
 272    req.type = UploadType::Texture2D;
 273    req.texture = desc;
 274    req.outResource = outTex;
 275    req.completionFence = fenceOut;
 276
 277    return true;
 278}
 279
 280bool UploadUIAtlasTexture(DX12ResourcesUI& uiRes, ID3D12Device* device, uint32_t atlasSlot,
 281    const AtlasBitmap& atlas) {
 282    if (!device || atlasSlot >= UI_MAX_ATLAS_TEXTURES || atlas.width <= 0 || atlas.height <= 0 ||
 283        atlas.pixels.empty() || (atlas.bytesPerPixel != 1 && atlas.bytesPerPixel != 4)) {
 284        return false;
 285    }
 286
 287    TextureUploadDesc desc = {};
 288    desc.width = atlas.width;
 289    desc.height = atlas.height;
 290    desc.format = atlas.bytesPerPixel == 4 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8_UNORM;
 291    desc.pixels = atlas.pixels.data();
 292    desc.rowPitch = atlas.width * atlas.bytesPerPixel;
 293
 294    std::atomic<uint64_t> uploadFence = 0;
 295    SubmitTextureUpload(desc, &uiRes.uiAtlasTextures[atlasSlot], &uploadFence);
 296
 297    uint64_t atlasReadyFence = gpu.copyFenceValue.fetch_add(1, std::memory_order_relaxed);
 298    uploadFence.store(atlasReadyFence, std::memory_order_release);
 299    toCopyThreadCV.notify_one();
 300
 301    if (gpu.copyFence->GetCompletedValue() < atlasReadyFence) {
 302        ThrowIfFailed(gpu.copyFence->SetEventOnCompletion(atlasReadyFence, gpu.copyFenceEvent));
 303        WaitForSingleObject(gpu.copyFenceEvent, INFINITE);
 304    }
 305
 306    D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
 307    srvDesc.Format = desc.format;
 308    srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
 309    srvDesc.Texture2D.MipLevels = 1;
 310    srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
 311
 312    CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle(uiRes.srvHeap->GetCPUDescriptorHandleForHeapStart(),
 313        atlasSlot, device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV));
 314    device->CreateShaderResourceView(uiRes.uiAtlasTextures[atlasSlot].Get(), &srvDesc, srvHandle);
 315    return true;
 316}
 317
 318void InitUIResources( DX12ResourcesUI& uiRes, ID3D12Device* device) {
 319    if (!InitFontSystem()) { // FONT system initialization (CPU-side)
 320        std::cerr << "Font system initialization failed failed\n";
 321        return;
 322    }
 323
 324    // Root signature
 325    CD3DX12_DESCRIPTOR_RANGE1 ranges[2]; // Descriptor ranges
 326
 327    // Range 0: SRV (t0)  → from srvHeap // 1: 1 Texture, 0: register t0 = atlas
 328    ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, UI_MAX_ATLAS_TEXTURES, 0, 0,
 329        D3D12_DESCRIPTOR_RANGE_FLAG_NONE);
 330    // Range 1: SAMPLER (s0) → from samplerHeap // 1: 1 Sampler, 0: register s0 = sampler
 331    ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_NONE);
 332
 333    CD3DX12_ROOT_PARAMETER1 rootParams[3];
 334    rootParams[0].InitAsConstantBufferView(0, 0, D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
 335        D3D12_SHADER_VISIBILITY_VERTEX);// b0 - Ortho constant buffer (vertex shader)
 336
 337    // Root Parameter 1: Descriptor Table containing only the SRV
 338    rootParams[1].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
 339
 340    // Root Parameter 2: Descriptor Table containing only the SAMPLER
 341    rootParams[2].InitAsDescriptorTable(1, &ranges[1],
 342        D3D12_SHADER_VISIBILITY_PIXEL);
 343
 344    CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootDesc;
 345    rootDesc.Init_1_1(_countof(rootParams), rootParams, 0, nullptr,
 346        D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
 347
 348    ComPtr<ID3DBlob> signature;
 349    ComPtr<ID3DBlob> errorBlob;
 350
 351    HRESULT hr = D3DX12SerializeVersionedRootSignature(&rootDesc,
 352        D3D_ROOT_SIGNATURE_VERSION_1_1, &signature, &errorBlob);
 353    if (FAILED(hr)) {
 354        if (errorBlob)
 355            std::cerr << "Root Signature Serialization Failed:\n"
 356            << (char*)errorBlob->GetBufferPointer() << std::endl;
 357        ThrowIfFailed(hr); // will print the real error
 358    }
 359
 360    ThrowIfFailed(device->CreateRootSignature(0, signature->GetBufferPointer(),
 361        signature->GetBufferSize(), IID_PPV_ARGS(&uiRes.uiRootSignature)));
 362    
 363    // Create SRV descriptor heap (1 texture)
 364    D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
 365    heapDesc.NumDescriptors = UI_MAX_ATLAS_TEXTURES;
 366    heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
 367    heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
 368    ThrowIfFailed(device->CreateDescriptorHeap( &heapDesc, IID_PPV_ARGS(&uiRes.srvHeap) ));
 369
 370    // Create SAMPLER descriptor heap (shader-visible)
 371    D3D12_DESCRIPTOR_HEAP_DESC samplerHeapDesc = {};
 372    samplerHeapDesc.NumDescriptors = 1;
 373    samplerHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
 374    samplerHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
 375    ThrowIfFailed(device->CreateDescriptorHeap(&samplerHeapDesc,
 376        IID_PPV_ARGS(&uiRes.samplerHeap)));
 377
 378    // Create the actual sampler.
 379    D3D12_SAMPLER_DESC samplerDesc = {};
 380    samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
 381    samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
 382    samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
 383    samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
 384    samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
 385    samplerDesc.MinLOD = 0;
 386    samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
 387
 388    device->CreateSampler(&samplerDesc,
 389        uiRes.samplerHeap->GetCPUDescriptorHandleForHeapStart());
 390
 391    // Shaders are compiled to DXIL during the build and embedded into the executable.
 392    // Input layout
 393
 394    D3D12_INPUT_ELEMENT_DESC layout[] = {
 395        { "POSITION",0,DXGI_FORMAT_R32G32_FLOAT,0,0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
 396        { "TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
 397        { "COLOR",0,DXGI_FORMAT_R32_UINT,0,16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
 398        { "TEXCOORD",1,DXGI_FORMAT_R32_UINT,0,20, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 }
 399    };
 400
 401    // PSO
 402    D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
 403    psoDesc.InputLayout = { layout,_countof(layout) };
 404    psoDesc.pRootSignature = uiRes.uiRootSignature.Get();
 405    psoDesc.VS = CD3DX12_SHADER_BYTECODE(g_uiVertexShader, sizeof(g_uiVertexShader));
 406    psoDesc.PS = CD3DX12_SHADER_BYTECODE(g_uiPixelShader, sizeof(g_uiPixelShader));
 407    psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
 408    psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
 409    psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
 410    psoDesc.BlendState.RenderTarget[0].BlendEnable = TRUE;
 411    psoDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
 412    psoDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
 413    psoDesc.DepthStencilState.DepthEnable = FALSE;
 414    psoDesc.SampleMask = UINT_MAX;
 415    psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
 416    psoDesc.NumRenderTargets = 1;
 417    psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
 418    psoDesc.SampleDesc.Count = 1;
 419
 420    ThrowIfFailed( device->CreateGraphicsPipelineState( &psoDesc, IID_PPV_ARGS(&uiRes.uiPSO)));
 421    
 422    // Vertex buffer
 423    auto uploadHeap = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
 424
 425    auto vbDesc = CD3DX12_RESOURCE_DESC::Buffer( uiRes.maxVertices * sizeof(UIVertex));
 426    ThrowIfFailed( device->CreateCommittedResource( &uploadHeap, D3D12_HEAP_FLAG_NONE, &vbDesc,
 427            D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uiRes.uiVertexBuffer)));
 428
 429    auto ibDesc = CD3DX12_RESOURCE_DESC::Buffer( uiRes.maxIndices * sizeof(uint16_t));
 430    ThrowIfFailed( device->CreateCommittedResource( &uploadHeap, D3D12_HEAP_FLAG_NONE, &ibDesc,
 431            D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uiRes.uiIndexBuffer)));
 432
 433    auto cbDesc = CD3DX12_RESOURCE_DESC::Buffer(256);
 434    ThrowIfFailed( device->CreateCommittedResource( &uploadHeap, D3D12_HEAP_FLAG_NONE, &cbDesc,
 435            D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uiRes.uiOrthoConstantBuffer)));
 436
 437    CD3DX12_RANGE readRange(0, 0);
 438    uiRes.uiVertexBuffer->Map( 0, &readRange, reinterpret_cast<void**>(&uiRes.pVertexDataBegin));
 439    uiRes.uiIndexBuffer->Map(  0, &readRange, reinterpret_cast<void**>(&uiRes.pIndexDataBegin));
 440    uiRes.uiOrthoConstantBuffer->Map( 0, &readRange, reinterpret_cast<void**>(&uiRes.pOrthoDataBegin));
 441    std::wcout << L"UI Resources Initialized (Phase 4A)\n";
 442    
 443    AtlasBitmap englishAtlas = BuildMSDFFontAtlas();
 444    AtlasBitmap iconAtlas = BuildIconAtlas();
 445    
 446    TextureUploadDesc desc = {};
 447    desc.width = englishAtlas.width;
 448    desc.height = englishAtlas.height;
 449    desc.format = DXGI_FORMAT_R8G8B8A8_UNORM;
 450    desc.pixels = englishAtlas.pixels.data();
 451    desc.rowPitch = englishAtlas.width * englishAtlas.bytesPerPixel;
 452
 453    int bytesPerPixel = 1;
 454
 455    SubmitTextureUpload(desc, &uiRes.uiAtlasTextures[UI_ENGLISH_ATLAS_SLOT], &atlasFence);// Enqueue the upload through upload queue
 456    // RESERVED FENCE VALUE FOR THIS UPLOAD (this is the key change)
 457    // The copy thread MUST eventually signal exactly this value.
 458    uint64_t atlasReadyFence = gpu.copyFenceValue.fetch_add(1, std::memory_order_relaxed);
 459    // Tell everyone (including the render thread) what fence value to wait for
 460    atlasFence.store(atlasReadyFence, std::memory_order_release);
 461	toCopyThreadCV.notify_one(); // Wakeup CPU thread to process the newly uploaded texture.
 462    // CPU-blocking wait until Copy Queue has processed this upload
 463    if (gpu.copyFence->GetCompletedValue() < atlasReadyFence) {
 464        ThrowIfFailed(gpu.copyFence->SetEventOnCompletion(atlasReadyFence, gpu.copyFenceEvent));
 465        WaitForSingleObject(gpu.copyFenceEvent, INFINITE);   // CPU blocks here
 466    }
 467
 468    // Now the texture is in DEFAULT heap → safe to create SRV
 469    D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
 470    srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
 471    srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
 472    srvDesc.Texture2D.MipLevels = 1;
 473    srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
 474    
 475    device->CreateShaderResourceView(uiRes.uiAtlasTextures[UI_ENGLISH_ATLAS_SLOT].Get(), &srvDesc,
 476        uiRes.srvHeap->GetCPUDescriptorHandleForHeapStart());
 477
 478    SaveToBmp("icon_atlas_debug.bmp", iconAtlas.pixels.data(),
 479        iconAtlas.width, iconAtlas.height, bytesPerPixel);
 480    UploadUIAtlasTexture(uiRes, device, UI_ICON_ATLAS_SLOT, iconAtlas);
 481
 482    std::wcout << L"Mandatory UI atlases uploaded: English slot " << UI_ENGLISH_ATLAS_SLOT
 483        << L", icon slot " << UI_ICON_ATLAS_SLOT << L"\n";
 484}
 485
 486// Cleanup
 487void CleanupUIResources(DX12ResourcesUI& uiRes) {
 488    if (uiRes.uiVertexBuffer) uiRes.uiVertexBuffer->Unmap(0, nullptr);
 489    if (uiRes.uiIndexBuffer) uiRes.uiIndexBuffer->Unmap(0, nullptr);
 490    if (uiRes.uiOrthoConstantBuffer) uiRes.uiOrthoConstantBuffer->Unmap(0, nullptr);
 491
 492    uiRes = {};
 493}
 494
 495static float TextScaleForHeight(float targetHeight) {
 496    auto glyphIt = glyphLookup.find(U'M');
 497    if (glyphIt == glyphLookup.end() || glyphIt->second.height <= 0) return 1.0f;
 498
 499    return targetHeight / (float)glyphIt->second.height;
 500}
 501
 502static float MeasureUIStringWidth(const char32_t* text, float scale) {
 503    if (!text) return 0.0f;
 504
 505    float cursorX = 0.0f;
 506    float maxRight = 0.0f;
 507
 508    for (const char32_t* p = text; *p; ++p) {
 509        auto glyphIt = glyphLookup.find(*p);
 510        if (glyphIt == glyphLookup.end()) continue;
 511
 512        const Glyph& g = glyphIt->second;
 513        float glyphLeft = cursorX + (float)g.bearingX * scale;
 514        float glyphRight = glyphLeft + (float)g.width * scale;
 515        if (glyphRight > maxRight) maxRight = glyphRight;
 516        cursorX += (float)g.advanceX * scale;
 517    }
 518
 519    return std::max(cursorX, maxRight);
 520}
 521
 522static const char32_t* LocalizedUIString(UITextID stringID) {
 523    const char32_t* text = GetUILocalizedString(stringID, UILanguage::English);
 524    return text ? text : U"";
 525}
 526
 527static const char32_t* LocalizedControlLabel(const UIControlDefinition& ctrl) {
 528    const char32_t* label = LocalizedUIString(ctrl.nameStringID);
 529    if (*label != U'\0') return label;
 530
 531    if (ctrl.action == Commands::INVALID || ctrl.type == 0 || ctrl.type == 3) {
 532        return LocalizedUIString(ctrl.nameStringID);
 533    }
 534
 535    return U"";
 536}
 537
 538static void ClampTopRibbonScroll(UITopRibbonLayout& layout, float viewportWidth) {
 539    const float maxScroll = std::max(0.0f, layout.totalContentWidthPx - viewportWidth);
 540    layout.scrollOffsetPx = std::clamp(layout.scrollOffsetPx, 0.0f, maxScroll);
 541}
 542
 543void PrecomputeTopRibbonLayout(UITopRibbonLayout& layout, float monitorDPIX, float monitorDPIY) {
 544    const float previousScroll = layout.scrollOffsetPx;
 545    layout = UITopRibbonLayout{};
 546
 547    layout.dpiX = monitorDPIX;
 548    layout.dpiY = monitorDPIY;
 549
 550    const float pixelsPerMMx = monitorDPIX / 25.4f;
 551    const float pixelsPerMMy = monitorDPIY / 25.4f;
 552    layout.buttonWidthPx = std::round(UI_BUTTON_WIDTH_MM * pixelsPerMMx);
 553    layout.iconSizePx = std::round(UI_ICON_SIZE_MM * pixelsPerMMy);
 554    layout.textHeightPx = std::round(UI_TEXT_HEIGHT_MM * pixelsPerMMy);
 555    layout.buttonHeightPx = std::max(std::round(UI_BUTTON_HEIGHT_MM * pixelsPerMMy),
 556        std::max(layout.iconSizePx, layout.textHeightPx) + 4.0f);
 557    layout.iconReservedWidthPx = layout.iconSizePx + 4.0f;
 558    layout.textStartOffsetPx = layout.iconReservedWidthPx + 4.0f;
 559    layout.textEndInsetPx = 6.0f;
 560    layout.buttonGapPx = UI_BUTTON_GAP_MM * pixelsPerMMx;
 561    layout.tabBarHeightPx = std::round(UI_TAB_BAR_HEIGHT_MM * pixelsPerMMy);
 562    layout.roundedCornerRadiusPx = std::max(1.0f, std::round(UI_BUTTON_CORNER_RADIUS_MM * pixelsPerMMy));
 563    layout.uiTextScale = TextScaleForHeight(layout.textHeightPx);
 564    layout.actionGroupLabelY = (UI_TAB_BAR_HEIGHT_MM + UI_DIVIDER_GAP_PX) * pixelsPerMMy;
 565    layout.actionGroupLabelHeightPx = UI_ACTION_GROUP_LABEL_HEIGHT_MM * pixelsPerMMy;
 566    layout.topActionGroupY = (UI_TAB_BAR_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 567        UI_ACTION_GROUP_LABEL_HEIGHT_MM) * pixelsPerMMy + 5.0f;
 568    layout.actionSubGroupLabelY = (UI_TAB_BAR_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 569        UI_ACTION_GROUP_LABEL_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 570        UI_ACTION_GROUP_HEIGHT_MM + UI_DIVIDER_GAP_PX) * pixelsPerMMy + 5.0f;
 571    layout.topUITotalHeightPx = std::round((UI_TAB_BAR_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 572        UI_ACTION_GROUP_LABEL_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 573        UI_ACTION_GROUP_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 574        UI_ACTION_GROUP_LABEL_HEIGHT_MM + UI_DIVIDER_GAP_PX) * pixelsPerMMy) + 7.0f;
 575
 576
 577    const float groupNavPaddingPx = 8.0f;
 578    const float groupNavGapPx = 2.0f * pixelsPerMMx;
 579    float groupNavX = 0.0f;
 580    for (size_t i = 0; i < TotalTopUIActionGroups; ++i) {
 581        const char32_t* label = LocalizedUIString(topUIActionGroupNames[i].labelStringID);
 582        layout.actionGroups[i].navX = groupNavX;
 583        layout.actionGroups[i].navWidth = std::ceil(MeasureUIStringWidth(label, layout.uiTextScale) +
 584            2.0f * groupNavPaddingPx);
 585        groupNavX += layout.actionGroups[i].navWidth + groupNavGapPx;
 586    }
 587    layout.actionGroupNavTotalWidthPx = groupNavX > 0.0f ? groupNavX - groupNavGapPx : 0.0f;
 588
 589    std::array<bool, TotalTopUIActionGroups> groupSeen{};
 590    float currentX = 5.0f;
 591    float verticalSlotMaxSize = 0.0f;
 592    float contentRight = currentX;
 593    int activeSubGroupIndex = -1;
 594    size_t activeSubGroupRun = 0;
 595
 596    for (size_t i = 0; i < TotalUIControls; ++i) {
 597        const UIControlDefinition& ctrl = AllUIControls[i];
 598        const size_t groupIndex = ctrl.actionGroupIndex;
 599        const size_t subGroupIndex = ctrl.actionSubGroupIndex;
 600
 601        if (groupIndex < TotalTopUIActionGroups && !groupSeen[groupIndex]) {
 602            layout.actionGroups[groupIndex].contentStartX = currentX;
 603            layout.actionGroups[groupIndex].contentEndX = currentX;
 604            groupSeen[groupIndex] = true;
 605        }
 606
 607        if (subGroupIndex < TotalTopUIActionSubGroups &&
 608            !layout.actionSubGroups[subGroupIndex].hasControls) {
 609            layout.actionSubGroups[subGroupIndex].contentStartX = currentX;
 610            layout.actionSubGroups[subGroupIndex].contentEndX = currentX;
 611            layout.actionSubGroups[subGroupIndex].hasControls = true;
 612        }
 613
 614        if (activeSubGroupIndex != (int)subGroupIndex &&
 615            layout.actionSubGroupRunCount < layout.actionSubGroupRuns.size()) {
 616            activeSubGroupIndex = (int)subGroupIndex;
 617            activeSubGroupRun = layout.actionSubGroupRunCount++;
 618            layout.actionSubGroupRuns[activeSubGroupRun].subGroupIndex = ctrl.actionSubGroupIndex;
 619            layout.actionSubGroupRuns[activeSubGroupRun].contentStartX = currentX;
 620            layout.actionSubGroupRuns[activeSubGroupRun].contentEndX = currentX;
 621        }
 622
 623        float btnWidth = layout.buttonWidthPx;
 624        float btnY = layout.topActionGroupY;
 625        const char32_t* label = LocalizedControlLabel(ctrl);
 626        if (ctrl.showText && *label != U'\0') {
 627            float contentWidth = layout.textStartOffsetPx +
 628                MeasureUIStringWidth(label, layout.uiTextScale) + layout.textEndInsetPx;
 629            btnWidth = std::max(btnWidth, contentWidth);
 630        }
 631        if (ctrl.noOfVerticalSlots > 1) {
 632            btnY += ctrl.verticalSlotNo * (layout.buttonHeightPx + 1.0f);
 633        }
 634
 635        layout.controls[i] = { currentX, btnY, btnWidth, layout.buttonHeightPx };
 636        verticalSlotMaxSize = std::max(verticalSlotMaxSize, btnWidth);
 637
 638        const bool endOfColumn = (i + 1 == TotalUIControls) || (AllUIControls[i + 1].verticalSlotNo == 0);
 639        if (endOfColumn) {
 640            const float columnRight = currentX + verticalSlotMaxSize;
 641            if (groupIndex < TotalTopUIActionGroups) {
 642                layout.actionGroups[groupIndex].contentEndX =
 643                    std::max(layout.actionGroups[groupIndex].contentEndX, columnRight);
 644            }
 645            if (subGroupIndex < TotalTopUIActionSubGroups) {
 646                layout.actionSubGroups[subGroupIndex].contentEndX =
 647                    std::max(layout.actionSubGroups[subGroupIndex].contentEndX, columnRight);
 648            }
 649            if (layout.actionSubGroupRunCount > 0) {
 650                layout.actionSubGroupRuns[activeSubGroupRun].contentEndX =
 651                    std::max(layout.actionSubGroupRuns[activeSubGroupRun].contentEndX, columnRight);
 652            }
 653            contentRight = std::max(contentRight, columnRight);
 654
 655            if (i + 1 < TotalUIControls) {
 656                currentX = columnRight + layout.buttonGapPx;
 657            }
 658            verticalSlotMaxSize = 0.0f;
 659        }
 660    }
 661
 662    for (size_t gIdx = 0; gIdx < TotalTopUIActionGroups; ++gIdx) {
 663        layout.actionGroups[gIdx].contentWidth = std::max(0.0f, layout.actionGroups[gIdx].contentEndX - layout.actionGroups[gIdx].contentStartX);
 664    }
 665
 666    layout.totalContentWidthPx = contentRight + 30.0f;
 667    layout.scrollOffsetPx = previousScroll;
 668    layout.isValid = true;
 669}
 670
 671static float MapRibbonToNav(float x, const UITopRibbonLayout& layout) {
 672    if (TotalTopUIActionGroups == 0) return 0.0f;
 673
 674    const auto& firstGroup = layout.actionGroups[0];
 675    if (x <= firstGroup.contentStartX) {
 676        return firstGroup.navX;
 677    }
 678    const auto& lastGroup = layout.actionGroups[TotalTopUIActionGroups - 1];
 679    if (x >= lastGroup.contentEndX) {
 680        return lastGroup.navX + lastGroup.navWidth;
 681    }
 682
 683    for (size_t i = 0; i < TotalTopUIActionGroups; ++i) {
 684        const auto& grp = layout.actionGroups[i];
 685        if (x >= grp.contentStartX && x <= grp.contentEndX) {
 686            float width = grp.contentWidth;
 687            if (width <= 0.0f) return grp.navX;
 688            float t = (x - grp.contentStartX) / width;
 689            return grp.navX + t * grp.navWidth;
 690        }
 691        if (i + 1 < TotalTopUIActionGroups) {
 692            const auto& nextGrp = layout.actionGroups[i + 1];
 693            if (x > grp.contentEndX && x < nextGrp.contentStartX) {
 694                float gapWidth = nextGrp.contentStartX - grp.contentEndX;
 695                if (gapWidth <= 0.0f) return nextGrp.navX;
 696                float t = (x - grp.contentEndX) / gapWidth;
 697                float startNav = grp.navX + grp.navWidth;
 698                float endNav = nextGrp.navX;
 699                return startNav + t * (endNav - startNav);
 700            }
 701        }
 702    }
 703
 704    return 0.0f;
 705}
 706
 707// PushRect
 708void PushRect( UIDrawContext& ctx, float x, float y, float w, float h,
 709    uint32_t color, DX12ResourcesUI& uiRes) {
 710    if (ctx.vertexCount + 4 > uiRes.maxVertices) return;
 711    if (ctx.indexCount + 6 > uiRes.maxIndices) return;
 712
 713    uint16_t base = ctx.vertexCount;
 714
 715    ctx.vertexPtr[0] = { x,y,0,0,color, UI_ENGLISH_ATLAS_SLOT };
 716    ctx.vertexPtr[1] = { x + w,y,0,0,color, UI_ENGLISH_ATLAS_SLOT };
 717    ctx.vertexPtr[2] = { x + w,y + h,0,0,color, UI_ENGLISH_ATLAS_SLOT };
 718    ctx.vertexPtr[3] = { x,y + h,0,0,color, UI_ENGLISH_ATLAS_SLOT };
 719
 720    ctx.indexPtr[0] = base + 0;
 721    ctx.indexPtr[1] = base + 1;
 722    ctx.indexPtr[2] = base + 2;
 723    ctx.indexPtr[3] = base + 0;
 724    ctx.indexPtr[4] = base + 2;
 725    ctx.indexPtr[5] = base + 3;
 726}
 727
 728static void PushTexturedQuad(UIDrawContext& ctx, float x, float y, float w, float h,
 729    const UIAtlasRegion& region, uint32_t atlasIndex, uint32_t color, DX12ResourcesUI& uiRes) {
 730    if (ctx.vertexCount + 4 > uiRes.maxVertices) return;
 731    if (ctx.indexCount + 6 > uiRes.maxIndices) return;
 732
 733    uint16_t base = ctx.vertexCount;
 734    ctx.vertexPtr[0] = { x,     y,     region.uvMinX, region.uvMinY, color, atlasIndex };
 735    ctx.vertexPtr[1] = { x + w, y,     region.uvMaxX, region.uvMinY, color, atlasIndex };
 736    ctx.vertexPtr[2] = { x + w, y + h, region.uvMaxX, region.uvMaxY, color, atlasIndex };
 737    ctx.vertexPtr[3] = { x,     y + h, region.uvMinX, region.uvMaxY, color, atlasIndex };
 738
 739    ctx.indexPtr[0] = base + 0;
 740    ctx.indexPtr[1] = base + 1;
 741    ctx.indexPtr[2] = base + 2;
 742    ctx.indexPtr[3] = base + 0;
 743    ctx.indexPtr[4] = base + 2;
 744    ctx.indexPtr[5] = base + 3;
 745
 746    ctx.vertexPtr += 4;
 747    ctx.indexPtr += 6;
 748    ctx.vertexCount += 4;
 749    ctx.indexCount += 6;
 750}
 751
 752void PushRoundedRectangle(UIDrawContext& ctx, float x, float y, float w, float h, float radiusPx,
 753    uint32_t color, DX12ResourcesUI& uiRes) {
 754    if (w <= 0.0f || h <= 0.0f) return;
 755    if (ctx.vertexCount + 36 > uiRes.maxVertices) return;
 756    if (ctx.indexCount + 54 > uiRes.maxIndices) return;
 757
 758    const float clampedRadius = std::max(1.0f, std::min(radiusPx, 0.5f * std::min(w, h)));
 759    const float xCuts[4] = { x, x + clampedRadius, x + w - clampedRadius, x + w };
 760    const float yCuts[4] = { y, y + clampedRadius, y + h - clampedRadius, y + h };
 761
 762    /*
 763    for (int row = 0; row < 3; ++row) {
 764        for (int col = 0; col < 3; ++col) {
 765            PushTexturedQuad(ctx, xCuts[col], yCuts[row],
 766                xCuts[col + 1] - xCuts[col], yCuts[row + 1] - yCuts[row],
 767                gIconAtlasMetadata.roundedRectangle.regions[row][col],
 768                UI_ICON_ATLAS_SLOT, color, uiRes);
 769        }
 770    }*/
 771	// Unrolled the above loops for better performance (fewer function calls, better instruction-level parallelism)
 772    PushTexturedQuad(ctx, xCuts[0], yCuts[0], xCuts[1] - xCuts[0], yCuts[1] - yCuts[0],
 773        gIconAtlasMetadata.roundedRectangle.regions[0][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 774    PushTexturedQuad(ctx, xCuts[1], yCuts[0], xCuts[2] - xCuts[1], yCuts[1] - yCuts[0],
 775        gIconAtlasMetadata.roundedRectangle.regions[0][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 776    PushTexturedQuad(ctx, xCuts[2], yCuts[0], xCuts[3] - xCuts[2], yCuts[1] - yCuts[0],
 777        gIconAtlasMetadata.roundedRectangle.regions[0][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 778    PushTexturedQuad(ctx, xCuts[0], yCuts[1], xCuts[1] - xCuts[0], yCuts[2] - yCuts[1],
 779        gIconAtlasMetadata.roundedRectangle.regions[1][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 780    PushTexturedQuad(ctx, xCuts[1], yCuts[1], xCuts[2] - xCuts[1], yCuts[2] - yCuts[1],
 781        gIconAtlasMetadata.roundedRectangle.regions[1][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 782    PushTexturedQuad(ctx, xCuts[2], yCuts[1], xCuts[3] - xCuts[2], yCuts[2] - yCuts[1],
 783        gIconAtlasMetadata.roundedRectangle.regions[1][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 784    PushTexturedQuad(ctx, xCuts[0], yCuts[2], xCuts[1] - xCuts[0], yCuts[3] - yCuts[2],
 785        gIconAtlasMetadata.roundedRectangle.regions[2][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 786    PushTexturedQuad(ctx, xCuts[1], yCuts[2], xCuts[2] - xCuts[1], yCuts[3] - yCuts[2],
 787        gIconAtlasMetadata.roundedRectangle.regions[2][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 788    PushTexturedQuad(ctx, xCuts[2], yCuts[2], xCuts[3] - xCuts[2], yCuts[3] - yCuts[2],
 789        gIconAtlasMetadata.roundedRectangle.regions[2][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 790}
 791
 792void PushTopRoundedRectangle(UIDrawContext& ctx, float x, float y, float w, float h, float radiusPx,
 793    uint32_t color, DX12ResourcesUI& uiRes) {
 794    if (w <= 0.0f || h <= 0.0f) return;
 795    if (ctx.vertexCount + 24 > uiRes.maxVertices) return;
 796    if (ctx.indexCount + 36 > uiRes.maxIndices) return;
 797
 798    const float clampedRadius = std::max(1.0f, std::min(radiusPx, 0.5f * std::min(w, h)));
 799    const float xCuts[4] = { x, x + clampedRadius, x + w - clampedRadius, x + w };
 800    const float yCuts[3] = { y, y + clampedRadius, y + h };
 801
 802    // Row 0 (Top part with rounded corners)
 803    PushTexturedQuad(ctx, xCuts[0], yCuts[0], xCuts[1] - xCuts[0], yCuts[1] - yCuts[0],
 804        gIconAtlasMetadata.roundedRectangle.regions[0][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 805    PushTexturedQuad(ctx, xCuts[1], yCuts[0], xCuts[2] - xCuts[1], yCuts[1] - yCuts[0],
 806        gIconAtlasMetadata.roundedRectangle.regions[0][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 807    PushTexturedQuad(ctx, xCuts[2], yCuts[0], xCuts[3] - xCuts[2], yCuts[1] - yCuts[0],
 808        gIconAtlasMetadata.roundedRectangle.regions[0][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 809
 810    // Row 1 (Bottom part with sharp corners utilizing the flat middle row)
 811    PushTexturedQuad(ctx, xCuts[0], yCuts[1], xCuts[1] - xCuts[0], yCuts[2] - yCuts[1],
 812        gIconAtlasMetadata.roundedRectangle.regions[1][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 813    PushTexturedQuad(ctx, xCuts[1], yCuts[1], xCuts[2] - xCuts[1], yCuts[2] - yCuts[1],
 814        gIconAtlasMetadata.roundedRectangle.regions[1][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 815    PushTexturedQuad(ctx, xCuts[2], yCuts[1], xCuts[3] - xCuts[2], yCuts[2] - yCuts[1],
 816        gIconAtlasMetadata.roundedRectangle.regions[1][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 817}
 818
 819static void PushIcon(UIDrawContext& ctx, float x, float y, float w, float h, char32_t iconCodepoint,
 820    uint32_t color, DX12ResourcesUI& uiRes) {
 821    auto iconIt = iconGlyphLookup.find(iconCodepoint);
 822    if (iconIt == iconGlyphLookup.end()) return;
 823    const Glyph& glyph = iconIt->second;
 824    UIAtlasRegion icon{ glyph.uvMinX, glyph.uvMinY, glyph.uvMaxX, glyph.uvMaxY };
 825    PushTexturedQuad(ctx, x, y, w, h, icon, UI_ICON_ATLAS_SLOT, color, uiRes);
 826}
 827
 828// Returns true if clicked this frame
 829bool PushInteractiveRect(UIDrawContext& ctx, float x, float y, float w, float h, uint32_t baseColor,
 830    uint32_t id, const UIInput& input, DX12ResourcesUI& uiRes, bool enabled = true) {
 831    uint32_t color = baseColor;
 832
 833    bool hovered = enabled && (input.mouseX >= x && input.mouseX < x + w &&
 834        input.mouseY >= y && input.mouseY < y + h);
 835
 836    if (hovered) color = 0xFF555555; // hover tint (TODO: theme-aware)
 837    if (hovered && input.leftButtonDown) color = 0xFF333333; // pressed tint
 838    if (!enabled) color = 0xFF1E1E1E; // If disabled, force a darker/grayer base color
 839    
 840    PushRect(ctx, x, y, w, h, color, uiRes);
 841
 842    if (!enabled) return false;// Disabled controls do NOT respond to clicks
 843    if (hovered && input.leftButtonPressedThisFrame) {
 844        return true;
 845    }
 846    return false;
 847}
 848
 849void PushText(UIDrawContext& ctx, float x, float y, const char* text, uint32_t color, DX12ResourcesUI& uiRes)
 850{
 851    float cursorX = x;
 852    uint32_t glyphCount = 0;
 853
 854    for (const char* p = text; *p; ++p)
 855    {
 856        // Bounds Checking (Crucial for text strings)
 857        if (ctx.vertexCount + (glyphCount + 1) * 4 > uiRes.maxVertices) return;
 858        if (ctx.indexCount + (glyphCount + 1) * 6 > uiRes.maxIndices) return;
 859
 860        char c = *p;
 861        if (glyphLookup.find(c) == glyphLookup.end()) continue;
 862
 863        const Glyph& g = glyphLookup[c];
 864        if (g.width <= 0 || g.height <= 0) {
 865            cursorX += g.advanceX;
 866            continue;
 867        }
 868
 869        float xpos = cursorX + g.bearingX;
 870        float ypos = y - g.bearingY;
 871        float w = (float)g.width;
 872        float h = (float)g.height;
 873        uint32_t vertexOffset = glyphCount * 4;
 874        uint32_t indexOffset = glyphCount * 6;
 875        
 876        // Add 4 vertices. Write relative to the current pointer (0, 1, 2, 3)
 877        uint32_t vidx = ctx.vertexCount + vertexOffset;
 878        ctx.vertexPtr[vertexOffset + 0] = { xpos,     ypos,     g.uvMinX, g.uvMinY, color, UI_ENGLISH_ATLAS_SLOT };
 879        ctx.vertexPtr[vertexOffset + 1] = { xpos + w, ypos,     g.uvMaxX, g.uvMinY, color, UI_ENGLISH_ATLAS_SLOT };
 880        ctx.vertexPtr[vertexOffset + 2] = { xpos + w, ypos + h, g.uvMaxX, g.uvMaxY, color, UI_ENGLISH_ATLAS_SLOT };
 881        ctx.vertexPtr[vertexOffset + 3] = { xpos,     ypos + h, g.uvMinX, g.uvMaxY, color, UI_ENGLISH_ATLAS_SLOT };
 882
 883        // Add 6 indices. Write indices relative to the current index pointer
 884        ctx.indexPtr[indexOffset + 0] = vidx + 0;
 885        ctx.indexPtr[indexOffset + 1] = vidx + 1;
 886        ctx.indexPtr[indexOffset + 2] = vidx + 2;
 887        ctx.indexPtr[indexOffset + 3] = vidx + 0;
 888        ctx.indexPtr[indexOffset + 4] = vidx + 2;
 889        ctx.indexPtr[indexOffset + 5] = vidx + 3;
 890
 891        glyphCount++;
 892        cursorX += g.advanceX;
 893    }
 894
 895    ctx.vertexPtr += glyphCount * 4;
 896    ctx.indexPtr += glyphCount * 6;
 897    ctx.vertexCount += glyphCount * 4;
 898    ctx.indexCount += glyphCount * 6;
 899}
 900
 901// This function renders the list of tabs, all top menu buttons (with dropdowns if required),
 902// side favorite / frequent buttons bars, right side property window, bottom status bar.
 903// This is also responsible for all relevant DirectX12 configurations required for rendering User Interface.
 904void RenderUIOverlay(SingleUIWindow& window, ID3D12GraphicsCommandList* cmd, DX12ResourcesUI& uiRes,
 905    UITopRibbonLayout& topRibbonLayout, float monitorDPIX, float monitorDPIY, const UIInput& input) {
 906    
 907    if (!cmd) return; //Defensive check.
 908
 909    cmd->SetPipelineState(uiRes.uiPSO.Get());
 910    cmd->SetGraphicsRootSignature(uiRes.uiRootSignature.Get());
 911
 912    // Bind descriptor heap
 913    ID3D12DescriptorHeap* heaps[] = { uiRes.srvHeap.Get(), uiRes.samplerHeap.Get() };
 914    cmd->SetDescriptorHeaps(_countof(heaps), heaps);
 915
 916    // Bind the descriptor table (which contains t0 + s0)    
 917    // Root Parameter 1 = SRV table// must match rootParams[1]
 918    cmd->SetGraphicsRootDescriptorTable(1, uiRes.srvHeap->GetGPUDescriptorHandleForHeapStart());
 919    // Root Parameter 2 = Sampler table
 920    cmd->SetGraphicsRootDescriptorTable(2, uiRes.samplerHeap->GetGPUDescriptorHandleForHeapStart());
 921    // Bind ortho constant buffer (still root parameter 0)
 922    cmd->SetGraphicsRootConstantBufferView(0, uiRes.uiOrthoConstantBuffer->GetGPUVirtualAddress());
 923
 924    float W = (float)window.dx.WindowWidth;
 925    float H = (float)window.dx.WindowHeight;
 926    float* ortho = (float*)uiRes.pOrthoDataBegin;
 927
 928    ortho[0] = 2 / W; ortho[1] = 0;   ortho[2] = 0; ortho[3] = -1;
 929    ortho[4] = 0;   ortho[5] = -2 / H; ortho[6] = 0; ortho[7] = 1;
 930    ortho[8] = 0;   ortho[9] = 0;   ortho[10] = 1; ortho[11] = 0;
 931    ortho[12] = 0;  ortho[13] = 0;  ortho[14] = 0; ortho[15] = 1;
 932
 933    cmd->SetGraphicsRootConstantBufferView( 0, uiRes.uiOrthoConstantBuffer->GetGPUVirtualAddress());
 934
 935    UIDrawContext ctx;
 936    ctx.vertexPtr = reinterpret_cast<UIVertex*>(uiRes.pVertexDataBegin);
 937    ctx.indexPtr = reinterpret_cast<uint16_t*>(uiRes.pIndexDataBegin);
 938    ctx.vertexCount = 0;
 939    ctx.indexCount = 0;
 940    if (!topRibbonLayout.isValid || topRibbonLayout.dpiX != monitorDPIX || topRibbonLayout.dpiY != monitorDPIY) {
 941        PrecomputeTopRibbonLayout(topRibbonLayout, monitorDPIX, monitorDPIY);
 942    }
 943    if (input.mouseWheelDelta != 0 && input.mouseY >= 0.0f && input.mouseY < topRibbonLayout.topUITotalHeightPx) {
 944        const float wheelSteps = input.mouseWheelDelta / (float)WHEEL_DELTA;
 945        const float scrollStepPx = std::max(topRibbonLayout.buttonWidthPx * 2.0f, 120.0f);
 946        topRibbonLayout.scrollOffsetPx -= wheelSteps * scrollStepPx;
 947    }
 948    ClampTopRibbonScroll(topRibbonLayout, W);
 949
 950    float pixelsPerMMx = monitorDPIX / 25.4f;
 951    float pixelsPerMMy = monitorDPIY / 25.4f;
 952    float iconSizePx = topRibbonLayout.iconSizePx;
 953    float buttonHeightPx = topRibbonLayout.buttonHeightPx;
 954	float iconReservedWidthPx = topRibbonLayout.iconReservedWidthPx;
 955    float textStartOffsetPx = topRibbonLayout.textStartOffsetPx;
 956    float textEndInsetPx = topRibbonLayout.textEndInsetPx;
 957    float tabBarHeightPx = topRibbonLayout.tabBarHeightPx;
 958    float topUITotalHeightPx = topRibbonLayout.topUITotalHeightPx;
 959    float roundedCornerRadiusPx = topRibbonLayout.roundedCornerRadiusPx;
 960
 961    auto canPushRect = [&]() {
 962        return ctx.vertexCount + 4 <= uiRes.maxVertices &&
 963            ctx.indexCount + 6 <= uiRes.maxIndices;
 964    };
 965
 966    auto incrementVertexIndexCounters = [&]() {
 967        ctx.vertexPtr += 4;
 968        ctx.indexPtr += 6;
 969        ctx.vertexCount += 4;
 970        ctx.indexCount += 6;
 971    };
 972
 973    auto pushRect = [&](float x, float y, float w, float h, uint32_t color) {
 974        bool pushed = canPushRect();
 975        PushRect(ctx, x, y, w, h, color, uiRes);
 976        if (pushed) incrementVertexIndexCounters();
 977    };
 978
 979    const float uiTextScale = topRibbonLayout.uiTextScale;
 980
 981    auto pushTextClipped = [&](float x, float y, const char32_t* text, float maxWidth, uint32_t color,
 982        float scale) {
 983        if (!text || maxWidth <= 0.0f) return;
 984
 985        float cursorX = x;
 986        float textRight = x + maxWidth;
 987
 988        for (const char32_t* p = text; *p; ++p) {
 989            if (*p > 0x7F) continue;
 990
 991            auto glyphIt = glyphLookup.find(*p);
 992            if (glyphIt == glyphLookup.end()) continue;
 993
 994            const Glyph& g = glyphIt->second;
 995            if (g.width <= 0 || g.height <= 0) {
 996                cursorX += (float)g.advanceX * scale;
 997                continue;
 998            }
 999
1000            // It is always better to be aligned to pixels for better text clarity.
1001            float xpos = std::floor(cursorX + (float)g.bearingX * scale + 0.5f);
1002            float ypos = std::floor(y - (float)g.bearingY * scale + 0.5f);
1003            float glyphWidth = (float)g.width * scale;
1004            float glyphHeight = (float)g.height * scale;
1005            float glyphRight = xpos + glyphWidth;
1006
1007            if (glyphRight > textRight) break;
1008            if (ctx.vertexCount + 4 > uiRes.maxVertices) return;
1009            if (ctx.indexCount + 6 > uiRes.maxIndices) return;
1010
1011            uint16_t base = ctx.vertexCount;
1012            ctx.vertexPtr[0] = { xpos,                  ypos,                   g.uvMinX, g.uvMinY, color, UI_ENGLISH_ATLAS_SLOT };
1013            ctx.vertexPtr[1] = { xpos + glyphWidth,     ypos,                   g.uvMaxX, g.uvMinY, color, UI_ENGLISH_ATLAS_SLOT };
1014            ctx.vertexPtr[2] = { xpos + glyphWidth,     ypos + glyphHeight,     g.uvMaxX, g.uvMaxY, color, UI_ENGLISH_ATLAS_SLOT };
1015            ctx.vertexPtr[3] = { xpos,                  ypos + glyphHeight,     g.uvMinX, g.uvMaxY, color, UI_ENGLISH_ATLAS_SLOT };
1016
1017            ctx.indexPtr[0] = base + 0;
1018            ctx.indexPtr[1] = base + 1;
1019            ctx.indexPtr[2] = base + 2;
1020            ctx.indexPtr[3] = base + 0;
1021            ctx.indexPtr[4] = base + 2;
1022            ctx.indexPtr[5] = base + 3;
1023
1024            ctx.vertexPtr += 4;
1025            ctx.indexPtr += 6;
1026            ctx.vertexCount += 4;
1027            ctx.indexCount += 6;
1028            cursorX += (float)g.advanceX * scale;
1029        }
1030    };
1031
1032    auto textBaselineY = [&](float y, float h, float scale) {
1033        auto glyphIt = glyphLookup.find(U'M');
1034        if (glyphIt == glyphLookup.end()) return y + h * 0.7f;
1035
1036        const Glyph& g = glyphIt->second;
1037        return y + h * 0.5f + (float)g.bearingY * scale - (float)g.height * scale * 0.5f;
1038    };
1039
1040    // ENGINEERING / PROJECT TABs
1041    // Action ids for engineering thread control (UI -> engineering)
1042    constexpr uint32_t ACTION_ENGINEERING_CLOSE = 0xE0000001u;
1043    constexpr uint32_t ACTION_ENGINEERING_CREATE = 0xE0000002u;
1044
1045    float currentX = 0.0f;
1046
1047    uint16_t tabCount = publishedTabCount.load(std::memory_order_acquire);
1048    uint16_t* tabList = publishedTabIndexes.load(std::memory_order_acquire);
1049    
1050    if (canPushRect()) {
1051        PushRect(ctx, 0.0f, 0.0f, 5000.0f, topUITotalHeightPx, uiActiveColors.actionGroupBackground, uiRes);//
1052        incrementVertexIndexCounters();
1053    }
1054
1055    if (canPushRect()) {
1056        PushRect(ctx, 0.0f, 0.0f, 5000.0f, tabBarHeightPx, uiActiveColors.tabBackground, uiRes);//
1057        incrementVertexIndexCounters();
1058    }
1059    // We will allow tabs to shrink progressively when too many tabs exist.
1060    // Compute sizing constraints
1061    const float defaultTabWidth = 160.0f; // legacy fixed width in pixels
1062    const float plusButtonWidth = buttonHeightPx; // reserve square area for '+'
1063    const float minTabWidth = std::max(4.0f * pixelsPerMMx, 8.0f); // 4mm minimum as requested, but at least 8px
1064
1065    // Determine how many slots we need to fit: tabs + one slot for '+' button
1066    uint16_t slotsNeeded = tabCount + 1;
1067    float availableForTabs = std::max(0.0f, W - plusButtonWidth);
1068
1069    float tentativeWidth = availableForTabs / (float)slotsNeeded;
1070    float tabWidthPx = defaultTabWidth;
1071    uint16_t visibleTabs = tabCount;
1072
1073    if (tentativeWidth >= defaultTabWidth) {
1074        tabWidthPx = defaultTabWidth;
1075    } else if (tentativeWidth >= minTabWidth) {
1076        tabWidthPx = tentativeWidth;
1077    } else {
1078        // If tentative width is below minimum, we must hide some tabs.
1079        visibleTabs = (uint16_t)std::floor(availableForTabs / minTabWidth);
1080        if (visibleTabs > tabCount) visibleTabs = tabCount;
1081        tabWidthPx = minTabWidth;
1082    }
1083
1084    // Render visible tabs only; hidden tabs are not drawn (will be handled by horizontal scroll in future)
1085    // Gap between tabs: 0.5 mm on either side
1086    float gapPx = 0.5f * pixelsPerMMx;
1087    for (uint16_t i = 0; i < visibleTabs; i++) {
1088        uint16_t tabID = tabList[i];
1089        bool isActive = (window.activeTabIndex == tabID);
1090
1091        // area for this tab (slot)
1092        float tabX = currentX;
1093        float tabW = tabWidthPx;
1094
1095        // content area inset by half-mm gaps on either side
1096        float contentX = tabX + gapPx;
1097        float contentW = std::max(0.0f, tabW - 2.0f * gapPx);
1098
1099        // X (close) button sizing — square inside tab on the right
1100        float xBtnSize = std::round(std::min(tabW * 0.5f, std::max( (float)std::round(UI_ICON_SIZE_MM * pixelsPerMMx), 10.0f)));
1101        if (xBtnSize + 4.0f > tabW) xBtnSize = std::max(4.0f, tabW - 4.0f);
1102        float xBtnX = tabX + tabW - xBtnSize - 4.0f;
1103        float xBtnY = std::floor((tabBarHeightPx - xBtnSize) * 0.5f);
1104
1105        // Entire tab background — draw only inside content area leaving gaps between tabs
1106        if (isActive) {
1107            PushTopRoundedRectangle(ctx, contentX, 0.0f, contentW, tabBarHeightPx, roundedCornerRadiusPx, uiActiveColors.actionGroupBackground, uiRes);
1108        } else {
1109            bool pushed = canPushRect();
1110            PushRect(ctx, contentX, 0.0f, contentW, tabBarHeightPx, uiActiveColors.tabBackground, uiRes);
1111            if (pushed) incrementVertexIndexCounters();
1112        }
1113
1114        // Check clicks on the X button first to avoid activating the tab when user intends to close
1115        bool xHovered = input.mouseX >= xBtnX && input.mouseX < xBtnX + xBtnSize &&
1116            input.mouseY >= xBtnY && input.mouseY < xBtnY + xBtnSize;
1117        if (xHovered && input.leftButtonPressedThisFrame) {
1118            // Signal close intent to engineering thread. Pass tabID in parameter p1.
1119            PushUIAction(ACTION_ENGINEERING_CLOSE, (uint32_t)tabID, 0);
1120        }
1121
1122        // If user clicked on non-X area of tab, activate it
1123        bool tabHovered = input.mouseX >= tabX && input.mouseX < tabX + tabW &&
1124            input.mouseY >= 0 && input.mouseY < tabBarHeightPx;
1125        if (!xHovered && tabHovered && input.leftButtonPressedThisFrame) {
1126            window.activeTabIndex = tabID; // Render thread will draw this tab's geometry on the next frame.
1127        }
1128
1129        // Draw the X button: only draw rounded background when hovered, otherwise render as plain text
1130        char32_t xChar[2] = { U'x', U'\0' };
1131        if (xHovered) {
1132            PushRoundedRectangle(ctx, xBtnX, xBtnY, xBtnSize, xBtnSize, std::max(1.0f, roundedCornerRadiusPx * 0.6f),
1133                0xFF444444, uiRes);
1134            pushTextClipped(xBtnX + 2.0f, textBaselineY(xBtnY, xBtnSize, uiTextScale), xChar, xBtnSize - 4.0f, 0xFFFFFFFF, uiTextScale);
1135        } else {
1136            // Render as plain small text matching tab text color
1137            pushTextClipped(xBtnX + 2.0f, textBaselineY(xBtnY, xBtnSize, uiTextScale), xChar, xBtnSize - 4.0f, uiActiveColors.tabBackgroundText, uiTextScale);
1138        }
1139
1140        // Draw label clipped to remaining area (avoid overlapping with X)
1141        std::u32string tabLabel;
1142        tabLabel.reserve(allTabs[tabID].fileName.size());
1143        for (wchar_t ch : allTabs[tabID].fileName) {
1144            if (ch <= 0x7F) tabLabel.push_back(static_cast<char32_t>(ch));
1145        }
1146
1147        float labelMaxWidth = contentW - (8.0f + xBtnSize + 4.0f);
1148        pushTextClipped(contentX + 8.0f, textBaselineY(0.0f, tabBarHeightPx, uiTextScale), tabLabel.c_str(), labelMaxWidth, uiActiveColors.tabBackgroundText, uiTextScale);
1149
1150        currentX += tabW;
1151
1152        // Draw 1px vertical separator centered in the gap between tabs (only between tabs)
1153        if (i + 1 < visibleTabs) {
1154            float sepX = tabX + tabW; // center of gap between this tab and next
1155            // align to pixel for crispness
1156            float sepXi = std::floor(sepX + 0.5f);
1157            pushRect(sepXi, 2.0f, 1.0f, tabBarHeightPx - 4.0f, uiActiveColors.actionGroupSeperator);
1158        }
1159    }
1160
1161    // If some tabs are hidden, we may draw a subtle indicator (ellipsis) — skip for now
1162
1163    // Render '+' create new thread button at the end of tab bar
1164    float plusX = currentX + 6.0f; // small padding before plus
1165    float plusSize = std::max(plusButtonWidth, std::round(UI_ICON_SIZE_MM * pixelsPerMMy) + 8.0f);
1166    bool plusHovered = input.mouseX >= plusX && input.mouseX < plusX + plusSize &&
1167        input.mouseY >= (tabBarHeightPx - plusSize) * 0.5f && input.mouseY < (tabBarHeightPx - plusSize) * 0.5f + plusSize;
1168    // '+' button: show rounded background only on hover; otherwise render as plain icon/text
1169    if (plusHovered) {
1170        PushRoundedRectangle(ctx, plusX, (tabBarHeightPx - plusSize) * 0.5f, plusSize, plusSize, roundedCornerRadiusPx,
1171            0xFF444444, uiRes);
1172        if (!gIconAtlasMetadata.mixedIconCodepoints.empty()) {
1173            PushIcon(ctx, plusX + (plusSize - iconSizePx) * 0.5f, (tabBarHeightPx - iconSizePx) * 0.5f,
1174                iconSizePx, iconSizePx, gIconAtlasMetadata.mixedIconCodepoints[0], 0xFFFFFFFF, uiRes);
1175        }
1176    } else {
1177        if (!gIconAtlasMetadata.mixedIconCodepoints.empty()) {
1178            PushIcon(ctx, plusX + (plusSize - iconSizePx) * 0.5f, (tabBarHeightPx - iconSizePx) * 0.5f,
1179                iconSizePx, iconSizePx, gIconAtlasMetadata.mixedIconCodepoints[0], uiActiveColors.tabBackgroundText, uiRes);
1180        }
1181    }
1182    if (plusHovered && input.leftButtonPressedThisFrame) {
1183        PushUIAction(ACTION_ENGINEERING_CREATE, 0, 0);
1184    }
1185
1186    // TOP BUTTONS (ACTION GROUP BAR)
1187    const float buttonGap = topRibbonLayout.buttonGapPx;
1188    const float actionGroupLabelY = topRibbonLayout.actionGroupLabelY;
1189	const float groupLabelHeight = topRibbonLayout.actionGroupLabelHeightPx;
1190    const float topActionGroupY = topRibbonLayout.topActionGroupY;
1191    const float actionSubGroupLabelY = topRibbonLayout.actionSubGroupLabelY;
1192    const float ribbonScrollX = topRibbonLayout.scrollOffsetPx;
1193
1194    // Draw the 5-pixel high extent-of-ribbon-visible visualization bar in the 5px gap below Action Group labels.
1195    // The gap starts at topActionGroupY - 5.0f.
1196    float extentX = MapRibbonToNav(topRibbonLayout.scrollOffsetPx, topRibbonLayout);
1197    float extentRight = MapRibbonToNav(topRibbonLayout.scrollOffsetPx + W, topRibbonLayout);
1198    float extentW = std::max(1.0f, extentRight - extentX);
1199    // Draw active indicator (orange)
1200    pushRect(extentX, topActionGroupY - 5.0f, extentW, 5.0f, 0xFF3399FF);
1201
1202    for (size_t groupIndex = 0; groupIndex < TotalTopUIActionGroups; ++groupIndex) {
1203        const UIActionGroupNames& group = topUIActionGroupNames[groupIndex];
1204        const UITopRibbonActionGroupLayout& groupLayout = topRibbonLayout.actionGroups[groupIndex];
1205        const char32_t* label = LocalizedUIString(group.labelStringID);
1206        const bool hovered = group.isEnabled &&
1207            input.mouseX >= groupLayout.navX && input.mouseX < groupLayout.navX + groupLayout.navWidth &&
1208            input.mouseY >= actionGroupLabelY && input.mouseY < actionGroupLabelY + groupLabelHeight;
1209
1210        if (hovered) {
1211            pushRect(groupLayout.navX, actionGroupLabelY, groupLayout.navWidth, groupLabelHeight,
1212                uiActiveColors.tabBackgroundHover);
1213        }
1214        if (hovered && input.leftButtonPressedThisFrame) {
1215            topRibbonLayout.scrollOffsetPx = groupLayout.contentStartX;
1216            ClampTopRibbonScroll(topRibbonLayout, W);
1217        }
1218
1219        pushTextClipped(groupLayout.navX + 4.0f, textBaselineY(actionGroupLabelY, groupLabelHeight, uiTextScale),
1220            label, groupLayout.navWidth - 8.0f, uiActiveColors.actionText, uiTextScale);
1221    }
1222
1223    for (size_t runIndex = 0; runIndex < topRibbonLayout.actionSubGroupRunCount; ++runIndex) {
1224        const UITopRibbonSubGroupRunLayout& run = topRibbonLayout.actionSubGroupRuns[runIndex];
1225        if (run.subGroupIndex >= TotalTopUIActionSubGroups) continue;
1226
1227        const UIActionGroupNames& subGroup = topUIActionSubGroupNames[run.subGroupIndex];
1228        const char32_t* label = LocalizedUIString(subGroup.labelStringID);
1229        const float runX = run.contentStartX - ribbonScrollX;
1230        const float runWidth = std::max(0.0f, run.contentEndX - run.contentStartX);
1231        const float labelWidth = MeasureUIStringWidth(label, uiTextScale);
1232        const float labelX = runX + std::max(4.0f, (runWidth - labelWidth) * 0.5f);
1233
1234        pushTextClipped(labelX, textBaselineY(actionSubGroupLabelY, groupLabelHeight, uiTextScale),
1235            label, std::max(0.0f, runWidth - 8.0f), uiActiveColors.actionText, uiTextScale);
1236
1237        if (runIndex + 1 < topRibbonLayout.actionSubGroupRunCount) {
1238            const float lineX = std::floor(run.contentEndX + buttonGap * 0.5f - ribbonScrollX);
1239            const float lineHeight = 3.0f * topRibbonLayout.buttonHeightPx + 2.0f;
1240            if (lineX >= -1.0f && lineX <= W + 1.0f) {
1241                pushRect(lineX, topActionGroupY, 1.0f, lineHeight, 0xFF555555);
1242            }
1243        }
1244    }
1245
1246    for (size_t i = 0; i < TotalUIControls; ++i) {
1247        const auto& ctrl = AllUIControls[i];
1248        const UITopRibbonControlLayout& ctrlLayout = topRibbonLayout.controls[i];
1249        const float btnX = ctrlLayout.x - ribbonScrollX;
1250        const float btnY = ctrlLayout.y;
1251        const float btnWidth = ctrlLayout.width;
1252        const float btnHeight = ctrlLayout.height;
1253        const char32_t* label = LocalizedControlLabel(ctrl);
1254        uint32_t baseColor = StableRandomUIColour((uint32_t)ctrl.action ^ ((uint32_t)i * 0x9E3779B9u));// Render
1255        uint32_t iconColor = StableRandomUIColour(((uint32_t)ctrl.action << 1) ^ 0xA511E9B3u ^ (uint32_t)i);
1256        const bool controlVisible = btnX + btnWidth >= 0.0f && btnX <= W;
1257        bool hovered = false;
1258
1259        if (ctrl.type == 1 || ctrl.type == 2) {                     // Button or Dropdown trigger
1260            hovered = controlVisible && ctrl.isEnabled && (input.mouseX >= btnX && input.mouseX < btnX + btnWidth &&
1261                input.mouseY >= btnY && input.mouseY < btnY + btnHeight);
1262            uint32_t drawColor = hovered && input.leftButtonDown ? 0xFF333333 : baseColor;
1263            if (hovered && !input.leftButtonDown) drawColor = 0xFF555555;
1264            if (controlVisible) {
1265                if (hovered) {
1266                    PushRoundedRectangle(ctx, btnX, btnY, btnWidth, btnHeight, roundedCornerRadiusPx,
1267                        drawColor, uiRes);
1268                } else {
1269                    float highlightWidth = ctrl.showText ? iconReservedWidthPx : btnWidth;
1270                    PushRoundedRectangle(ctx, btnX, btnY, highlightWidth, btnHeight, roundedCornerRadiusPx,
1271                        baseColor, uiRes);
1272                }
1273            }
1274
1275            bool clicked = hovered && input.leftButtonPressedThisFrame;
1276
1277            if (clicked && ctrl.isEnabled) {
1278                PushUIAction((uint32_t)ctrl.action);
1279                if (ctrl.zIndex == 1) { // Dropdown trigger
1280                    window.activeDropdownAction = ctrl.action;
1281                }
1282            }
1283
1284            if (!ctrl.isEnabled) { // Gray-out overlay for disabled controls
1285                if (controlVisible) {
1286                    float highlightWidth = ctrl.showText ? iconReservedWidthPx : btnWidth;
1287                    PushRoundedRectangle(ctx, btnX, btnY, highlightWidth, btnHeight, roundedCornerRadiusPx,
1288                        0xAA333333, uiRes);
1289                }
1290            }
1291        }
1292        else if (ctrl.type == 3) {
1293            // Future textbox
1294            if (controlVisible) {
1295                PushRoundedRectangle(ctx, btnX, btnY, btnWidth, btnHeight, roundedCornerRadiusPx,
1296                    0xFF1E1E1E, uiRes);
1297            }
1298        }
1299        else {
1300            // Plain label
1301            if (controlVisible) {
1302                PushRoundedRectangle(ctx, btnX, btnY, btnWidth, btnHeight, roundedCornerRadiusPx,
1303                    0xFF2D2D30, uiRes);
1304            }
1305        }
1306
1307        if (!controlVisible) continue;
1308
1309        float iconX = btnX + (iconReservedWidthPx - iconSizePx) * 0.5f;
1310        float iconY = btnY + (btnHeight - iconSizePx) * 0.5f;
1311        if (!gIconAtlasMetadata.mixedIconCodepoints.empty()) {
1312            const uint32_t randomIconIndex =
1313                ((uint32_t)ctrl.action ^ (uint32_t)i) % (uint32_t)gIconAtlasMetadata.mixedIconCodepoints.size();
1314            PushIcon(ctx, iconX, iconY, iconSizePx, iconSizePx,
1315                gIconAtlasMetadata.mixedIconCodepoints[randomIconIndex], iconColor, uiRes);
1316        }
1317
1318        if (ctrl.showText) {
1319            float textX = btnX + textStartOffsetPx;
1320            float textWidth = btnWidth - textStartOffsetPx - textEndInsetPx;
1321            uint32_t textColor = 0xFFFFFFFF; // default hovered/active color (white)
1322            if (!hovered) {
1323                textColor = ctrl.isEnabled ? uiActiveColors.actionText : 0xAA888888;
1324            }
1325            pushTextClipped(textX, textBaselineY(btnY, btnHeight, uiTextScale),
1326                label, textWidth, textColor, uiTextScale);
1327        }
1328    }
1329
1330    // ACTIVE DROPDOWN (placeholder)
1331    if (window.activeDropdownAction != Commands::INVALID) {
1332        float dropX = 400.0f;   // TODO: track real button X for proper positioning
1333        float dropY = topActionGroupY + 80.0f;
1334        pushRect(dropX, dropY, 160, 220, 0xFF1E1E1E);
1335        window.activeDropdownAction = Commands::INVALID;   // immediate-mode auto-close
1336    }
1337
1338    // DRAW ALL UI GEOMETRY
1339    if (ctx.indexCount == 0) return;
1340
1341    D3D12_VERTEX_BUFFER_VIEW vbv{};
1342    vbv.BufferLocation = uiRes.uiVertexBuffer->GetGPUVirtualAddress();
1343    vbv.SizeInBytes = ctx.vertexCount * sizeof(UIVertex);
1344    vbv.StrideInBytes = sizeof(UIVertex);
1345
1346    D3D12_INDEX_BUFFER_VIEW ibv{};
1347    ibv.BufferLocation = uiRes.uiIndexBuffer->GetGPUVirtualAddress();
1348    ibv.SizeInBytes = ctx.indexCount * sizeof(uint16_t);
1349    ibv.Format = DXGI_FORMAT_R16_UINT;
1350
1351    cmd->IASetVertexBuffers(0, 1, &vbv);
1352    cmd->IASetIndexBuffer(&ibv);
1353    cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1354    cmd->DrawIndexedInstanced(ctx.indexCount, 1, 0, 0, 0);
1355}

Miscellaneous philosophy:

Renderer must support these scripts:

Script Languages
Latin English, German, French, Spanish, Portuguese, Polish, Dutch, Swedish, Italian
Cyrillic Russian, Ukrainian
CJK Chinese, Japanese
Hangul Korean
Arabic Urdu
Indic Hindi, Bengali, Telugu, Tamil, etc
Thai Thai
Vietnamese Latin + diacritics

Recommended Font Families: NotoSans-Regular NotoSansCJK-Regular NotoSansDevanagari NotoSansTamil NotoSansTelugu NotoSansThai NotoSansArabic NotoSansHebrew