Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/ext/imgui/imgui_impl_thin3d.cpp
3187 views
1
// dear imgui: Renderer Backend for PPSSPP's thin3d
2
3
#include "imgui.h"
4
#include "imgui_impl_thin3d.h"
5
#include <cstdio>
6
7
#include "Common/System/Display.h"
8
#include "Common/Math/lin/matrix4x4.h"
9
10
static Lin::Matrix4x4 g_drawMatrix;
11
12
static ImFont *g_proportionalFont = nullptr;
13
static ImFont *g_fixedFont = nullptr;
14
15
enum class RegisteredTextureType {
16
Framebuffer,
17
Texture,
18
NativeTexture,
19
};
20
21
struct RegisteredTexture {
22
RegisteredTextureType type;
23
union {
24
void *nativeTexture;
25
Draw::Texture *texture;
26
struct {
27
Draw::Framebuffer *framebuffer;
28
Draw::Aspect aspect;
29
};
30
};
31
ImGuiPipeline pipeline;
32
};
33
34
struct BackendData {
35
Draw::SamplerState *fontSampler = nullptr;
36
Draw::Texture *fontImage = nullptr;
37
Draw::Pipeline *pipelines[2]{};
38
std::vector<RegisteredTexture> tempTextures;
39
};
40
41
#define TEX_ID_OFFSET 256
42
43
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
44
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
45
// FIXME: multi-context support is not tested and probably dysfunctional in this backend.
46
static BackendData *ImGui_ImplThin3d_GetBackendData() {
47
return ImGui::GetCurrentContext() ? (BackendData *)ImGui::GetIO().BackendRendererUserData : nullptr;
48
}
49
50
// Render function
51
void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *draw) {
52
if (!draw_data) {
53
// Possible race condition.
54
return;
55
}
56
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
57
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
58
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
59
if (fb_width <= 0 || fb_height <= 0) {
60
return;
61
}
62
63
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
64
draw->BindSamplerStates(0, 1, &bd->fontSampler);
65
66
// Setup viewport
67
Draw::Viewport viewport;
68
viewport.TopLeftX = 0;
69
viewport.TopLeftY = 0;
70
viewport.Width = (float)fb_width;
71
viewport.Height = (float)fb_height;
72
viewport.MinDepth = 0.0f;
73
viewport.MaxDepth = 1.0f;
74
draw->SetViewport(viewport);
75
76
Lin::Matrix4x4 mtx = ComputeOrthoMatrix(draw_data->DisplaySize.x, draw_data->DisplaySize.y, draw->GetDeviceCaps().coordConvention);
77
78
Draw::VsTexColUB ub{};
79
memcpy(ub.WorldViewProj, mtx.getReadPtr(), sizeof(Lin::Matrix4x4));
80
ub.saturation = 1.0f;
81
82
// Will project scissor/clipping rectangles into framebuffer space
83
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
84
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
85
86
_assert_(sizeof(ImDrawIdx) == 2);
87
88
ImTextureID prevTexId = (ImTextureID)-1;
89
90
std::vector<Draw::ClippedDraw> draws;
91
Draw::Texture *boundTexture = nullptr;
92
Draw::Framebuffer *boundFBAsTexture = nullptr;
93
void *boundNativeTexture = nullptr;
94
Draw::Pipeline *boundPipeline = bd->pipelines[0];
95
Draw::SamplerState *boundSampler = bd->fontSampler;
96
Draw::Aspect boundAspect = Draw::Aspect::COLOR_BIT;
97
98
// Render command lists
99
for (int n = 0; n < draw_data->CmdListsCount; n++) {
100
const ImDrawList* cmd_list = draw_data->CmdLists[n];
101
draws.clear();
102
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
103
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
104
// We don't use the callback mechanism.
105
_dbg_assert_(pcmd->UserCallback == nullptr);
106
107
// Update the texture pointers.
108
if (!pcmd->TextureId) {
109
// Default
110
boundTexture = bd->fontImage;
111
boundNativeTexture = nullptr;
112
boundFBAsTexture = nullptr;
113
boundPipeline = bd->pipelines[0];
114
boundSampler = bd->fontSampler;
115
} else {
116
size_t index = (size_t)pcmd->TextureId - TEX_ID_OFFSET;
117
if (index >= bd->tempTextures.size()) {
118
WARN_LOG(Log::System, "Missing temp texture %d (out of %d)", (int)index, (int)bd->tempTextures.size());
119
continue;
120
}
121
_dbg_assert_(index < bd->tempTextures.size());
122
switch (bd->tempTextures[index].type) {
123
case RegisteredTextureType::Framebuffer:
124
boundFBAsTexture = bd->tempTextures[index].framebuffer;
125
boundTexture = nullptr;
126
boundNativeTexture = nullptr;
127
boundAspect = bd->tempTextures[index].aspect;
128
break;
129
case RegisteredTextureType::Texture:
130
boundTexture = bd->tempTextures[index].texture;
131
boundFBAsTexture = nullptr;
132
boundNativeTexture = nullptr;
133
boundAspect = Draw::Aspect::COLOR_BIT;
134
break;
135
case RegisteredTextureType::NativeTexture:
136
boundTexture = nullptr;
137
boundFBAsTexture = nullptr;
138
boundNativeTexture = bd->tempTextures[index].nativeTexture;
139
boundAspect = Draw::Aspect::COLOR_BIT;
140
break;
141
}
142
boundPipeline = bd->pipelines[(int)bd->tempTextures[index].pipeline];
143
boundSampler = bd->fontSampler;
144
}
145
146
// Project scissor/clipping rectangles into framebuffer space
147
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
148
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
149
150
// Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds
151
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
152
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
153
if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; }
154
if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; }
155
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
156
continue;
157
158
Draw::ClippedDraw clippedDraw;
159
clippedDraw.pipeline = boundPipeline;
160
clippedDraw.bindTexture = boundTexture;
161
clippedDraw.bindFramebufferAsTex = boundFBAsTexture;
162
clippedDraw.bindNativeTexture = boundNativeTexture;
163
clippedDraw.samplerState = boundSampler;
164
clippedDraw.aspect = boundAspect;
165
clippedDraw.clipx = clip_min.x;
166
clippedDraw.clipy = clip_min.y;
167
clippedDraw.clipw = clip_max.x - clip_min.x;
168
clippedDraw.cliph = clip_max.y - clip_min.y;
169
clippedDraw.indexCount = pcmd->ElemCount;
170
clippedDraw.indexOffset = pcmd->IdxOffset;
171
draws.push_back(clippedDraw);
172
}
173
draw->DrawIndexedClippedBatchUP(cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.size(), cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.size(), draws, &ub, sizeof(ub));
174
}
175
176
draw->SetScissorRect(0, 0, fb_width, fb_height);
177
178
// Discard temp textures.
179
bd->tempTextures.clear();
180
}
181
182
bool ImGui_ImplThin3d_CreateDeviceObjects(Draw::DrawContext *draw) {
183
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
184
185
if (!bd->fontSampler) {
186
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
187
Draw::SamplerStateDesc desc{};
188
desc.magFilter = Draw::TextureFilter::LINEAR;
189
desc.minFilter = Draw::TextureFilter::LINEAR;
190
desc.mipFilter = Draw::TextureFilter::NEAREST;
191
desc.wrapU = Draw::TextureAddressMode::REPEAT;
192
desc.wrapV = Draw::TextureAddressMode::REPEAT;
193
desc.wrapW = Draw::TextureAddressMode::REPEAT;
194
desc.maxAniso = 1.0f;
195
bd->fontSampler = draw->CreateSamplerState(desc);
196
}
197
198
if (!bd->pipelines[0]) {
199
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
200
201
using namespace Draw;
202
203
static const Draw::InputLayoutDesc ilDesc = {
204
sizeof(ImDrawVert),
205
{
206
{ SEM_POSITION, DataFormat::R32G32_FLOAT, offsetof(ImDrawVert, pos) },
207
{ SEM_TEXCOORD0, DataFormat::R32G32_FLOAT, offsetof(ImDrawVert, uv) },
208
{ SEM_COLOR0, DataFormat::R8G8B8A8_UNORM, offsetof(ImDrawVert, col) },
209
},
210
};
211
InputLayout *inputLayout = draw->CreateInputLayout(ilDesc);
212
213
BlendState *blend = draw->CreateBlendState({ true, 0xF,
214
BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA, BlendOp::ADD,
215
BlendFactor::ONE, BlendFactor::ONE_MINUS_SRC_ALPHA, BlendOp::ADD,
216
});
217
BlendState *blendOpaque = draw->CreateBlendState({ false, 0xF });
218
219
DepthStencilStateDesc dsDesc{};
220
DepthStencilState *depthStencil = draw->CreateDepthStencilState(dsDesc);
221
RasterState *rasterNoCull = draw->CreateRasterState({});
222
223
ShaderModule *vs_texture_color_2d = draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D);
224
ShaderModule *fs_texture_color_2d = draw->GetFshaderPreset(FS_TEXTURE_COLOR_2D);
225
226
PipelineDesc pipelineDesc{
227
Primitive::TRIANGLE_LIST,
228
{ vs_texture_color_2d, fs_texture_color_2d },
229
inputLayout,
230
depthStencil,
231
blend,
232
rasterNoCull,
233
&vsTexColBufDesc
234
};
235
236
bd->pipelines[0] = draw->CreateGraphicsPipeline(pipelineDesc, "imgui-pipeline");
237
pipelineDesc.blend = blendOpaque;
238
bd->pipelines[1] = draw->CreateGraphicsPipeline(pipelineDesc, "imgui-pipeline-opaque");
239
240
inputLayout->Release();
241
blend->Release();
242
blendOpaque->Release();
243
depthStencil->Release();
244
rasterNoCull->Release();
245
}
246
247
if (!bd->fontImage) {
248
ImGuiIO& io = ImGui::GetIO();
249
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
250
251
unsigned char* pixels;
252
int width, height;
253
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
254
size_t upload_size = width * height * 4 * sizeof(char);
255
256
Draw::TextureDesc desc{};
257
desc.width = width;
258
desc.height = height;
259
desc.mipLevels = 1;
260
desc.format = Draw::DataFormat::R8G8B8A8_UNORM;
261
desc.type = Draw::TextureType::LINEAR2D;
262
desc.swizzle = Draw::TextureSwizzle::DEFAULT;
263
desc.depth = 1;
264
desc.tag = "imgui-font";
265
desc.initData.push_back((const uint8_t *)pixels);
266
bd->fontImage = draw->CreateTexture(desc);
267
io.Fonts->SetTexID(0);
268
}
269
270
return true;
271
}
272
273
void ImGui_ImplThin3d_DestroyDeviceObjects() {
274
ImGuiIO& io = ImGui::GetIO();
275
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
276
if (bd->fontImage) {
277
bd->fontImage->Release();
278
bd->fontImage = nullptr;
279
io.Fonts->SetTexID(0);
280
}
281
for (int i = 0; i < ARRAY_SIZE(bd->pipelines); i++) {
282
if (bd->pipelines[i]) {
283
bd->pipelines[i]->Release();
284
bd->pipelines[i] = nullptr;
285
}
286
}
287
if (bd->fontSampler) {
288
bd->fontSampler->Release();
289
bd->fontSampler = nullptr;
290
}
291
}
292
293
bool ImGui_ImplThin3d_Init(Draw::DrawContext *draw,
294
const uint8_t *ttf_font_proportional, size_t proportional_size,
295
const uint8_t *ttf_font_fixed, size_t fixed_size) {
296
ImGuiIO& io = ImGui::GetIO();
297
g_proportionalFont = nullptr;
298
if (ttf_font_proportional) {
299
g_proportionalFont = io.Fonts->AddFontFromMemoryTTF((void *)ttf_font_proportional, (int)proportional_size, 21.0f / g_display.dpi_scale_x, nullptr, io.Fonts->GetGlyphRangesDefault());
300
}
301
if (ttf_font_fixed) {
302
g_fixedFont = io.Fonts->AddFontFromMemoryTTF((void *)ttf_font_fixed, (int)fixed_size, 20.0f / g_display.dpi_scale_x, nullptr, io.Fonts->GetGlyphRangesDefault());
303
} else {
304
g_fixedFont = io.Fonts->AddFontDefault();
305
}
306
307
if (!g_proportionalFont) {
308
g_proportionalFont = g_fixedFont;
309
}
310
311
// g_fixedFont = io.Fonts->AddFontDefault();
312
ImGui::GetStyle().ScaleAllSizes(1.0f / g_display.dpi_scale_x);
313
ImGui::GetStyle().Colors[ImGuiCol_Border] = ImColor(IM_COL32(0x2A, 0x2F, 0x3B, 0xFF));
314
315
IMGUI_CHECKVERSION();
316
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
317
318
// Setup backend capabilities flags
319
BackendData* bd = IM_NEW(BackendData)();
320
io.BackendRendererUserData = (void*)bd;
321
io.BackendRendererName = "imgui_impl_thin3d";
322
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
323
ImGui_ImplThin3d_CreateDeviceObjects(draw);
324
return true;
325
}
326
327
void ImGui_PushFixedFont() {
328
ImGui::PushFont(g_fixedFont);
329
}
330
331
void ImGui_PopFont() {
332
ImGui::PopFont();
333
}
334
335
ImFont *ImGui_GetFixedFont() {
336
return g_fixedFont;
337
}
338
339
void ImGui_ImplThin3d_Shutdown() {
340
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
341
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
342
ImGuiIO& io = ImGui::GetIO();
343
344
ImGui_ImplThin3d_DestroyDeviceObjects();
345
io.BackendRendererName = nullptr;
346
io.BackendRendererUserData = nullptr;
347
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
348
IM_DELETE(bd);
349
}
350
351
void ImGui_ImplThin3d_NewFrame(Draw::DrawContext *draw, Lin::Matrix4x4 drawMatrix) {
352
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
353
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplThin3d_Init()?");
354
355
// This one checks if objects already have been created, so ok to call every time.
356
ImGui_ImplThin3d_CreateDeviceObjects(draw);
357
g_drawMatrix = drawMatrix;
358
}
359
360
ImTextureID ImGui_ImplThin3d_AddNativeTextureTemp(void *texture, ImGuiPipeline pipeline) {
361
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
362
363
RegisteredTexture tex{ RegisteredTextureType::NativeTexture};
364
tex.nativeTexture = texture;
365
tex.pipeline = pipeline;
366
367
bd->tempTextures.push_back(tex);
368
return (ImTextureID)(uint64_t)(TEX_ID_OFFSET + bd->tempTextures.size() - 1);
369
}
370
371
ImTextureID ImGui_ImplThin3d_AddTextureTemp(Draw::Texture *texture, ImGuiPipeline pipeline) {
372
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
373
374
RegisteredTexture tex{ RegisteredTextureType::Texture };
375
tex.texture = texture;
376
tex.pipeline = pipeline;
377
378
bd->tempTextures.push_back(tex);
379
return (ImTextureID)(uint64_t)(TEX_ID_OFFSET + bd->tempTextures.size() - 1);
380
}
381
382
ImTextureID ImGui_ImplThin3d_AddFBAsTextureTemp(Draw::Framebuffer *framebuffer, Draw::Aspect aspect, ImGuiPipeline pipeline) {
383
BackendData* bd = ImGui_ImplThin3d_GetBackendData();
384
385
RegisteredTexture tex{ RegisteredTextureType::Framebuffer };
386
tex.framebuffer = framebuffer;
387
tex.aspect = aspect;
388
tex.pipeline = pipeline;
389
390
bd->tempTextures.push_back(tex);
391
return (ImTextureID)(uint64_t)(TEX_ID_OFFSET + bd->tempTextures.size() - 1);
392
}
393
394