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:
22struct InternalSubTab;
23
24using Microsoft::WRL::ComPtr;
25
26struct DX12ResourcesUI { // GPU resources shared by all windows (read-only during recording).
27 // The dynamic vertex/index/ortho buffers live per window (DX12ResourcesPerWindow): all
28 // windows of a monitor are recorded into ONE command list and executed together, so a
29 // shared upload buffer would be overwritten by the last-recorded window.
30 std::array<ComPtr<ID3D12Resource>, UI_MAX_ATLAS_TEXTURES> uiAtlasTextures; // 1024×1024 or 2048×2048 RGBA (or R8 for alpha-only)
31
32 ComPtr<ID3D12PipelineState> uiPSO;
33 ComPtr<ID3D12RootSignature> uiRootSignature;
34 ComPtr<ID3D12DescriptorHeap> srvHeap;
35 ComPtr<ID3D12DescriptorHeap> samplerHeap;
36
37 uint32_t maxVertices = 65536;
38 uint32_t maxIndices = 65536 * 3;
39};
40
41// DirectX12 Immediate Mode UI System (Phase 4A). Tab Bar Rendering Only
42// External interfaces of User Interface sub module of the code. The platform-agnostic pieces
43// (UIDrawContext, Push* tessellation, PrecomputeTopRibbonLayout, BuildUIOverlay, atlas bitmap
44// builders) are declared in UserInterface.h and implemented in UserInterface.cpp.
45void InitUIResources(DX12ResourcesUI& uiRes, ID3D12Device* device);
46void CleanupUIResources(DX12ResourcesUI& uiRes);
47
48// Slots 0 and 1 are currently reserved for the mandatory English and Icon atlases.
49// Future script atlases can use slots [UI_FIRST_DYNAMIC_SCRIPT_ATLAS_SLOT, UI_MAX_ATLAS_TEXTURES).
50bool UploadUIAtlasTexture(DX12ResourcesUI& uiRes, ID3D12Device* device, uint32_t atlasSlot,
51 const AtlasBitmap& atlas);
52
53// (Re)builds monitor `monitorId`'s icon atlas at its DPI-floored icon cell size and (re)creates that
54// monitor's SRV heap (English@0 shared, icon@1 this monitor). Call from RestartRenderThreads while the
55// render threads are joined; the old atlas must already be drained + released (see RestartRenderThreads).
56void BuildMonitorIconAtlas(DX12ResourcesUI& uiRes, ID3D12Device* device, int monitorId);
57
58void RenderUIOverlay(SingleUIWindow& window, ID3D12GraphicsCommandList* cmdList,
59 DX12ResourcesUI& uiRes, UITopRibbonLayout& topRibbonLayout,
60 float monitorDPIX, float monitorDPIY, const UIInput& input,
61 uint64_t activeInternalSubTabMemoryId, int monitorId);
1// Copyright (c) 2026-Present : Ram Shanker: All rights reserved.
2
3// DirectX12 half of the UI overlay module: atlas texture upload, PSO / root signature / sampler
4// heap, per-window dynamic buffers and the actual draw call. The platform-agnostic half (atlas
5// bitmap building, ribbon layout, tessellation, widget logic) lives in UserInterface.cpp.
6
7#include "UserInterface-DirectX12.h"
8#include <algorithm>
9#include <cstdio>
10#include <d3dcompiler.h>
11#include "ShaderUIVertex.h"
12#include "ShaderUIPixel.h"
13#include <MemoryManagerGPU-DirectX12.h>
14#include "विश्वकर्मा.h"
15#include "TextureSaver.h"
16extern शंकर gpu;
17extern void PrintHResult(int);
18std::atomic<uint64_t> atlasFence = 0;
19
20bool SubmitTextureUpload(const TextureUploadDesc& desc,
21 ComPtr<ID3D12Resource>* outTex, std::atomic<uint64_t>* fenceOut) {
22
23 uint32_t index = gUploadQueue.writeIndex.fetch_add(1, std::memory_order_relaxed);
24 UploadRequest& req = gUploadQueue.requests[index % MAX_UPLOAD_REQUESTS];
25
26 req.type = UploadType::Texture2D;
27 req.texture = desc;
28 req.outResource = outTex;
29 req.completionFence = fenceOut;
30
31 return true;
32}
33
34// Both UI/text root signatures declare their SRV range as all UI_MAX_ATLAS_TEXTURES descriptors with
35// DESCRIPTOR_RANGE_FLAG_NONE, i.e. DESCRIPTORS_STATIC: every slot must hold a valid view before the
36// table is bound, even the ones no shader samples. Point the not-yet-used slots at the English atlas;
37// a real atlas uploaded into one of them later simply overwrites the alias.
38static void FillSpareAtlasSlots(ID3D12DescriptorHeap* heap, ID3D12Resource* englishAtlasTexture,
39 ID3D12Device* device, uint32_t firstSpareSlot) {
40 if (!heap || !englishAtlasTexture || !device) return;
41
42 const UINT inc = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
43 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
44 srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
45 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
46 srvDesc.Texture2D.MipLevels = 1;
47 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
48
49 for (uint32_t slot = firstSpareSlot; slot < UI_MAX_ATLAS_TEXTURES; ++slot) {
50 CD3DX12_CPU_DESCRIPTOR_HANDLE handle(heap->GetCPUDescriptorHandleForHeapStart(), slot, inc);
51 device->CreateShaderResourceView(englishAtlasTexture, &srvDesc, handle);
52 }
53}
54
55bool UploadUIAtlasTexture(DX12ResourcesUI& uiRes, ID3D12Device* device, uint32_t atlasSlot,
56 const AtlasBitmap& atlas) {
57 if (!device || atlasSlot >= UI_MAX_ATLAS_TEXTURES || atlas.width <= 0 || atlas.height <= 0 ||
58 atlas.pixels.empty() || (atlas.bytesPerPixel != 1 && atlas.bytesPerPixel != 4)) {
59 return false;
60 }
61
62 TextureUploadDesc desc = {};
63 desc.width = atlas.width;
64 desc.height = atlas.height;
65 desc.format = atlas.bytesPerPixel == 4 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8_UNORM;
66 desc.pixels = atlas.pixels.data();
67 desc.rowPitch = atlas.width * atlas.bytesPerPixel;
68
69 std::atomic<uint64_t> uploadFence = 0;
70 SubmitTextureUpload(desc, &uiRes.uiAtlasTextures[atlasSlot], &uploadFence);
71
72 uint64_t atlasReadyFence = gpu.copyFenceValue.fetch_add(1, std::memory_order_relaxed);
73 uploadFence.store(atlasReadyFence, std::memory_order_release);
74 toCopyThreadCV.notify_one();
75
76 if (gpu.copyFence->GetCompletedValue() < atlasReadyFence) {
77 ThrowIfFailed(gpu.copyFence->SetEventOnCompletion(atlasReadyFence, gpu.copyFenceEvent));
78 WaitForSingleObject(gpu.copyFenceEvent, INFINITE);
79 }
80
81 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
82 srvDesc.Format = desc.format;
83 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
84 srvDesc.Texture2D.MipLevels = 1;
85 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
86
87 CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle(uiRes.srvHeap->GetCPUDescriptorHandleForHeapStart(),
88 atlasSlot, device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV));
89 device->CreateShaderResourceView(uiRes.uiAtlasTextures[atlasSlot].Get(), &srvDesc, srvHandle);
90 return true;
91}
92
93void BuildMonitorIconAtlas(DX12ResourcesUI& uiRes, ID3D12Device* device, int monitorId) {
94 if (!device || monitorId < 0 || monitorId >= MV_MAX_MONITORS) return;
95 OneMonitorController& screen = gpu.screens[monitorId];
96
97 // Icon cell size = this monitor's on-screen icon px. PrecomputeTopRibbonLayout (run by
98 // RestartRenderThreads just before us) already floored the DPI, so iconSizePx is >= 20 on coarse
99 // monitors. Fall back defensively if the layout has not been computed yet.
100 int cellSize = static_cast<int>(screen.topRibbonLayout.iconSizePx);
101 if (cellSize <= 0) cellSize = 20;
102
103 // Build the CPU atlas and fill this monitor's icon glyph lookup + metadata.
104 AtlasBitmap iconAtlas = BuildIconAtlas(cellSize, MonitorIconAtlasCPU(monitorId));
105
106 // Upload into this monitor's icon texture via the copy-queue path, then CPU-block until ready.
107 // Caller must have already drained + released any previous atlas for this monitor (RestartRenderThreads),
108 // so this write does not free a texture the GPU is still reading.
109 TextureUploadDesc desc = {};
110 desc.width = iconAtlas.width;
111 desc.height = iconAtlas.height;
112 desc.format = DXGI_FORMAT_R8G8B8A8_UNORM;
113 desc.pixels = iconAtlas.pixels.data();
114 desc.rowPitch = iconAtlas.width * iconAtlas.bytesPerPixel;
115
116 std::atomic<uint64_t> uploadFence = 0;
117 SubmitTextureUpload(desc, &screen.uiIconAtlasTexture, &uploadFence);
118 uint64_t atlasReadyFence = gpu.copyFenceValue.fetch_add(1, std::memory_order_relaxed);
119 uploadFence.store(atlasReadyFence, std::memory_order_release);
120 toCopyThreadCV.notify_one();
121 if (gpu.copyFence->GetCompletedValue() < atlasReadyFence) {
122 ThrowIfFailed(gpu.copyFence->SetEventOnCompletion(atlasReadyFence, gpu.copyFenceEvent));
123 WaitForSingleObject(gpu.copyFenceEvent, INFINITE);
124 }
125
126 // (Re)create this monitor's shader-visible SRV heap: English @ slot 0 (shared texture uploaded by
127 // InitUIResources) + icon @ slot 1 (this monitor's texture). Sized for future dynamic script atlases.
128 D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
129 heapDesc.NumDescriptors = UI_MAX_ATLAS_TEXTURES;
130 heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
131 heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
132 ThrowIfFailed(device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&screen.uiSrvHeap)));
133
134 const UINT inc = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
135 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
136 srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
137 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
138 srvDesc.Texture2D.MipLevels = 1;
139 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
140
141 CD3DX12_CPU_DESCRIPTOR_HANDLE englishHandle(screen.uiSrvHeap->GetCPUDescriptorHandleForHeapStart(),
142 UI_ENGLISH_ATLAS_SLOT, inc);
143 device->CreateShaderResourceView(uiRes.uiAtlasTextures[UI_ENGLISH_ATLAS_SLOT].Get(), &srvDesc, englishHandle);
144
145 CD3DX12_CPU_DESCRIPTOR_HANDLE iconHandle(screen.uiSrvHeap->GetCPUDescriptorHandleForHeapStart(),
146 UI_ICON_ATLAS_SLOT, inc);
147 device->CreateShaderResourceView(screen.uiIconAtlasTexture.Get(), &srvDesc, iconHandle);
148
149 FillSpareAtlasSlots(screen.uiSrvHeap.Get(), uiRes.uiAtlasTextures[UI_ENGLISH_ATLAS_SLOT].Get(),
150 device, UI_FIRST_DYNAMIC_SCRIPT_ATLAS_SLOT);
151
152 char debugName[64];
153 std::snprintf(debugName, sizeof(debugName), "icon_atlas_debug_%d.bmp", monitorId);
154 SaveToBmp(debugName, iconAtlas.pixels.data(), iconAtlas.width, iconAtlas.height, iconAtlas.bytesPerPixel);
155
156 std::wcout << L"Icon atlas built for monitor " << monitorId << L" (cell " << cellSize << L" px)\n";
157}
158
159void InitUIResources( DX12ResourcesUI& uiRes, ID3D12Device* device) {
160 if (!InitFontSystem()) { // FONT system initialization (CPU-side)
161 std::cerr << "Font system initialization failed failed\n";
162 return;
163 }
164
165 // Root signature
166 CD3DX12_DESCRIPTOR_RANGE1 ranges[2]; // Descriptor ranges
167
168 // Range 0: SRV (t0) → from srvHeap // 1: 1 Texture, 0: register t0 = atlas
169 ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, UI_MAX_ATLAS_TEXTURES, 0, 0,
170 D3D12_DESCRIPTOR_RANGE_FLAG_NONE);
171 // Range 1: SAMPLER (s0) → from samplerHeap // 1: 1 Sampler, 0: register s0 = sampler
172 ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_NONE);
173
174 CD3DX12_ROOT_PARAMETER1 rootParams[3];
175 rootParams[0].InitAsConstantBufferView(0, 0, D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
176 D3D12_SHADER_VISIBILITY_VERTEX);// b0 - Ortho constant buffer (vertex shader)
177
178 // Root Parameter 1: Descriptor Table containing only the SRV
179 rootParams[1].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
180
181 // Root Parameter 2: Descriptor Table containing only the SAMPLER
182 rootParams[2].InitAsDescriptorTable(1, &ranges[1],
183 D3D12_SHADER_VISIBILITY_PIXEL);
184
185 CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootDesc;
186 rootDesc.Init_1_1(_countof(rootParams), rootParams, 0, nullptr,
187 D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
188
189 ComPtr<ID3DBlob> signature;
190 ComPtr<ID3DBlob> errorBlob;
191
192 HRESULT hr = D3DX12SerializeVersionedRootSignature(&rootDesc,
193 D3D_ROOT_SIGNATURE_VERSION_1_1, &signature, &errorBlob);
194 if (FAILED(hr)) {
195 if (errorBlob)
196 std::cerr << "Root Signature Serialization Failed:\n"
197 << (char*)errorBlob->GetBufferPointer() << std::endl;
198 ThrowIfFailed(hr); // will print the real error
199 }
200
201 ThrowIfFailed(device->CreateRootSignature(0, signature->GetBufferPointer(),
202 signature->GetBufferSize(), IID_PPV_ARGS(&uiRes.uiRootSignature)));
203 uiRes.uiRootSignature->SetName(L"UI Overlay");
204
205 // Create SRV descriptor heap (1 texture)
206 D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
207 heapDesc.NumDescriptors = UI_MAX_ATLAS_TEXTURES;
208 heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
209 heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
210 ThrowIfFailed(device->CreateDescriptorHeap( &heapDesc, IID_PPV_ARGS(&uiRes.srvHeap) ));
211
212 // Create SAMPLER descriptor heap (shader-visible)
213 D3D12_DESCRIPTOR_HEAP_DESC samplerHeapDesc = {};
214 samplerHeapDesc.NumDescriptors = 1;
215 samplerHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
216 samplerHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
217 ThrowIfFailed(device->CreateDescriptorHeap(&samplerHeapDesc,
218 IID_PPV_ARGS(&uiRes.samplerHeap)));
219
220 // Create the actual sampler.
221 D3D12_SAMPLER_DESC samplerDesc = {};
222 samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
223 samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
224 samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
225 samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
226 samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
227 samplerDesc.MinLOD = 0;
228 samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
229
230 device->CreateSampler(&samplerDesc,
231 uiRes.samplerHeap->GetCPUDescriptorHandleForHeapStart());
232
233 // Shaders are compiled to DXIL during the build and embedded into the executable.
234 // Input layout
235
236 D3D12_INPUT_ELEMENT_DESC layout[] = {
237 { "POSITION",0,DXGI_FORMAT_R32G32_FLOAT,0,0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
238 { "TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
239 { "COLOR",0,DXGI_FORMAT_R32_UINT,0,16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
240 { "TEXCOORD",1,DXGI_FORMAT_R32_UINT,0,20, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 }
241 };
242
243 // PSO
244 D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
245 psoDesc.InputLayout = { layout,_countof(layout) };
246 psoDesc.pRootSignature = uiRes.uiRootSignature.Get();
247 psoDesc.VS = CD3DX12_SHADER_BYTECODE(g_uiVertexShader, sizeof(g_uiVertexShader));
248 psoDesc.PS = CD3DX12_SHADER_BYTECODE(g_uiPixelShader, sizeof(g_uiPixelShader));
249 psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
250 psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
251 psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
252 psoDesc.BlendState.RenderTarget[0].BlendEnable = TRUE;
253 psoDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
254 psoDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
255 psoDesc.DepthStencilState.DepthEnable = FALSE;
256 psoDesc.SampleMask = UINT_MAX;
257 psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
258 psoDesc.NumRenderTargets = 1;
259 psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
260 psoDesc.SampleDesc.Count = 1;
261
262 ThrowIfFailed( device->CreateGraphicsPipelineState( &psoDesc, IID_PPV_ARGS(&uiRes.uiPSO)));
263
264 // The dynamic vertex/index/ortho buffers are per window (DX12ResourcesPerWindow), created
265 // lazily by RenderUIOverlay (see EnsureWindowUIBuffers).
266 std::wcout << L"UI Resources Initialized (Phase 4A)\n";
267
268 AtlasBitmap englishAtlas = BuildMSDFFontAtlas();
269 // The icon atlas is per monitor now (its cell size scales with DPI). It is built + uploaded and its
270 // SRV heap created by BuildMonitorIconAtlas from RestartRenderThreads. InitUIResources only handles
271 // the shared, DPI-independent English MSDF atlas below.
272
273 TextureUploadDesc desc = {};
274 desc.width = englishAtlas.width;
275 desc.height = englishAtlas.height;
276 desc.format = DXGI_FORMAT_R8G8B8A8_UNORM;
277 desc.pixels = englishAtlas.pixels.data();
278 desc.rowPitch = englishAtlas.width * englishAtlas.bytesPerPixel;
279
280 SubmitTextureUpload(desc, &uiRes.uiAtlasTextures[UI_ENGLISH_ATLAS_SLOT], &atlasFence);// Enqueue the upload through upload queue
281 // RESERVED FENCE VALUE FOR THIS UPLOAD (this is the key change)
282 // The copy thread MUST eventually signal exactly this value.
283 uint64_t atlasReadyFence = gpu.copyFenceValue.fetch_add(1, std::memory_order_relaxed);
284 // Tell everyone (including the render thread) what fence value to wait for
285 atlasFence.store(atlasReadyFence, std::memory_order_release);
286 toCopyThreadCV.notify_one(); // Wakeup CPU thread to process the newly uploaded texture.
287 // CPU-blocking wait until Copy Queue has processed this upload
288 if (gpu.copyFence->GetCompletedValue() < atlasReadyFence) {
289 ThrowIfFailed(gpu.copyFence->SetEventOnCompletion(atlasReadyFence, gpu.copyFenceEvent));
290 WaitForSingleObject(gpu.copyFenceEvent, INFINITE); // CPU blocks here
291 }
292
293 // Now the texture is in DEFAULT heap → safe to create SRV
294 D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
295 srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
296 srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
297 srvDesc.Texture2D.MipLevels = 1;
298 srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
299
300 device->CreateShaderResourceView(uiRes.uiAtlasTextures[UI_ENGLISH_ATLAS_SLOT].Get(), &srvDesc,
301 uiRes.srvHeap->GetCPUDescriptorHandleForHeapStart());
302
303 // This shared heap is only ever bound by the print path (PrinterController::Record2DDraws), which
304 // draws text and no icons — so the icon slot stays aliased here too. Icons come from the
305 // per-monitor heaps built by BuildMonitorIconAtlas, which own a real icon SRV at UI_ICON_ATLAS_SLOT.
306 FillSpareAtlasSlots(uiRes.srvHeap.Get(), uiRes.uiAtlasTextures[UI_ENGLISH_ATLAS_SLOT].Get(),
307 device, UI_ENGLISH_ATLAS_SLOT + 1);
308
309 std::wcout << L"Shared English MSDF atlas uploaded: slot " << UI_ENGLISH_ATLAS_SLOT
310 << L" (per-monitor icon atlases built by RestartRenderThreads)\n";
311}
312
313// Cleanup
314void CleanupUIResources(DX12ResourcesUI& uiRes) {
315 uiRes = {};
316}
317
318
319// Creates this window's dynamic UI buffers on first use. Per window by necessity: one monitor
320// command list records all its windows before executing, so a shared upload buffer would be
321// overwritten by the last-recorded window and every window would present that window's UI.
322static void EnsureWindowUIBuffers(DX12ResourcesPerWindow& winRes, ID3D12GraphicsCommandList* cmd,
323 const DX12ResourcesUI& uiRes) {
324 if (winRes.uiVertexBuffer) return;
325
326 ComPtr<ID3D12Device> device;
327 if (FAILED(cmd->GetDevice(IID_PPV_ARGS(&device)))) return;
328
329 auto uploadHeap = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
330
331 auto vbDesc = CD3DX12_RESOURCE_DESC::Buffer(uiRes.maxVertices * sizeof(UIVertex));
332 ThrowIfFailed(device->CreateCommittedResource(&uploadHeap, D3D12_HEAP_FLAG_NONE, &vbDesc,
333 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&winRes.uiVertexBuffer)));
334
335 auto ibDesc = CD3DX12_RESOURCE_DESC::Buffer(uiRes.maxIndices * sizeof(uint16_t));
336 ThrowIfFailed(device->CreateCommittedResource(&uploadHeap, D3D12_HEAP_FLAG_NONE, &ibDesc,
337 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&winRes.uiIndexBuffer)));
338
339 auto cbDesc = CD3DX12_RESOURCE_DESC::Buffer(256);
340 ThrowIfFailed(device->CreateCommittedResource(&uploadHeap, D3D12_HEAP_FLAG_NONE, &cbDesc,
341 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&winRes.uiOrthoConstantBuffer)));
342
343 CD3DX12_RANGE readRange(0, 0);
344 winRes.uiVertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&winRes.pUIVertexDataBegin));
345 winRes.uiIndexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&winRes.pUIIndexDataBegin));
346 winRes.uiOrthoConstantBuffer->Map(0, &readRange, reinterpret_cast<void**>(&winRes.pUIOrthoDataBegin));
347}
348
349// This function renders the list of tabs, all top menu buttons (with dropdowns if required),
350// side favorite / frequent buttons bars, right side property window, bottom status bar.
351// This is also responsible for all relevant DirectX12 configurations required for rendering User Interface.
352void RenderUIOverlay(SingleUIWindow& window, ID3D12GraphicsCommandList* cmd, DX12ResourcesUI& uiRes,
353 UITopRibbonLayout& topRibbonLayout, float monitorDPIX, float monitorDPIY, const UIInput& input,
354 uint64_t activeInternalSubTabMemoryId, int monitorId) {
355
356 if (!cmd) return; //Defensive check.
357 if (monitorId < 0 || monitorId >= MV_MAX_MONITORS) return;
358
359 // This monitor's icon atlas + SRV heap (English@0 shared, icon@1 this monitor). Built by
360 // BuildMonitorIconAtlas in RestartRenderThreads; if not ready yet, skip UI this frame.
361 ID3D12DescriptorHeap* monitorSrvHeap = gpu.screens[monitorId].uiSrvHeap.Get();
362 if (!monitorSrvHeap) return;
363
364 EnsureWindowUIBuffers(window.dx, cmd, uiRes);
365 if (!window.dx.pUIVertexDataBegin || !window.dx.pUIIndexDataBegin ||
366 !window.dx.pUIOrthoDataBegin) {
367 return;
368 }
369
370
371 cmd->SetPipelineState(uiRes.uiPSO.Get());
372 cmd->SetGraphicsRootSignature(uiRes.uiRootSignature.Get());
373
374 // Bind descriptor heap
375 ID3D12DescriptorHeap* heaps[] = { monitorSrvHeap, uiRes.samplerHeap.Get() };
376 cmd->SetDescriptorHeaps(_countof(heaps), heaps);
377
378 // Bind the descriptor table (which contains t0 + s0)
379 // Root Parameter 1 = SRV table// must match rootParams[1]
380 cmd->SetGraphicsRootDescriptorTable(1, monitorSrvHeap->GetGPUDescriptorHandleForHeapStart());
381 // Root Parameter 2 = Sampler table
382 cmd->SetGraphicsRootDescriptorTable(2, uiRes.samplerHeap->GetGPUDescriptorHandleForHeapStart());
383 // Bind this window's ortho constant buffer (still root parameter 0)
384 cmd->SetGraphicsRootConstantBufferView(0, window.dx.uiOrthoConstantBuffer->GetGPUVirtualAddress());
385
386 float W = (float)window.dx.WindowWidth;
387 float H = (float)window.dx.WindowHeight;
388 float* ortho = (float*)window.dx.pUIOrthoDataBegin;
389
390 ortho[0] = 2 / W; ortho[1] = 0; ortho[2] = 0; ortho[3] = -1;
391 ortho[4] = 0; ortho[5] = -2 / H; ortho[6] = 0; ortho[7] = 1;
392 ortho[8] = 0; ortho[9] = 0; ortho[10] = 1; ortho[11] = 0;
393 ortho[12] = 0; ortho[13] = 0; ortho[14] = 0; ortho[15] = 1;
394
395 UIDrawContext ctx;
396 ctx.vertexPtr = reinterpret_cast<UIVertex*>(window.dx.pUIVertexDataBegin);
397 ctx.indexPtr = reinterpret_cast<uint16_t*>(window.dx.pUIIndexDataBegin);
398 ctx.vertexCount = 0;
399 ctx.indexCount = 0;
400
401 // Portable widget/hit-test half (UserInterface.cpp): fills ctx and emits UIActions.
402 BuildUIOverlay(window, ctx, uiRes, topRibbonLayout, monitorDPIX, monitorDPIY, input,
403 activeInternalSubTabMemoryId, monitorId);
404
405 // DRAW ALL UI GEOMETRY
406 if (ctx.indexCount == 0) return;
407
408 D3D12_VERTEX_BUFFER_VIEW vbv{};
409 vbv.BufferLocation = window.dx.uiVertexBuffer->GetGPUVirtualAddress();
410 vbv.SizeInBytes = ctx.vertexCount * sizeof(UIVertex);
411 vbv.StrideInBytes = sizeof(UIVertex);
412
413 D3D12_INDEX_BUFFER_VIEW ibv{};
414 ibv.BufferLocation = window.dx.uiIndexBuffer->GetGPUVirtualAddress();
415 ibv.SizeInBytes = ctx.indexCount * sizeof(uint16_t);
416 ibv.Format = DXGI_FORMAT_R16_UINT;
417
418 cmd->IASetVertexBuffers(0, 1, &vbv);
419 cmd->IASetIndexBuffer(&ibv);
420 cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
421 cmd->DrawIndexedInstanced(ctx.indexCount, 1, 0, 0, 0);
422}
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