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| Devanagari | Hindi, Marathi, Nepali etc |
81| Bengali | Bengali, Assamese |
82| Gurmukhi | Punjabi |
83| Gujarati | Gujarati |
84| Odia | Odia |
85| Tamil | Tamil |
86| Telugu | Telugu |
87| Kannada | Kannada |
88| Malayalam | Malayalam |
89| Arabic script | Urdu, Arabic, Persian, Kashmiri |
90| Chinese Han | Chinese + Japanese Kanji |
91| Japanese kana | Hiragana/Katakana |
92| Hangul | Korean |
93| Thai | Thai |
94
95Professional CAD software language coverage (As per ChatGPT).
96| Software | Languages |
97| ---------- | --------- |
98| AutoCAD | ~15 |
99| SolidWorks | ~13 |
100| Fusion360 | ~10 |
101| CATIA | ~8 |
102All softwares listed below are copy right of respective software companies.
103
104HENCE OUR LANGUAGE LIST IS FROZEN ! ;)
105
106Estimated size overhead of bundling all the fonts:
107
108| Font | Typical Size |
109| ---------------------------- | ------------ |
110| Noto Sans (Latin + extended) | ~2 MB |
111| Noto Sans Devanagari | ~1.5 MB |
112| Noto Sans Bengali | ~1.3 MB |
113| Noto Sans Gurmukhi | ~0.9 MB |
114| Noto Sans Gujarati | ~1.0 MB |
115| Noto Sans Oriya (Odia) | ~1.1 MB |
116| Noto Sans Tamil | ~0.9 MB |
117| Noto Sans Telugu | ~1.2 MB |
118| Noto Sans Kannada | ~1.2 MB |
119| Noto Sans Malayalam | ~1.4 MB |
120| Noto Sans Arabic | ~1.2 MB |
121| Noto Sans Thai | ~0.7 MB |
122
123Subtotal (non-CJK): โ 14โ15 MB
124
125| Font | Approx Size |
126| -------------------------------------- | ----------- |
127| Noto Sans CJK SC (Simplified Chinese) | ~16โ18 MB |
128| Noto Sans CJK TC (Traditional Chinese) | ~16โ18 MB |
129| Noto Sans CJK JP (Japanese) | ~16โ18 MB |
130| Noto Sans CJK KR (Korean) | ~16โ18 MB |
131
132CJK 3 variants (SC + JP + KR): โ 48โ54 MB
133
134Total: โ 65 MB , ~60% Compression expected in Installer. โ 40 MB. Acceptable.
135
136Runtime: Entire font files will not be loaded at runtime.
137They will be loaded on demand to minimize memory footprint.
138
139*/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 <unordered_map>
13#include <iostream>
14#include <atomic>
15
16#include "ConstantsApplication.h"
17#include "UserInterface.h" // It also includes "UserInterface-TextTranslations.h"
18
19// Do not #include "เคตเคฟเคถเฅเคตเคเคฐเฅเคฎเคพ.h" otherwise it will lead to circular dependency error. Declare this struct exist.
20struct SingleUIWindow; // Add this forward declaration:
21
22using Microsoft::WRL::ComPtr;
23
24struct DX12ResourcesUI { // GPU resources
25 ComPtr<ID3D12Resource> uiAtlasTexture; // 1024ร1024 or 2048ร2048 RGBA (or R8 for alpha-only)
26 ComPtr<ID3D12Resource> uiVertexBuffer; // Dynamic upload buffer for vertices
27 ComPtr<ID3D12Resource> uiIndexBuffer; // Dynamic upload buffer for indices
28
29 UINT8* pVertexDataBegin = nullptr; // Mapped pointer for immediate writing
30 UINT8* pIndexDataBegin = nullptr;
31 UINT8* pOrthoDataBegin = nullptr;
32
33 ID3D12Resource* iconAtlas; // Required ?
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 PushText(UIDrawContext& ctx, float x, float y, const char* text, uint32_t color, DX12ResourcesUI& uiRes);
57void RenderUIOverlay( SingleUIWindow& window, ID3D12GraphicsCommandList* cmdList,
58 DX12ResourcesUI& uiRes, float monitorDPI, const UIInput& input);
1// Copyright (c) 2026-Present : Ram Shanker: All rights reserved.
2
3#include "UserInterface-DirectX12.h"
4#include <d3dcompiler.h>
5#include "FontManager.h"
6#include <MemoryManagerGPU-DirectX12.h>
7#include "เคตเคฟเคถเฅเคตเคเคฐเฅเคฎเคพ.h"
8extern เคถเคเคเคฐ gpu;
9extern std::atomic<uint16_t*> publishedTabIndexes;
10extern std::atomic<uint16_t> publishedTabCount;
11std::atomic<uint32_t> actionWriteIndex;
12std::string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
13std::atomic<uint64_t> atlasFence = 0;
14
15// Shader compilation helper
16static void CompileShader( const char* code, const char* entry, const char* target, UINT flags,
17 ComPtr<ID3DBlob>& outBlob) {
18
19 ComPtr<ID3DBlob> errorBlob;
20 HRESULT hr = D3DCompile( code, strlen(code), nullptr, nullptr, nullptr, entry, target, flags, 0,
21 &outBlob, &errorBlob);
22 if (FAILED(hr)) {
23 if (errorBlob) { std::cerr << (char*)errorBlob->GetBufferPointer() << std::endl; }
24 ThrowIfFailed(hr);
25 }
26}
27
28bool SubmitTextureUpload(const TextureUploadDesc& desc,
29 ComPtr<ID3D12Resource>* outTex, std::atomic<uint64_t>* fenceOut) {
30
31 uint32_t index = gUploadQueue.writeIndex.fetch_add(1, std::memory_order_relaxed);
32 UploadRequest& req = gUploadQueue.requests[index % MAX_UPLOAD_REQUESTS];
33
34 req.type = UploadType::Texture2D;
35 req.texture = desc;
36 req.outResource = outTex;
37 req.completionFence = fenceOut;
38
39 return true;
40}
41
42void InitUIResources( DX12ResourcesUI& uiRes, ID3D12Device* device) {
43 if (!InitFontSystem()) { // FONT system initialization (CPU-side)
44 std::cerr << "Font system initilization failed failed\n";
45 return;
46 }
47
48 // Root signature
49 CD3DX12_DESCRIPTOR_RANGE1 ranges[2]; // Descriptor ranges
50
51 // Range 0: SRV (t0) โ from srvHeap // 1: 1 Texture, 0: register t0 = atlas
52 ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_NONE);
53 // Range 1: SAMPLER (s0) โ from samplerHeap // 1: 1 Sampler, 0: register s0 = sampler
54 ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_NONE);
55
56 CD3DX12_ROOT_PARAMETER1 rootParams[3];
57 rootParams[0].InitAsConstantBufferView(0, 0, D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
58 D3D12_SHADER_VISIBILITY_VERTEX);// b0 - Ortho constant buffer (vertex shader)
59
60 // Root Parameter 1: Descriptor Table containing only the SRV
61 rootParams[1].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
62
63 // Root Parameter 2: Descriptor Table containing only the SAMPLER
64 rootParams[2].InitAsDescriptorTable(1, &ranges[1],
65 D3D12_SHADER_VISIBILITY_PIXEL);
66
67 CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootDesc;
68 rootDesc.Init_1_1(_countof(rootParams), rootParams, 0, nullptr,
69 D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
70
71 ComPtr<ID3DBlob> signature;
72 ComPtr<ID3DBlob> errorBlob;
73
74 HRESULT hr = D3DX12SerializeVersionedRootSignature(&rootDesc,
75 D3D_ROOT_SIGNATURE_VERSION_1_1, &signature, &errorBlob);
76 if (FAILED(hr)) {
77 if (errorBlob)
78 std::cerr << "Root Signature Serialization Failed:\n"
79 << (char*)errorBlob->GetBufferPointer() << std::endl;
80 ThrowIfFailed(hr); // will print the real error
81 }
82
83 ThrowIfFailed(device->CreateRootSignature(0, signature->GetBufferPointer(),
84 signature->GetBufferSize(), IID_PPV_ARGS(&uiRes.uiRootSignature)));
85
86 // Create SRV descriptor heap (1 texture)
87 D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
88 heapDesc.NumDescriptors = 1;
89 heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
90 heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
91 ThrowIfFailed(device->CreateDescriptorHeap( &heapDesc, IID_PPV_ARGS(&uiRes.srvHeap) ));
92
93 // Create SAMPLER descriptor heap (shader-visible)
94 D3D12_DESCRIPTOR_HEAP_DESC samplerHeapDesc = {};
95 samplerHeapDesc.NumDescriptors = 1;
96 samplerHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
97 samplerHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
98 ThrowIfFailed(device->CreateDescriptorHeap(&samplerHeapDesc,
99 IID_PPV_ARGS(&uiRes.samplerHeap)));
100
101 // Create the actual sampler (Point sampling is perfect for UI atlas)
102 D3D12_SAMPLER_DESC samplerDesc = {};
103 samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; // sharp UI text
104 samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
105 samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
106 samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
107 samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
108 samplerDesc.MinLOD = 0;
109 samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
110
111 device->CreateSampler(&samplerDesc,
112 uiRes.samplerHeap->GetCPUDescriptorHandleForHeapStart());
113
114 // Shaders
115
116#if defined(_DEBUG)
117 UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
118#else
119 UINT compileFlags = 0;
120#endif
121
122 static const char* vsCode = R"(
123cbuffer OrthoConstantBuffer : register(b0) {
124 float4x4 ortho;
125};
126
127struct VSInput {
128 float2 position : POSITION;
129 float2 uv : TEXCOORD0;
130 uint color : COLOR0;
131};
132
133struct PSInput {
134 float4 position : SV_POSITION;
135 float2 uv : TEXCOORD0;
136 uint color : COLOR0;
137};
138
139PSInput VSMain(VSInput input) {
140 PSInput output;
141 float4 pos = float4(input.position,0,1);
142 output.position = mul(pos,ortho);
143 output.uv = input.uv;
144 output.color = input.color;
145 return output;
146}
147)";
148
149 static const char* psCode = R"(
150struct PSInput {
151 float4 position : SV_POSITION;
152 float2 uv : TEXCOORD0;
153 uint color : COLOR0;
154};
155
156Texture2D atlas : register(t0);
157SamplerState samp : register(s0);
158
159float4 PSMain(PSInput input) : SV_TARGET {
160 float4 tex = atlas.Sample(samp, input.uv);
161 float r = ((input.color >> 0) & 0xFF) / 255.0;
162 float g = ((input.color >> 8) & 0xFF) / 255.0;
163 float b = ((input.color >> 16) & 0xFF) / 255.0;
164 float a = ((input.color >> 24) & 0xFF) / 255.0;
165 return float4(r, g, b, a) * tex;
166}
167)";
168
169 ComPtr<ID3DBlob> vsBlob;
170 ComPtr<ID3DBlob> psBlob;
171 CompileShader(vsCode, "VSMain", "vs_5_0", compileFlags, vsBlob);
172 CompileShader(psCode, "PSMain", "ps_5_0", compileFlags, psBlob);
173 // Input layout
174
175 D3D12_INPUT_ELEMENT_DESC layout[] = {
176 { "POSITION",0,DXGI_FORMAT_R32G32_FLOAT,0,0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
177 { "TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
178 { "COLOR",0,DXGI_FORMAT_R32_UINT,0,16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 }
179 };
180
181 // PSO
182 D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
183 psoDesc.InputLayout = { layout,_countof(layout) };
184 psoDesc.pRootSignature = uiRes.uiRootSignature.Get();
185 psoDesc.VS = CD3DX12_SHADER_BYTECODE(vsBlob.Get());
186 psoDesc.PS = CD3DX12_SHADER_BYTECODE(psBlob.Get());
187 psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
188 psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
189 psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
190 psoDesc.BlendState.RenderTarget[0].BlendEnable = TRUE;
191 psoDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
192 psoDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
193 psoDesc.DepthStencilState.DepthEnable = FALSE;
194 psoDesc.SampleMask = UINT_MAX;
195 psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
196 psoDesc.NumRenderTargets = 1;
197 psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
198 psoDesc.SampleDesc.Count = 1;
199
200 ThrowIfFailed( device->CreateGraphicsPipelineState( &psoDesc, IID_PPV_ARGS(&uiRes.uiPSO)));
201
202 // Vertex buffer
203 auto uploadHeap = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
204
205 auto vbDesc = CD3DX12_RESOURCE_DESC::Buffer( uiRes.maxVertices * sizeof(UIVertex));
206 ThrowIfFailed( device->CreateCommittedResource( &uploadHeap, D3D12_HEAP_FLAG_NONE, &vbDesc,
207 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uiRes.uiVertexBuffer)));
208
209 auto ibDesc = CD3DX12_RESOURCE_DESC::Buffer( uiRes.maxIndices * sizeof(uint16_t));
210 ThrowIfFailed( device->CreateCommittedResource( &uploadHeap, D3D12_HEAP_FLAG_NONE, &ibDesc,
211 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uiRes.uiIndexBuffer)));
212
213 auto cbDesc = CD3DX12_RESOURCE_DESC::Buffer(256);
214 ThrowIfFailed( device->CreateCommittedResource( &uploadHeap, D3D12_HEAP_FLAG_NONE, &cbDesc,
215 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uiRes.uiOrthoConstantBuffer)));
216
217 CD3DX12_RANGE readRange(0, 0);
218 uiRes.uiVertexBuffer->Map( 0, &readRange, reinterpret_cast<void**>(&uiRes.pVertexDataBegin));
219 uiRes.uiIndexBuffer->Map( 0, &readRange, reinterpret_cast<void**>(&uiRes.pIndexDataBegin));
220 uiRes.uiOrthoConstantBuffer->Map( 0, &readRange, reinterpret_cast<void**>(&uiRes.pOrthoDataBegin));
221 std::wcout << L"UI Resources Initialized (Phase 4A)\n";
222
223 AtlasBitmap atlas = BuildFontAtlas();// BUILD ATLAS ON CPU
224
225 TextureUploadDesc desc = {};
226 desc.width = atlas.width;
227 desc.height = atlas.height;
228 desc.format = DXGI_FORMAT_R8_UNORM;
229 desc.pixels = atlas.pixels.data();
230 desc.rowPitch = atlas.width;
231
232 SubmitTextureUpload(desc, &uiRes.uiAtlasTexture, &atlasFence);// Enqueue the upload through upload queue
233 // RESERVED FENCE VALUE FOR THIS UPLOAD (this is the key change)
234 // The copy thread MUST eventually signal exactly this value.
235 uint64_t atlasReadyFence = gpu.copyFenceValue.fetch_add(1, std::memory_order_relaxed);
236 // Tell everyone (including the render thread) what fence value to wait for
237 atlasFence.store(atlasReadyFence, std::memory_order_release);
238 // CPU-blocking wait until Copy Queue has processed this upload
239 if (gpu.copyFence->GetCompletedValue() < atlasReadyFence) {
240 ThrowIfFailed(gpu.copyFence->SetEventOnCompletion(atlasReadyFence, gpu.copyFenceEvent));
241 WaitForSingleObject(gpu.copyFenceEvent, INFINITE); // CPU blocks here
242 }
243
244 // Now the texture is in DEFAULT heap โ safe to create SRV
245 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
246 srvDesc.Format = DXGI_FORMAT_R8_UNORM;
247 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
248 srvDesc.Texture2D.MipLevels = 1;
249 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
250
251 device->CreateShaderResourceView(uiRes.uiAtlasTexture.Get(), &srvDesc,
252 uiRes.srvHeap->GetCPUDescriptorHandleForHeapStart());
253
254 std::wcout << L"UI Atlas uploaded and SRV created (fence = " << atlasReadyFence << L")\n";
255}
256
257// Cleanup
258void CleanupUIResources(DX12ResourcesUI& uiRes) {
259 if (uiRes.uiVertexBuffer) uiRes.uiVertexBuffer->Unmap(0, nullptr);
260 if (uiRes.uiIndexBuffer) uiRes.uiIndexBuffer->Unmap(0, nullptr);
261 if (uiRes.uiOrthoConstantBuffer) uiRes.uiOrthoConstantBuffer->Unmap(0, nullptr);
262
263 uiRes = {};
264}
265
266// PushRect
267void PushRect( UIDrawContext& ctx, float x, float y, float w, float h,
268 uint32_t color, DX12ResourcesUI& uiRes) {
269 if (ctx.vertexCount + 4 > uiRes.maxVertices) return;
270 if (ctx.indexCount + 6 > uiRes.maxIndices) return;
271
272 uint16_t base = ctx.vertexCount;
273
274 ctx.vertexPtr[0] = { x,y,0,0,color };
275 ctx.vertexPtr[1] = { x + w,y,0,0,color };
276 ctx.vertexPtr[2] = { x + w,y + h,0,0,color };
277 ctx.vertexPtr[3] = { x,y + h,0,0,color };
278
279 ctx.indexPtr[0] = base + 0;
280 ctx.indexPtr[1] = base + 1;
281 ctx.indexPtr[2] = base + 2;
282 ctx.indexPtr[3] = base + 0;
283 ctx.indexPtr[4] = base + 2;
284 ctx.indexPtr[5] = base + 3;
285
286 ctx.vertexPtr += 4;
287 ctx.indexPtr += 6;
288 ctx.vertexCount += 4;
289 ctx.indexCount += 6;
290}
291
292// Returns true if clicked this frame
293bool PushInteractiveRect(UIDrawContext& ctx, float x, float y, float w, float h, uint32_t baseColor,
294 uint32_t id, const UIInput& input, DX12ResourcesUI& uiRes, bool enabled = true) {
295 uint32_t color = baseColor;
296
297 bool hovered = enabled && (input.mouseX >= x && input.mouseX < x + w &&
298 input.mouseY >= y && input.mouseY < y + h);
299
300 if (hovered) color = 0xFF555555; // hover tint (TODO: theme-aware)
301 if (hovered && input.leftButtonDown) color = 0xFF333333; // pressed tint
302 if (!enabled) color = 0xFF1E1E1E; // If disabled, force a darker/grayer base color
303
304 PushRect(ctx, x, y, w, h, color, uiRes);
305
306 if (!enabled) return false;// Disabled controls do NOT respond to clicks
307 if (hovered && input.leftButtonPressedThisFrame) {
308 return true;
309 }
310 return false;
311}
312
313void PushText(UIDrawContext& ctx, float x, float y, const char* text, uint32_t color, DX12ResourcesUI& uiRes)
314{
315 float cursorX = x;
316
317 for (const char* p = text; *p; ++p)
318 {
319 char c = *p;
320 if (glyphLookup.find(c) == glyphLookup.end()) continue;
321
322 const Glyph& g = glyphLookup[c];
323
324 float xpos = cursorX + g.bearingX;
325 float ypos = y - g.bearingY;
326 float w = (float)g.width;
327 float h = (float)g.height;
328 uint16_t base = ctx.vertexCount;
329
330 // Add 4 vertices
331 uint32_t vidx = ctx.vertexCount;
332 ctx.vertexPtr[vidx + 0] = { xpos, ypos, g.uvMinX, g.uvMinY, color };
333 ctx.vertexPtr[vidx + 1] = { xpos + w, ypos, g.uvMaxX, g.uvMinY, color };
334 ctx.vertexPtr[vidx + 2] = { xpos + w, ypos + h, g.uvMaxX, g.uvMaxY, color };
335 ctx.vertexPtr[vidx + 3] = { xpos, ypos + h, g.uvMinX, g.uvMaxY, color };
336
337 // Add 6 indices
338 uint32_t iidx = ctx.indexCount;
339 ctx.indexPtr[iidx + 0] = vidx + 0;
340 ctx.indexPtr[iidx + 1] = vidx + 1;
341 ctx.indexPtr[iidx + 2] = vidx + 2;
342 ctx.indexPtr[iidx + 3] = vidx + 0;
343 ctx.indexPtr[iidx + 4] = vidx + 2;
344 ctx.indexPtr[iidx + 5] = vidx + 3;
345
346 ctx.vertexPtr += 4;
347 ctx.indexPtr += 6;
348 ctx.vertexCount += 4;
349 ctx.indexCount += 6;
350
351 cursorX += g.advanceX;
352 }
353}
354
355// Convenience for tabs (fixed width for now)
356bool PushTab(UIDrawContext& ctx, float x, float w, float h, uint16_t tabID, bool isActive,
357 const UIInput& input, DX12ResourcesUI& uiRes) {
358
359 uint32_t color = isActive ? COLOR_UI_TAB_ACTIVE : COLOR_UI_TAB_INACTIVE;
360 uint32_t actionID = 0x10000000u | tabID; // high bit = tab family
361
362 if (PushInteractiveRect(ctx, x, 0, w, h, color, actionID, input, uiRes)) {
363 // Optional immediate feedback (UI thread)
364 // window.activeTabIndex = tabID; // you can still do it here if you want instant visual
365 return true;
366 }
367 return false;
368}
369
370// This function renders the list of tabs, all top menu buttons (with dropdowns if required),
371// side favourite / frequent buttons bars, right side property window, bottom status bar.
372// This is also responsible for all relevant DirectX12 configurations required for rendering User Interface.
373void RenderUIOverlay(SingleUIWindow& window, ID3D12GraphicsCommandList* cmd, DX12ResourcesUI& uiRes,
374 float monitorDPI, const UIInput& input) {
375
376 if (!cmd) return; //Defensive check.
377
378 cmd->SetPipelineState(uiRes.uiPSO.Get());
379 cmd->SetGraphicsRootSignature(uiRes.uiRootSignature.Get());
380
381 // Bind descriptor heap
382 ID3D12DescriptorHeap* heaps[] = { uiRes.srvHeap.Get(), uiRes.samplerHeap.Get() };
383 cmd->SetDescriptorHeaps(_countof(heaps), heaps);
384
385 // Bind the descriptor table (which contains t0 + s0)
386 // Root Parameter 1 = SRV table// must match rootParams[1]
387 cmd->SetGraphicsRootDescriptorTable(1, uiRes.srvHeap->GetGPUDescriptorHandleForHeapStart());
388 // Root Parameter 2 = Sampler table
389 cmd->SetGraphicsRootDescriptorTable(2, uiRes.samplerHeap->GetGPUDescriptorHandleForHeapStart());
390 // Bind ortho constant buffer (still root parameter 0)
391 cmd->SetGraphicsRootConstantBufferView(0, uiRes.uiOrthoConstantBuffer->GetGPUVirtualAddress());
392
393 float W = (float)window.dx.WindowWidth;
394 float H = (float)window.dx.WindowHeight;
395 float* ortho = (float*)uiRes.pOrthoDataBegin;
396
397 ortho[0] = 2 / W; ortho[1] = 0; ortho[2] = 0; ortho[3] = -1;
398 ortho[4] = 0; ortho[5] = -2 / H; ortho[6] = 0; ortho[7] = 1;
399 ortho[8] = 0; ortho[9] = 0; ortho[10] = 1; ortho[11] = 0;
400 ortho[12] = 0; ortho[13] = 0; ortho[14] = 0; ortho[15] = 1;
401
402 cmd->SetGraphicsRootConstantBufferView( 0, uiRes.uiOrthoConstantBuffer->GetGPUVirtualAddress());
403
404 UIDrawContext ctx;
405 ctx.vertexPtr = reinterpret_cast<UIVertex*>(uiRes.pVertexDataBegin);
406 ctx.indexPtr = reinterpret_cast<uint16_t*>(uiRes.pIndexDataBegin);
407 ctx.vertexCount = 0;
408 ctx.indexCount = 0;
409 float pixelsPerMM = monitorDPI / 25.4f;
410 float buttonWidthPx = UI_BUTTON_WIDTH_MM * pixelsPerMM;
411 float tabBarHeight = UI_TAB_BAR_HEIGHT_MM * pixelsPerMM;
412 float ribbonY = tabBarHeight + UI_DIVIDER_WIDTH_PX; // โ only one declaration
413
414 // ENGINEERING / PROJECT TABs
415 float tabWidth = 120.0f;
416 float currentX = 0.0f;
417
418 uint16_t tabCount = publishedTabCount.load(std::memory_order_acquire);
419 uint16_t* tabList = publishedTabIndexes.load(std::memory_order_acquire);
420
421 for (uint16_t i = 0; i < tabCount; i++) {
422 uint16_t tabID = tabList[i];
423 bool active = (window.activeTabIndex == tabID);
424 if (PushTab(ctx, currentX, tabWidth, tabBarHeight, tabID, active, input, uiRes)) {
425 window.activeTabIndex = tabID; // instant visual feedback
426 }
427 currentX += tabWidth;
428 }
429
430 // TOP BUTTONS (ACTION GROUP BAR)
431 currentX = 20.0f; // reset X for action groups
432
433 const float buttonBaseHeight = 32.0f;
434 const float buttonGap = 4.0f;
435 const float subGroupGap = 18.0f;
436 const float groupGap = 28.0f;
437
438 uint32_t currentActionGroupIndex = 0xFFFFFFFF;
439 uint32_t currentSubGroupID = 0xFFFFFFFF;
440
441 float groupLabelY = ribbonY;
442 float subGroupLabelY = ribbonY + 18.0f;
443
444 for (size_t i = 0; i < TotalUIControls; ++i) {
445 const auto& ctrl = AllUIControls[i];
446
447 if (ctrl.actionGrpupIndex != currentActionGroupIndex) { // Action Group change
448 if (currentActionGroupIndex != 0xFFFFFFFF) currentX += groupGap;
449 currentActionGroupIndex = ctrl.actionGrpupIndex;
450 currentSubGroupID = 0xFFFFFFFF;
451
452 PushRect(ctx, currentX, groupLabelY, 6, 48, 0xFF555555, uiRes); // group separator
453 currentX += 12.0f;
454 }
455
456 if (ctrl.actionSubGroupID != currentSubGroupID) { // Sub-Group change
457 if (currentSubGroupID != 0xFFFFFFFF) {currentX += subGroupGap;}
458 currentSubGroupID = ctrl.actionSubGroupID;
459
460 PushRect(ctx, currentX, subGroupLabelY, 110, 16, 0xFF2D2D30, uiRes); // sub-group label bar
461 currentX += 8.0f;
462 }
463
464 // Button geometry (vertical stacking support)
465 float btnWidth = (ctrl.defaultWidthPX > 0)
466 ? ctrl.defaultWidthPX * pixelsPerMM / 25.4f // interpret as mm
467 : buttonWidthPx;
468 float btnHeight = buttonBaseHeight * ctrl.noOfVerticalSlots;
469 float btnY = ribbonY + 38.0f;
470
471 if (ctrl.noOfVerticalSlots > 1) {btnY += ctrl.verticalSlotNo * buttonBaseHeight;}
472 uint32_t baseColor = ctrl.isEnabled ? 0xFF2D2D30 : 0xFF1E1E1E;// Render
473
474 if (ctrl.type == 1 || ctrl.type == 2) { // Button or Dropdown trigger
475 bool clicked = PushInteractiveRect(ctx, currentX, btnY, btnWidth, btnHeight,
476 baseColor, (uint32_t)ctrl.action, input, uiRes, ctrl.isEnabled);
477
478 if (clicked && ctrl.isEnabled) {
479 PushUIAction((uint32_t)ctrl.action);
480 if (ctrl.zIndex == 1) { // Dropdown trigger
481 window.activeDropdownAction = ctrl.action;
482 }
483 }
484
485 if (!ctrl.isEnabled) { // Gray-out overlay for disabled controls
486 PushRect(ctx, currentX, btnY, btnWidth, btnHeight, 0xAA333333, uiRes);
487 }
488 }
489 else if (ctrl.type == 3) {
490 // Future textbox
491 PushRect(ctx, currentX, btnY, btnWidth, btnHeight, 0xFF1E1E1E, uiRes);
492 }
493 else {
494 // Plain label
495 PushRect(ctx, currentX, btnY, btnWidth, btnHeight, 0xFF2D2D30, uiRes);
496 }
497
498 // Draw "x" below button
499 PushText(ctx, currentX, btnY + btnHeight + 14.0f, "x", 0xFFFFFFFF, uiRes);// below button
500 currentX += btnWidth + buttonGap;
501 }
502
503 currentX += 30.0f;// Final padding
504
505 // ACTIVE DROPDOWN (placeholder)
506 if (window.activeDropdownAction != UIAction::INVALID) {
507 float dropX = 400.0f; // TODO: track real button X for proper positioning
508 float dropY = ribbonY + 80.0f;
509 PushRect(ctx, dropX, dropY, 160, 220, 0xFF1E1E1E, uiRes);
510 window.activeDropdownAction = UIAction::INVALID; // immediate-mode auto-close
511 }
512
513 // DRAW ALL UI GEOMETRY
514 if (ctx.indexCount == 0) return;
515
516 D3D12_VERTEX_BUFFER_VIEW vbv{};
517 vbv.BufferLocation = uiRes.uiVertexBuffer->GetGPUVirtualAddress();
518 vbv.SizeInBytes = ctx.vertexCount * sizeof(UIVertex);
519 vbv.StrideInBytes = sizeof(UIVertex);
520
521 D3D12_INDEX_BUFFER_VIEW ibv{};
522 ibv.BufferLocation = uiRes.uiIndexBuffer->GetGPUVirtualAddress();
523 ibv.SizeInBytes = ctx.indexCount * sizeof(uint16_t);
524 ibv.Format = DXGI_FORMAT_R16_UINT;
525
526 cmd->IASetVertexBuffers(0, 1, &vbv);
527 cmd->IASetIndexBuffer(&ibv);
528 cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
529 cmd->DrawIndexedInstanced(ctx.indexCount, 1, 0, 0, 0);
530}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