1/****************************************************************************
2**
3** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40/*
41 * Based on https://github.com/seanchas116/qtimgui/
42 *
43 * MIT License https://github.com/seanchas116/qtimgui/blob/master/LICENSE
44 *
45 */
46
47#include "imguirenderer_p.h"
48#include <renderview_p.h>
49#include <renderer_p.h>
50#include <submissioncontext_p.h>
51#include <Qt3DRender/private/geometryrenderermanager_p.h>
52
53#include <QDateTime>
54#include <QGuiApplication>
55#include <QMouseEvent>
56#include <QClipboard>
57#include <QCursor>
58#include <QOpenGLExtraFunctions>
59
60#include "imgui.h"
61
62#ifndef GL_VERTEX_ARRAY_BINDING
63// just for building on some platforms, won't run anyway as this requires GL/ES > 2
64#define GL_VERTEX_ARRAY_BINDING 0x85B5
65#endif
66
67QT_BEGIN_NAMESPACE
68
69using namespace Qt3DRender;
70using namespace Render;
71using namespace Render::Debug;
72using namespace Render::OpenGL;
73
74namespace {
75
76 const QHash<int, ImGuiKey> keyMap = {
77 { Qt::Key_Tab, ImGuiKey_Tab },
78 { Qt::Key_Left, ImGuiKey_LeftArrow },
79 { Qt::Key_Right, ImGuiKey_RightArrow },
80 { Qt::Key_Up, ImGuiKey_UpArrow },
81 { Qt::Key_Down, ImGuiKey_DownArrow },
82 { Qt::Key_PageUp, ImGuiKey_PageUp },
83 { Qt::Key_PageDown, ImGuiKey_PageDown },
84 { Qt::Key_Home, ImGuiKey_Home },
85 { Qt::Key_End, ImGuiKey_End },
86 { Qt::Key_Delete, ImGuiKey_Delete },
87 { Qt::Key_Backspace, ImGuiKey_Backspace },
88 { Qt::Key_Enter, ImGuiKey_Enter },
89 { Qt::Key_Escape, ImGuiKey_Escape },
90 { Qt::Key_A, ImGuiKey_A },
91 { Qt::Key_C, ImGuiKey_C },
92 { Qt::Key_V, ImGuiKey_V },
93 { Qt::Key_X, ImGuiKey_X },
94 { Qt::Key_Y, ImGuiKey_Y },
95 { Qt::Key_Z, ImGuiKey_Z },
96 };
97
98 QByteArray g_currentClipboardText;
99
100 // doesn't handle primitive restart when using indexes
101 int vertexToPrimitiveCount(Qt3DRender::QGeometryRenderer::PrimitiveType primitiveType, int numVertices) {
102 int nPrimitives = 0;
103 switch (primitiveType) {
104 case QGeometryRenderer::Points:
105 case QGeometryRenderer::LineLoop: nPrimitives += numVertices; break;
106 case QGeometryRenderer::Triangles: nPrimitives += numVertices / 3; break;
107 case QGeometryRenderer::Lines: nPrimitives += numVertices / 2; break;
108 case QGeometryRenderer::TriangleFan:
109 case QGeometryRenderer::TriangleStrip:
110 case QGeometryRenderer::LineStrip: nPrimitives += numVertices - 1; break;
111 case QGeometryRenderer::TrianglesAdjacency: nPrimitives += numVertices / 6; break;
112 case QGeometryRenderer::TriangleStripAdjacency:
113 case QGeometryRenderer::LineStripAdjacency: nPrimitives += numVertices / 2 - 1; break;
114 case QGeometryRenderer::LinesAdjacency: nPrimitives += numVertices / 4; break;
115 case QGeometryRenderer::Patches: nPrimitives += 1;
116 }
117 return nPrimitives;
118 }
119
120 const char *primitiveTypeName(Qt3DRender::QGeometryRenderer::PrimitiveType primitiveType) {
121 switch (primitiveType) {
122 case QGeometryRenderer::Points: return "Points";
123 case QGeometryRenderer::LineLoop: return "LineLoop";
124 case QGeometryRenderer::Triangles: return "Triangles";
125 case QGeometryRenderer::TrianglesAdjacency: return "TriangleAdjacency";
126 case QGeometryRenderer::TriangleFan: return "TriangleFan";
127 case QGeometryRenderer::TriangleStrip: return "TriangleStrip";
128 case QGeometryRenderer::TriangleStripAdjacency: return "TriangleStringAdjacency";
129 case QGeometryRenderer::LineStrip: return "LineStrip";
130 case QGeometryRenderer::LineStripAdjacency: return "LineStripAdjacency";
131 case QGeometryRenderer::Lines: return "Lines";
132 case QGeometryRenderer::LinesAdjacency: return "LinesAdjacency";
133 case QGeometryRenderer::Patches: return "Patches";
134 }
135 return "";
136 }
137}
138
139ImGuiRenderer::ImGuiRenderer(Qt3DRender::Render::OpenGL::Renderer *renderer)
140 : m_renderer(renderer)
141{
142 ImGui::CreateContext();
143
144 ImGuiIO &io = ImGui::GetIO();
145 for (ImGuiKey key : keyMap.values())
146 io.KeyMap[key] = key;
147
148#ifndef QT_NO_CLIPBOARD
149 io.SetClipboardTextFn = [](void *user_data, const char *text) {
150 Q_UNUSED(user_data)
151 QGuiApplication::clipboard()->setText(QString::fromLatin1(str: text));
152 };
153 io.GetClipboardTextFn = [](void *user_data) {
154 Q_UNUSED(user_data)
155 g_currentClipboardText = QGuiApplication::clipboard()->text().toUtf8();
156 return static_cast<const char *>(g_currentClipboardText.data());
157 };
158#endif
159
160 std::fill(first: std::begin(arr&: m_fpsLog), last: std::end(arr&: m_fpsLog), value: 0.f);
161 std::fill(first: std::begin(arr&: m_jobsLog), last: std::end(arr&: m_jobsLog), value: 0.f);
162 m_fpsRange.first = m_fpsRange.second = 0.f;
163 m_jobsRange.first = m_jobsRange.second = 0.f;
164}
165
166void ImGuiRenderer::renderDebugOverlay(const QVector<RenderView *> &renderViews, const RenderView *renderView, int jobsInLastFrame)
167{
168 if (!newFrame(renderView))
169 return;
170
171 const int renderViewsCount = renderViews.size();
172
173 int logIndex = qMin(IMGUI_PERF_LOG_SIZE - 1, b: ImGui::GetFrameCount());
174 if (logIndex == IMGUI_PERF_LOG_SIZE - 1) {
175 std::rotate(first: std::begin(arr&: m_fpsLog), middle: std::begin(arr&: m_fpsLog) + 1, last: std::end(arr&: m_fpsLog));
176 std::rotate(first: std::begin(arr&: m_jobsLog), middle: std::begin(arr&: m_jobsLog) + 1, last: std::end(arr&: m_jobsLog));
177 }
178 m_fpsLog[logIndex] = ImGui::GetIO().Framerate;
179 m_fpsRange.first = m_fpsRange.second = 0.f;
180 for (float v: m_fpsLog) {
181 m_fpsRange.first = qMin(a: m_fpsRange.first, b: qMax(a: v - 5.f, b: 0.f));
182 m_fpsRange.second = qMax(a: m_fpsRange.second, b: v + 2.f);
183 }
184 m_jobsLog[logIndex] = jobsInLastFrame;
185 m_jobsRange.first = m_jobsRange.second = 0.f;
186 for (float v: m_jobsLog) {
187 m_jobsRange.first = qMin(a: m_jobsRange.first, b: qMax(a: v - 5.f, b: 0.f));
188 m_jobsRange.second = qMax(a: m_jobsRange.second, b: v + 2.f);
189 }
190
191 {
192 ImGui::Begin(name: "Qt3D Profiling");
193 char caption[50];
194 sprintf(s: caption, format: "Avg %.3f ms/frame (%.1f FPS)", static_cast<double>(1000.0f / ImGui::GetIO().Framerate), static_cast<double>(ImGui::GetIO().Framerate));
195 ImGui::PlotLines(label: "FPS", values: m_fpsLog, values_count: logIndex + 1, values_offset: 0, overlay_text: caption, scale_min: m_fpsRange.first, scale_max: m_fpsRange.second, graph_size: ImVec2(0, 80));
196 ImGui::PlotHistogram(label: "Jobs", values: m_jobsLog, values_count: logIndex + 1, values_offset: 0, overlay_text: nullptr, scale_min: m_jobsRange.first, scale_max: m_jobsRange.second, graph_size: ImVec2(0, 80));
197
198 int nCommands = 0;
199 int nVertices = 0;
200 int nPrimitives = 0;
201 QSet<HGeometryRenderer> inUseGeometries;
202 QSet<Qt3DCore::QNodeId> inUseTextures;
203 for (int j=0; j<renderViewsCount; j++) {
204 RenderView *rv = renderViews.at(i: j);
205 nCommands += rv->commandCount();
206 rv->forEachCommand(func: [&] (const RenderCommand &command) {
207 if (command.m_type != RenderCommand::Draw)
208 return;
209 nVertices += command.m_primitiveCount;
210 nPrimitives += vertexToPrimitiveCount(primitiveType: command.m_primitiveType, numVertices: command.m_primitiveCount);
211 inUseGeometries.insert(value: command.m_geometryRenderer);
212 const auto &textures = command.m_parameterPack.textures();
213 for (const auto &ns: textures)
214 inUseTextures.insert(value: ns.nodeId);
215 });
216 }
217
218 auto columnNumber = [](int i) {
219 if (i == 0)
220 ImGui::Text(fmt: "--");
221 else
222 ImGui::Text(fmt: " %d", i);
223 ImGui::NextColumn();
224 };
225 auto column2Numbers = [](int i, int of) {
226 if (of == 0)
227 ImGui::Text(fmt: "--");
228 else
229 ImGui::Text(fmt: " %d of %d", i, of);
230 ImGui::NextColumn();
231 };
232
233 ImGui::Columns(count: 5);
234 ImGui::Separator();
235 for (auto s: {"Jobs", "RV", "Cmds", "Verts", "Prims"}) {
236 ImGui::Text(fmt: "#%s", s);
237 ImGui::NextColumn();
238 }
239 for (auto s: {jobsInLastFrame, renderViewsCount, nCommands, nVertices, nPrimitives})
240 columnNumber(s);
241
242 ImGui::Columns(count: 3);
243 ImGui::Separator();
244 for (auto s: {"Entities", "Geometries", "Textures"}) {
245 ImGui::Text(fmt: "#%s", s);
246 ImGui::NextColumn();
247 }
248 columnNumber(m_renderer->nodeManagers()->renderNodesManager()->count());
249 column2Numbers(inUseGeometries.size(), m_renderer->nodeManagers()->geometryRendererManager()->count());
250 column2Numbers(inUseTextures.size(), m_renderer->nodeManagers()->textureManager()->count());
251
252 ImGui::Columns(count: 1);
253 ImGui::Separator();
254
255 bool pj = m_renderer->services()->systemInformation()->isTraceEnabled();
256 ImGui::AlignTextToFramePadding();
257 ImGui::Text(fmt: "Profiling: ");
258 ImGui::SameLine();
259 if (ImGui::Checkbox(label: "Jobs", v: &pj))
260 QMetaObject::invokeMethod(obj: m_renderer->services()->systemInformation(), member: "setTraceEnabled", type: Qt::QueuedConnection, Q_ARG(bool, pj));
261 ImGui::SameLine();
262 if (ImGui::Button(label: "Reveal"))
263 QMetaObject::invokeMethod(obj: m_renderer->services()->systemInformation(), member: "revealLogFolder", type: Qt::QueuedConnection);
264
265 ImGui::AlignTextToFramePadding();
266 ImGui::Text(fmt: "Show:");
267 ImGui::SameLine();
268 if (ImGui::Button(label: "GL Info"))
269 m_showGLInfoWindow = !m_showGLInfoWindow;
270 ImGui::SameLine();
271 if (ImGui::Button(label: "Render Views"))
272 m_showRenderDetailsWindow = !m_showRenderDetailsWindow;
273
274 ImGui::AlignTextToFramePadding();
275 ImGui::Text(fmt: "Dump:");
276 ImGui::SameLine();
277 if (ImGui::Button(label: "SceneGraph##1"))
278 QMetaObject::invokeMethod(obj: m_renderer->services()->systemInformation(), member: "dumpCommand",
279 type: Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render scenegraph")));
280 ImGui::SameLine();
281 if (ImGui::Button(label: "FrameGraph##1"))
282 QMetaObject::invokeMethod(obj: m_renderer->services()->systemInformation(), member: "dumpCommand",
283 type: Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render framegraph")));
284 ImGui::SameLine();
285 if (ImGui::Button(label: "Render Views##1"))
286 QMetaObject::invokeMethod(obj: m_renderer->services()->systemInformation(), member: "dumpCommand",
287 type: Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render framepaths")));
288
289 ImGui::AlignTextToFramePadding();
290 ImGui::Text(fmt: " ");
291 ImGui::SameLine();
292 if (ImGui::Button(label: "Filter State##1"))
293 QMetaObject::invokeMethod(obj: m_renderer->services()->systemInformation(), member: "dumpCommand",
294 type: Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render filterstates")));
295 ImGui::SameLine();
296 if (ImGui::Button(label: "JobsGraph##1"))
297 QMetaObject::invokeMethod(obj: m_renderer->services()->systemInformation(), member: "dumpCommand",
298 type: Qt::QueuedConnection, Q_ARG(QString, QLatin1String("dump jobs")));
299
300 ImGui::End();
301
302 if (m_showGLInfoWindow)
303 showGLInfo();
304 if (m_showRenderDetailsWindow)
305 showRenderDetails(renderViews);
306 }
307
308 ImGui::Render();
309 renderDrawList(draw_data: ImGui::GetDrawData());
310}
311
312void ImGuiRenderer::setCapabilities(const QString &capabilities)
313{
314 m_capabilities = capabilities.toLatin1();
315}
316
317void ImGuiRenderer::showGLInfo()
318{
319 ImGui::Begin(name: "Open GL Details", p_open: &m_showGLInfoWindow);
320 ImGui::Text(fmt: "%s", m_capabilities.data());
321 ImGui::End();
322}
323
324void ImGuiRenderer::showRenderDetails(const QVector<RenderView *> &renderViews)
325{
326 ImGui::Begin(name: "Render Views", p_open: &m_showRenderDetailsWindow);
327
328 int i = 1;
329 for (const RenderView *view: renderViews) {
330 QString label(QLatin1String("View ") + QString::number(i++));
331 if (ImGui::TreeNode(label: label.toLatin1().data())) {
332 ImGui::Text(fmt: "Viewport: (%.1f, %.1f, %.1f, %.1f)", view->viewport().x(), view->viewport().y(),
333 view->viewport().width(), view->viewport().height());
334 ImGui::Text(fmt: "Surface Size: (%d, %d)", view->surfaceSize().width(), view->surfaceSize().height());
335 ImGui::Text(fmt: "Pixel Ratio: %.1f", view->devicePixelRatio());
336 ImGui::Text(fmt: "No Draw: %s", view->noDraw() ? "TRUE" : "FALSE");
337 ImGui::Text(fmt: "Frustum Culling: %s", view->frustumCulling() ? "TRUE" : "FALSE");
338 ImGui::Text(fmt: "Compute: %s", view->isCompute() ? "TRUE" : "FALSE");
339 ImGui::Text(fmt: "Clear Depth Value: %f", static_cast<double>(view->clearDepthValue()));
340 ImGui::Text(fmt: "Clear Stencil Value: %d", view->clearStencilValue());
341 int j = 1;
342
343 view->forEachCommand(func: [&] (const RenderCommand &command) {
344 GeometryRenderer *rGeometryRenderer = m_renderer->nodeManagers()->data<GeometryRenderer, GeometryRendererManager>(handle: command.m_geometryRenderer);
345 QString label = QString(QLatin1String("Command %1 {%2}")).arg(args: QString::number(j++), args: QString::number(rGeometryRenderer->peerId().id()));
346 if (ImGui::TreeNode(label: label.toLatin1().data())) {
347 ImGui::Text(fmt: "Primitive Type: %s %s", primitiveTypeName(primitiveType: command.m_primitiveType),
348 command.m_drawIndexed ? "(indexed)" : "");
349 ImGui::Text(fmt: "# Vertices: %d", command.m_primitiveCount);
350 ImGui::Text(fmt: "# Primitives: %d", vertexToPrimitiveCount(primitiveType: command.m_primitiveType, numVertices: command.m_primitiveCount));
351 ImGui::Text(fmt: "# Instances: %d", command.m_instanceCount);
352 ImGui::TreePop();
353 }
354 });
355 ImGui::TreePop();
356 ImGui::Separator();
357 }
358 }
359
360 if (ImGui::Button(label: "Dump"))
361 QMetaObject::invokeMethod(obj: m_renderer->services()->systemInformation(), member: "dumpCommand",
362 type: Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render rendercommands")));
363 ImGui::End();
364}
365
366void ImGuiRenderer::renderDrawList(ImDrawData *draw_data)
367{
368 // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
369 ImGuiIO& io = ImGui::GetIO();
370 int fb_width = int(io.DisplaySize.x * io.DisplayFramebufferScale.x);
371 int fb_height = int(io.DisplaySize.y * io.DisplayFramebufferScale.y);
372 if (fb_width == 0 || fb_height == 0)
373 return;
374 draw_data->ScaleClipRects(sc: io.DisplayFramebufferScale);
375
376 // Backup GL state
377 GLint last_active_texture; m_funcs->glGetIntegerv(GL_ACTIVE_TEXTURE, params: &last_active_texture);
378 m_funcs->glActiveTexture(GL_TEXTURE0);
379 GLint last_program; m_funcs->glGetIntegerv(GL_CURRENT_PROGRAM, params: &last_program);
380 GLint last_texture; m_funcs->glGetIntegerv(GL_TEXTURE_BINDING_2D, params: &last_texture);
381 GLint last_array_buffer; m_funcs->glGetIntegerv(GL_ARRAY_BUFFER_BINDING, params: &last_array_buffer);
382 GLint last_element_array_buffer; m_funcs->glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, params: &last_element_array_buffer);
383 GLint last_vertex_array; m_funcs->glGetIntegerv(GL_VERTEX_ARRAY_BINDING, params: &last_vertex_array);
384 GLint last_blend_src_rgb; m_funcs->glGetIntegerv(GL_BLEND_SRC_RGB, params: &last_blend_src_rgb);
385 GLint last_blend_dst_rgb; m_funcs->glGetIntegerv(GL_BLEND_DST_RGB, params: &last_blend_dst_rgb);
386 GLint last_blend_src_alpha; m_funcs->glGetIntegerv(GL_BLEND_SRC_ALPHA, params: &last_blend_src_alpha);
387 GLint last_blend_dst_alpha; m_funcs->glGetIntegerv(GL_BLEND_DST_ALPHA, params: &last_blend_dst_alpha);
388 GLint last_blend_equation_rgb; m_funcs->glGetIntegerv(GL_BLEND_EQUATION_RGB, params: &last_blend_equation_rgb);
389 GLint last_blend_equation_alpha; m_funcs->glGetIntegerv(GL_BLEND_EQUATION_ALPHA, params: &last_blend_equation_alpha);
390 GLint last_viewport[4]; m_funcs->glGetIntegerv(GL_VIEWPORT, params: last_viewport);
391 GLint last_scissor_box[4]; m_funcs->glGetIntegerv(GL_SCISSOR_BOX, params: last_scissor_box);
392 GLboolean last_enable_blend = m_funcs->glIsEnabled(GL_BLEND);
393 GLboolean last_enable_cull_face = m_funcs->glIsEnabled(GL_CULL_FACE);
394 GLboolean last_enable_depth_test = m_funcs->glIsEnabled(GL_DEPTH_TEST);
395 GLboolean last_enable_scissor_test = m_funcs->glIsEnabled(GL_SCISSOR_TEST);
396
397 // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
398 m_funcs->glEnable(GL_BLEND);
399 m_funcs->glBlendEquation(GL_FUNC_ADD);
400 m_funcs->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
401 m_funcs->glDisable(GL_CULL_FACE);
402 m_funcs->glDisable(GL_DEPTH_TEST);
403 m_funcs->glEnable(GL_SCISSOR_TEST);
404
405 // Setup viewport, orthographic projection matrix
406 m_funcs->glViewport(x: 0, y: 0, width: static_cast<GLsizei>(fb_width), height: static_cast<GLsizei>(fb_height));
407 const float ortho_projection[4][4] = {
408 { 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f },
409 { 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f },
410 { 0.0f, 0.0f, -1.0f, 0.0f },
411 {-1.0f, 1.0f, 0.0f, 1.0f },
412 };
413 m_funcs->glUseProgram(program: m_shaderHandle);
414 m_funcs->glUniform1i(location: m_attribLocationTex, x: 0);
415 m_funcs->glUniformMatrix4fv(location: m_attribLocationProjMtx, count: 1, GL_FALSE, value: &ortho_projection[0][0]);
416 m_funcs->glBindVertexArray(array: m_vaoHandle);
417
418 for (int n = 0; n < draw_data->CmdListsCount; n++) {
419 const ImDrawList* cmd_list = draw_data->CmdLists[n];
420 const ImDrawIdx* idx_buffer_offset = nullptr;
421
422 m_funcs->glBindBuffer(GL_ARRAY_BUFFER, buffer: m_vboHandle);
423 m_funcs->glBufferData(GL_ARRAY_BUFFER, size: static_cast<GLsizeiptr>(cmd_list->VtxBuffer.Size) * sizeof(ImDrawVert), data: static_cast<const GLvoid*>(cmd_list->VtxBuffer.Data), GL_STREAM_DRAW);
424
425 m_funcs->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: m_elementsHandle);
426 m_funcs->glBufferData(GL_ELEMENT_ARRAY_BUFFER, size: static_cast<GLsizeiptr>(cmd_list->IdxBuffer.Size) * sizeof(ImDrawIdx), data: static_cast<const GLvoid*>(cmd_list->IdxBuffer.Data), GL_STREAM_DRAW);
427
428 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
429 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
430 if (pcmd->UserCallback) {
431 pcmd->UserCallback(cmd_list, pcmd);
432 } else {
433 m_funcs->glBindTexture(GL_TEXTURE_2D, texture: (GLuint)(intptr_t)pcmd->TextureId);
434 m_funcs->glScissor(x: static_cast<int>(pcmd->ClipRect.x), y: static_cast<int>(fb_height - pcmd->ClipRect.w),
435 width: static_cast<int>(pcmd->ClipRect.z - pcmd->ClipRect.x), height: static_cast<int>(pcmd->ClipRect.w - pcmd->ClipRect.y));
436 m_funcs->glDrawElements(GL_TRIANGLES, count: static_cast<GLsizei>(pcmd->ElemCount), type: sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, indices: idx_buffer_offset);
437 }
438 idx_buffer_offset += pcmd->ElemCount;
439 }
440 }
441
442 // Restore modified GL state
443 m_funcs->glUseProgram(program: last_program);
444 m_funcs->glBindTexture(GL_TEXTURE_2D, texture: last_texture);
445 m_funcs->glActiveTexture(texture: last_active_texture);
446 m_funcs->glBindVertexArray(array: last_vertex_array);
447 m_funcs->glBindBuffer(GL_ARRAY_BUFFER, buffer: last_array_buffer);
448 m_funcs->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: last_element_array_buffer);
449 m_funcs->glBlendEquationSeparate(modeRGB: last_blend_equation_rgb, modeAlpha: last_blend_equation_alpha);
450 m_funcs->glBlendFuncSeparate(srcRGB: last_blend_src_rgb, dstRGB: last_blend_dst_rgb, srcAlpha: last_blend_src_alpha, dstAlpha: last_blend_dst_alpha);
451 if (last_enable_blend)
452 m_funcs->glEnable(GL_BLEND); else m_funcs->glDisable(GL_BLEND);
453 if (last_enable_cull_face)
454 m_funcs->glEnable(GL_CULL_FACE); else m_funcs->glDisable(GL_CULL_FACE);
455 if (last_enable_depth_test)
456 m_funcs->glEnable(GL_DEPTH_TEST); else m_funcs->glDisable(GL_DEPTH_TEST);
457 if (last_enable_scissor_test)
458 m_funcs->glEnable(GL_SCISSOR_TEST); else m_funcs->glDisable(GL_SCISSOR_TEST);
459 m_funcs->glViewport(x: last_viewport[0], y: last_viewport[1], width: static_cast<GLsizei>(last_viewport[2]), height: static_cast<GLsizei>(last_viewport[3]));
460 m_funcs->glScissor(x: last_scissor_box[0], y: last_scissor_box[1], width: static_cast<GLsizei>(last_scissor_box[2]), height: static_cast<GLsizei>(last_scissor_box[3]));
461}
462
463bool ImGuiRenderer::createFontsTexture()
464{
465 // Build texture atlas
466 ImGuiIO& io = ImGui::GetIO();
467 unsigned char* pixels;
468 int width, height;
469 io.Fonts->GetTexDataAsRGBA32(out_pixels: &pixels, out_width: &width, out_height: &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
470
471 // Upload texture to graphics system
472 GLint last_texture;
473 m_funcs->glGetIntegerv(GL_TEXTURE_BINDING_2D, params: &last_texture);
474 m_funcs->glGenTextures(n: 1, textures: &m_fontTexture);
475 m_funcs->glBindTexture(GL_TEXTURE_2D, texture: m_fontTexture);
476 m_funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
477 m_funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
478 m_funcs->glTexImage2D(GL_TEXTURE_2D, level: 0, GL_RGBA, width, height, border: 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
479
480 // Store our identifier
481 io.Fonts->TexID = (void *)(intptr_t)m_fontTexture;
482
483 // Restore state
484 m_funcs->glBindTexture(GL_TEXTURE_2D, texture: last_texture);
485
486 return true;
487}
488
489bool ImGuiRenderer::createDeviceObjects()
490{
491 auto *glContext = m_renderer->submissionContext()->openGLContext();
492 if (glContext->format().majorVersion() < 3) {
493 qWarning() << "Qt3D Profiling overlay requires GL or GL ES >= 3";
494 return false;
495 }
496
497 // Backup GL state
498 GLint last_texture, last_array_buffer, last_vertex_array;
499 m_funcs->glGetIntegerv(GL_TEXTURE_BINDING_2D, params: &last_texture);
500 m_funcs->glGetIntegerv(GL_ARRAY_BUFFER_BINDING, params: &last_array_buffer);
501 m_funcs->glGetIntegerv(GL_VERTEX_ARRAY_BINDING, params: &last_vertex_array);
502
503 const GLchar *vertex_shader =
504 "#version 330\n"
505 "uniform mat4 ProjMtx;\n"
506 "in vec2 Position;\n"
507 "in vec2 UV;\n"
508 "in vec4 Color;\n"
509 "out vec2 Frag_UV;\n"
510 "out vec4 Frag_Color;\n"
511 "void main()\n"
512 "{\n"
513 " Frag_UV = UV;\n"
514 " Frag_Color = Color;\n"
515 " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
516 "}\n";
517
518 const GLchar* fragment_shader =
519 "#version 330\n"
520 "uniform sampler2D Texture;\n"
521 "in vec2 Frag_UV;\n"
522 "in vec4 Frag_Color;\n"
523 "out vec4 Out_Color;\n"
524 "void main()\n"
525 "{\n"
526 " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
527 "}\n";
528
529 const GLchar *vertex_shader_es3 =
530 "#version 300 es\n"
531 "uniform mat4 ProjMtx;\n"
532 "in vec2 Position;\n"
533 "in vec2 UV;\n"
534 "in vec4 Color;\n"
535 "out vec2 Frag_UV;\n"
536 "out vec4 Frag_Color;\n"
537 "void main()\n"
538 "{\n"
539 " Frag_UV = UV;\n"
540 " Frag_Color = Color;\n"
541 " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
542 "}\n";
543
544 const GLchar* fragment_shader_es3 =
545 "#version 300 es\n"
546 "precision highp float;\n"
547 "uniform sampler2D Texture;\n"
548 "in vec2 Frag_UV;\n"
549 "in vec4 Frag_Color;\n"
550 "out vec4 Out_Color;\n"
551 "void main()\n"
552 "{\n"
553 " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
554 "}\n";
555
556// m_shaderHandle = m_funcs->glCreateProgram();
557// m_vertHandle = m_funcs->glCreateShader(GL_VERTEX_SHADER);
558// m_fragHandle = m_funcs->glCreateShader(GL_FRAGMENT_SHADER);
559// auto *glContext = m_renderer->submissionContext()->openGLContext();
560// m_funcs->glShaderSource(m_vertHandle, 1, &vertex_shader, nullptr);
561// m_funcs->glShaderSource(m_fragHandle, 1, &fragment_shader, nullptr);
562// m_funcs->glCompileShader(m_vertHandle);
563// m_funcs->glCompileShader(m_fragHandle);
564// m_funcs->glAttachShader(m_shaderHandle, m_vertHandle);
565// m_funcs->glAttachShader(m_shaderHandle, m_fragHandle);
566// m_funcs->glLinkProgram(m_shaderHandle);
567
568 QString logs;
569 m_shader = new QOpenGLShaderProgram(this);
570 if (glContext->isOpenGLES()) {
571 if (!m_shader->addShaderFromSourceCode(type: QOpenGLShader::Vertex, source: vertex_shader_es3))
572 logs += m_shader->log();
573 if (!m_shader->addShaderFromSourceCode(type: QOpenGLShader::Fragment, source: fragment_shader_es3))
574 logs += m_shader->log();
575 } else {
576 if (!m_shader->addShaderFromSourceCode(type: QOpenGLShader::Vertex, source: vertex_shader))
577 logs += m_shader->log();
578 if (!m_shader->addShaderFromSourceCode(type: QOpenGLShader::Fragment, source: fragment_shader))
579 logs += m_shader->log();
580 }
581 m_shader->link();
582 logs += m_shader->log();
583 if (!logs.isEmpty())
584 qWarning() << logs;
585 m_shaderHandle = m_shader->programId();
586
587 m_attribLocationTex = m_funcs->glGetUniformLocation(program: m_shaderHandle, name: "Texture");
588 m_attribLocationProjMtx = m_funcs->glGetUniformLocation(program: m_shaderHandle, name: "ProjMtx");
589 m_attribLocationPosition = m_funcs->glGetAttribLocation(program: m_shaderHandle, name: "Position");
590 m_attribLocationUV = m_funcs->glGetAttribLocation(program: m_shaderHandle, name: "UV");
591 m_attribLocationColor = m_funcs->glGetAttribLocation(program: m_shaderHandle, name: "Color");
592
593 m_funcs->glGenBuffers(n: 1, buffers: &m_vboHandle);
594 m_funcs->glGenBuffers(n: 1, buffers: &m_elementsHandle);
595
596 m_funcs->glGenVertexArrays(n: 1, arrays: &m_vaoHandle);
597 m_funcs->glBindVertexArray(array: m_vaoHandle);
598 m_funcs->glBindBuffer(GL_ARRAY_BUFFER, buffer: m_vboHandle);
599 m_funcs->glEnableVertexAttribArray(index: m_attribLocationPosition);
600 m_funcs->glEnableVertexAttribArray(index: m_attribLocationUV);
601 m_funcs->glEnableVertexAttribArray(index: m_attribLocationColor);
602
603#define OFFSETOF(TYPE, ELEMENT) (reinterpret_cast<size_t>(&((static_cast<TYPE *>(nullptr))->ELEMENT)))
604 m_funcs->glVertexAttribPointer(indx: m_attribLocationPosition, size: 2, GL_FLOAT, GL_FALSE, stride: sizeof(ImDrawVert), ptr: reinterpret_cast<GLvoid*>(OFFSETOF(ImDrawVert, pos)));
605 m_funcs->glVertexAttribPointer(indx: m_attribLocationUV, size: 2, GL_FLOAT, GL_FALSE, stride: sizeof(ImDrawVert), ptr: reinterpret_cast<GLvoid*>(OFFSETOF(ImDrawVert, uv)));
606 m_funcs->glVertexAttribPointer(indx: m_attribLocationColor, size: 4, GL_UNSIGNED_BYTE, GL_TRUE, stride: sizeof(ImDrawVert), ptr: reinterpret_cast<GLvoid*>(OFFSETOF(ImDrawVert, col)));
607#undef OFFSETOF
608
609 createFontsTexture();
610
611 // Restore modified GL state
612 m_funcs->glBindTexture(GL_TEXTURE_2D, texture: last_texture);
613 m_funcs->glBindBuffer(GL_ARRAY_BUFFER, buffer: last_array_buffer);
614 m_funcs->glBindVertexArray(array: last_vertex_array);
615
616 return true;
617}
618
619bool ImGuiRenderer::newFrame(const RenderView *renderView)
620{
621 if (!m_funcs)
622 m_funcs = m_renderer->submissionContext()->openGLContext()->extraFunctions();
623 if (!m_fontTexture)
624 createDeviceObjects();
625 if (!m_shader)
626 return false;
627
628 ImGuiIO& io = ImGui::GetIO();
629
630 // Setup display size (every frame to accommodate for window resizing)
631 io.DisplaySize = ImVec2(renderView->surfaceSize().width() / renderView->devicePixelRatio(), renderView->surfaceSize().height() / renderView->devicePixelRatio());
632 io.DisplayFramebufferScale = ImVec2(renderView->devicePixelRatio(), renderView->devicePixelRatio());
633
634 // Setup time step
635 double current_time = QDateTime::currentMSecsSinceEpoch() / 1000.;
636 io.DeltaTime = m_time > 0.0 ? static_cast<float>(current_time - m_time) : 1.0f / 60.0f;
637 if (io.DeltaTime == 0.f)
638 io.DeltaTime = 1.0f / 60.0f;
639 m_time = current_time;
640
641 // Setup inputs
642 for (int i = 0; i < 3; i++)
643 io.MouseDown[i] = m_mousePressed[i];
644
645 io.MouseWheelH = m_mouseWheelH;
646 io.MouseWheel = m_mouseWheel;
647 m_mouseWheelH = 0;
648 m_mouseWheel = 0;
649
650 // Start the frame
651 ImGui::NewFrame();
652 return true;
653}
654
655void ImGuiRenderer::onMouseChange(QMouseEvent *event)
656{
657 ImGuiIO& io = ImGui::GetIO();
658 io.MousePos = ImVec2(event->pos().x(), event->pos().y());
659 m_mousePressed[0] = event->buttons() & Qt::LeftButton;
660 m_mousePressed[1] = event->buttons() & Qt::RightButton;
661 m_mousePressed[2] = event->buttons() & Qt::MiddleButton;
662}
663
664void ImGuiRenderer::onWheel(QWheelEvent *event)
665{
666 // 5 lines per unit
667 m_mouseWheelH += event->pixelDelta().x() / (ImGui::GetTextLineHeight());
668 m_mouseWheel += event->pixelDelta().y() / (5.f * ImGui::GetTextLineHeight());
669}
670
671void ImGuiRenderer::onKeyPressRelease(QKeyEvent *event)
672{
673 ImGuiIO& io = ImGui::GetIO();
674 if (keyMap.contains(akey: event->key()))
675 io.KeysDown[keyMap[event->key()]] = event->type() == QEvent::KeyPress;
676
677 if (event->type() == QEvent::KeyPress) {
678 QString text = event->text();
679 if (text.size() == 1)
680 io.AddInputCharacter(c: static_cast<ImWchar>(text.at(i: 0).unicode()));
681 }
682
683#ifdef Q_OS_DARWIN
684 io.KeyCtrl = event->modifiers() & Qt::MetaModifier;
685 io.KeyShift = event->modifiers() & Qt::ShiftModifier;
686 io.KeyAlt = event->modifiers() & Qt::AltModifier;
687 io.KeySuper = event->modifiers() & Qt::ControlModifier; // Command key
688#else
689 io.KeyCtrl = event->modifiers() & Qt::ControlModifier;
690 io.KeyShift = event->modifiers() & Qt::ShiftModifier;
691 io.KeyAlt = event->modifiers() & Qt::AltModifier;
692 io.KeySuper = event->modifiers() & Qt::MetaModifier;
693#endif
694}
695
696void ImGuiRenderer::processEvent(QEvent *event)
697{
698 switch (event->type()) {
699 case QEvent::MouseMove:
700 case QEvent::MouseButtonPress:
701 case QEvent::MouseButtonRelease:
702 this->onMouseChange(event: static_cast<QMouseEvent *>(event));
703 break;
704 case QEvent::Wheel:
705 this->onWheel(event: static_cast<QWheelEvent *>(event));
706 break;
707 case QEvent::KeyPress:
708 case QEvent::KeyRelease:
709 this->onKeyPressRelease(event: static_cast<QKeyEvent *>(event));
710 break;
711 default:
712 break;
713 }
714}
715
716QT_END_NAMESPACE
717

source code of qt3d/src/plugins/renderers/opengl/debug/imguirenderer.cpp