The basic Metal plumbing for letting an MTLRasterizationRateMap through is important for Vision Pro, that seems to rely heavily on VRS to reduce the GPU load via eye-tracked, dynamic foveation. Metal's approach is kind of different than everyone else. For a more complete, forward looking story, introduce support for some of the D3D12 and Vulkan VRS features. (these seem to be identical feature-wise, with Vulkan essentially copying D3D, just with a lot more convoluted API) A certain fragmentation is however inevitable. This way we support: - setting a per-draw rate (say I want to draw this mesh with 4x4 rate, i.e. 4x reduced resolution both horizontally and vertically). This is supported with D3D12/Vulkan, and could also be supported with the Qualcomm OpenGL ES extension, although that's not currently implemented. - setting a texture-based rate map, D3D12/Vulkan style. This implies starting to support R8_UINT textures since R8_UNORM, while the difference isn't necessarily relevant here, will be rejected by some APIs. This is done by setting a QRhiTexture on a QRhiShadingRateMap (a new lightweight, wrapper-style object that does not do much on its own, just contains either a texture or some other native object) - setting an MTLRasterizationRateMap on a QRhiShadingRateMap. This is only supported on Metal, naturally, and is realized by an overload of createFrom(). Conversely, setting a QRhiTexture (or attempting to set a per-draw rate) will not work with Metal. We do not offer ways to create a rate map directly via Qt, due to the underlying API differences. So for the user this involves either uploading a Grayscale8 or similar QImage into an R8UI QRhiTexture, or by setting up an MTLRasterizationRateMap directly with Metal and passing that in to the QRhiShadingRateMap instead of a QRhiTexture. We also do not care about the bizarre 'render to a texture, get a buffer with scale rates, and then do a scaling pass using the buffer and MSL intrinsics in the shader' approach of Metal. For now we assume that in case of the Vision Pro this is taken care of by the XR compositor, and is not something the applications (Qt) implements. Some D3D12/Vulkan VRS features are left as potential future enhancements. For example, when it comes to combining the two rates, we always do MAX, there is no API to control the combiners. The third mode of D3D12/Vulkan VRS, per-triangle (writing to a special variable in the vertex shader) is not planned to be supported now or in the future. The Vulkan backend gets quite a lot of changes, mainly due to the difficulty of dealing with extensions in a portable and robust manner in that API, and the API versioning design mistakes in Vulkan 1.1 and beyond. Now both swapchain and texture render pass objects go through vkCreateRenderPass2, when supported, but the KHR variant still, not the 1.2+ core functions, in order to play nice with Vulkan 1.1 and the Android ecosystem. Some QRhi docs are enhanced, e.g. around QRhiRenderPassDescriptor, since as the manual test demonstrates, dynamically taking VRS into use in a scene involves certain steps (new/compatible QRhiRenderPassDescriptor is needed, rebuilding the render target) that are not trivial. (these are just the usual consequences of Vulkan's VkRenderPass permeating everything in the rendering engines) The manual test can be used to exercise both per-draw and image-based VRS with D3D12 and Vulkan. It also includes a limited example of setting up a rate map with Metal (but the test app does not implement the special scaling) A number of things, such as operating with MSAA enabled or using VRS combined with multiview are not currently exercised by the test app, and are left as a future exercise to verify. Task-number: QTBUG-126297 Change-Id: I5210f4cff6b8360b6bb47cdbe8d9caee1c29b8a5 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
673 lines
22 KiB
C++
673 lines
22 KiB
C++
// Copyright (C) 2022 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
|
|
|
#include "qrhiimgui.h"
|
|
#include <QtCore/qfile.h>
|
|
#include <QtGui/qguiapplication.h>
|
|
#include <QtGui/qevent.h>
|
|
#include <QtGui/qclipboard.h>
|
|
#include <QtGui/qimage.h>
|
|
|
|
#include "imgui.h"
|
|
|
|
// the imgui default
|
|
static_assert(sizeof(ImDrawVert) == 20);
|
|
// switched to uint in imconfig.h to avoid trouble with 4 byte offset alignment reqs
|
|
static_assert(sizeof(ImDrawIdx) == 4);
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
static QShader getShader(const QString &name)
|
|
{
|
|
QFile f(name);
|
|
if (f.open(QIODevice::ReadOnly))
|
|
return QShader::fromSerialized(f.readAll());
|
|
|
|
return QShader();
|
|
}
|
|
|
|
QRhiImguiRenderer::~QRhiImguiRenderer()
|
|
{
|
|
releaseResources();
|
|
}
|
|
|
|
void QRhiImguiRenderer::releaseResources()
|
|
{
|
|
for (Texture &t : m_textures) {
|
|
if (t.ownTex)
|
|
delete t.tex;
|
|
delete t.srb;
|
|
}
|
|
m_textures.clear();
|
|
|
|
m_vbuf.reset();
|
|
m_ibuf.reset();
|
|
m_ubuf.reset();
|
|
m_ps.reset();
|
|
m_linearSampler.reset();
|
|
m_nearestSampler.reset();
|
|
|
|
m_rhi = nullptr;
|
|
}
|
|
|
|
void QRhiImguiRenderer::prepare(QRhi *rhi,
|
|
QRhiRenderTarget *rt,
|
|
QRhiCommandBuffer *cb,
|
|
const QMatrix4x4 &mvp,
|
|
float opacity,
|
|
float hdrWhiteLevelMultiplierOrZeroForSDRsRGB)
|
|
{
|
|
if (!m_rhi) {
|
|
m_rhi = rhi;
|
|
} else if (m_rhi != rhi) {
|
|
releaseResources();
|
|
m_rhi = rhi;
|
|
}
|
|
|
|
if (!m_rhi || f.draw.isEmpty())
|
|
return;
|
|
|
|
m_rt = rt;
|
|
m_cb = cb;
|
|
|
|
if (!m_vbuf) {
|
|
m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, f.totalVbufSize));
|
|
m_vbuf->setName(QByteArrayLiteral("imgui vertex buffer"));
|
|
if (!m_vbuf->create())
|
|
return;
|
|
} else {
|
|
if (f.totalVbufSize > m_vbuf->size()) {
|
|
m_vbuf->setSize(f.totalVbufSize);
|
|
if (!m_vbuf->create())
|
|
return;
|
|
}
|
|
}
|
|
if (!m_ibuf) {
|
|
m_ibuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::IndexBuffer, f.totalIbufSize));
|
|
m_ibuf->setName(QByteArrayLiteral("imgui index buffer"));
|
|
if (!m_ibuf->create())
|
|
return;
|
|
} else {
|
|
if (f.totalIbufSize > m_ibuf->size()) {
|
|
m_ibuf->setSize(f.totalIbufSize);
|
|
if (!m_ibuf->create())
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!m_ubuf) {
|
|
m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 4 + 4));
|
|
m_ubuf->setName(QByteArrayLiteral("imgui uniform buffer"));
|
|
if (!m_ubuf->create())
|
|
return;
|
|
}
|
|
|
|
if (!m_linearSampler) {
|
|
m_linearSampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
|
|
QRhiSampler::Repeat, QRhiSampler::Repeat));
|
|
m_linearSampler->setName(QByteArrayLiteral("imgui linear sampler"));
|
|
if (!m_linearSampler->create())
|
|
return;
|
|
}
|
|
|
|
if (!m_nearestSampler) {
|
|
m_nearestSampler.reset(m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
|
|
QRhiSampler::Repeat, QRhiSampler::Repeat));
|
|
m_nearestSampler->setName(QByteArrayLiteral("imgui nearest sampler"));
|
|
if (!m_nearestSampler->create())
|
|
return;
|
|
}
|
|
|
|
if (m_textures.isEmpty()) {
|
|
Texture fontTex;
|
|
fontTex.image = sf.fontTextureData;
|
|
m_textures.insert(nullptr, fontTex);
|
|
sf.reset();
|
|
} else if (sf.isValid()) {
|
|
Texture fontTex;
|
|
fontTex.image = sf.fontTextureData;
|
|
Texture &fontTexEntry(m_textures[nullptr]);
|
|
delete fontTexEntry.tex;
|
|
delete fontTexEntry.srb;
|
|
fontTexEntry = fontTex;
|
|
sf.reset();
|
|
}
|
|
|
|
QVarLengthArray<void *, 8> texturesNeedUpdate;
|
|
for (auto it = m_textures.begin(), end = m_textures.end(); it != end; ++it) {
|
|
Texture &t(*it);
|
|
if (!t.tex) {
|
|
t.tex = m_rhi->newTexture(QRhiTexture::RGBA8, t.image.size());
|
|
t.tex->setName(QByteArrayLiteral("imgui texture ") + QByteArray::number(qintptr(it.key())));
|
|
if (!t.tex->create())
|
|
return;
|
|
texturesNeedUpdate.append(it.key());
|
|
}
|
|
if (!t.srb) {
|
|
QRhiSampler *sampler = t.filter == QRhiSampler::Nearest ? m_nearestSampler.get() : m_linearSampler.get();
|
|
t.srb = m_rhi->newShaderResourceBindings();
|
|
t.srb->setBindings({
|
|
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_ubuf.get()),
|
|
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, t.tex, sampler)
|
|
});
|
|
if (!t.srb->create())
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If layer.enabled is toggled on the item or an ancestor, the render
|
|
// target is then suddenly different and may not be compatible.
|
|
if (m_ps && m_rt->renderPassDescriptor()->serializedFormat() != m_renderPassFormat)
|
|
m_ps.reset();
|
|
|
|
if (m_ps && m_rt->sampleCount() != m_ps->sampleCount())
|
|
m_ps.reset();
|
|
|
|
if (!m_ps) {
|
|
QShader vs = getShader(QLatin1String(":/imgui.vert.qsb"));
|
|
QShader fs = getShader(QLatin1String(":/imgui.frag.qsb"));
|
|
if (!vs.isValid() || !fs.isValid()) {
|
|
qWarning("Failed to load imgui shaders");
|
|
return;
|
|
}
|
|
|
|
m_ps.reset(m_rhi->newGraphicsPipeline());
|
|
QRhiGraphicsPipeline::TargetBlend blend;
|
|
blend.enable = true;
|
|
// Premultiplied alpha (matches imgui.frag). Would not be needed if we
|
|
// only cared about outputting to the window (the common case), but
|
|
// once going through a texture (Item layer, ShaderEffect) which is
|
|
// then sampled by Quick, the result wouldn't be correct otherwise.
|
|
blend.srcColor = QRhiGraphicsPipeline::One;
|
|
blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
|
|
blend.srcAlpha = QRhiGraphicsPipeline::One;
|
|
blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
|
|
m_ps->setTargetBlends({ blend });
|
|
m_ps->setCullMode(QRhiGraphicsPipeline::None);
|
|
m_ps->setDepthTest(true);
|
|
m_ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
|
|
m_ps->setDepthWrite(false);
|
|
m_ps->setFlags(QRhiGraphicsPipeline::UsesScissor | QRhiGraphicsPipeline::UsesShadingRate);
|
|
|
|
m_ps->setShaderStages({
|
|
{ QRhiShaderStage::Vertex, vs },
|
|
{ QRhiShaderStage::Fragment, fs }
|
|
});
|
|
|
|
QRhiVertexInputLayout inputLayout;
|
|
inputLayout.setBindings({
|
|
{ 4 * sizeof(float) + sizeof(quint32) }
|
|
});
|
|
inputLayout.setAttributes({
|
|
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
|
|
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) },
|
|
{ 0, 2, QRhiVertexInputAttribute::UNormByte4, 4 * sizeof(float) }
|
|
});
|
|
m_ps->setVertexInputLayout(inputLayout);
|
|
m_ps->setSampleCount(rt->sampleCount());
|
|
m_ps->setShaderResourceBindings(m_textures[0].srb);
|
|
m_ps->setRenderPassDescriptor(m_rt->renderPassDescriptor());
|
|
m_renderPassFormat = m_rt->renderPassDescriptor()->serializedFormat();
|
|
|
|
if (!m_ps->create())
|
|
return;
|
|
}
|
|
|
|
QRhiResourceUpdateBatch *u = m_rhi->nextResourceUpdateBatch();
|
|
|
|
for (const CmdListBuffer &b : f.vbuf)
|
|
u->updateDynamicBuffer(m_vbuf.get(), b.offset, b.data.size(), b.data.constData());
|
|
|
|
for (const CmdListBuffer &b : f.ibuf)
|
|
u->updateDynamicBuffer(m_ibuf.get(), b.offset, b.data.size(), b.data.constData());
|
|
|
|
u->updateDynamicBuffer(m_ubuf.get(), 0, 64, mvp.constData());
|
|
u->updateDynamicBuffer(m_ubuf.get(), 64, 4, &opacity);
|
|
u->updateDynamicBuffer(m_ubuf.get(), 68, 4, &hdrWhiteLevelMultiplierOrZeroForSDRsRGB);
|
|
|
|
for (int i = 0; i < texturesNeedUpdate.count(); ++i) {
|
|
Texture &t(m_textures[texturesNeedUpdate[i]]);
|
|
if (!t.image.isNull()) {
|
|
u->uploadTexture(t.tex, t.image);
|
|
t.image = QImage();
|
|
}
|
|
}
|
|
|
|
m_cb->resourceUpdate(u);
|
|
}
|
|
|
|
void QRhiImguiRenderer::render()
|
|
{
|
|
if (!m_rhi || f.draw.isEmpty() || !m_ps)
|
|
return;
|
|
|
|
m_cb->setGraphicsPipeline(m_ps.get());
|
|
|
|
const QSize viewportSize = m_rt->pixelSize();
|
|
bool needsViewport = true;
|
|
|
|
for (const DrawCmd &c : f.draw) {
|
|
QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), f.vbuf[c.cmdListBufferIdx].offset);
|
|
if (needsViewport) {
|
|
needsViewport = false;
|
|
m_cb->setViewport({ 0, 0, float(viewportSize.width()), float(viewportSize.height()) });
|
|
}
|
|
const float sx1 = c.clipRect.x() + c.itemPixelOffset.x();
|
|
const float sy1 = c.clipRect.y() + c.itemPixelOffset.y();
|
|
const float sx2 = c.clipRect.z() + c.itemPixelOffset.x();
|
|
const float sy2 = c.clipRect.w() + c.itemPixelOffset.y();
|
|
QPoint scissorPos = QPointF(sx1, viewportSize.height() - sy2).toPoint();
|
|
QSize scissorSize = QSizeF(sx2 - sx1, sy2 - sy1).toSize();
|
|
scissorPos.setX(qMax(0, scissorPos.x()));
|
|
scissorPos.setY(qMax(0, scissorPos.y()));
|
|
scissorSize.setWidth(qMin(viewportSize.width(), scissorSize.width()));
|
|
scissorSize.setHeight(qMin(viewportSize.height(), scissorSize.height()));
|
|
m_cb->setScissor({ scissorPos.x(), scissorPos.y(), scissorSize.width(), scissorSize.height() });
|
|
m_cb->setShaderResources(m_textures[c.textureId].srb);
|
|
m_cb->setVertexInput(0, 1, &vbufBinding, m_ibuf.get(), c.indexOffset, QRhiCommandBuffer::IndexUInt32);
|
|
m_cb->drawIndexed(c.elemCount);
|
|
}
|
|
}
|
|
|
|
void QRhiImguiRenderer::registerCustomTexture(void *id,
|
|
QRhiTexture *texture,
|
|
QRhiSampler::Filter filter,
|
|
CustomTextureOwnership ownership)
|
|
{
|
|
Q_ASSERT(id);
|
|
auto it = m_textures.constFind(id);
|
|
if (it != m_textures.cend()) {
|
|
if (it->ownTex)
|
|
delete it->tex;
|
|
delete it->srb;
|
|
}
|
|
Texture t;
|
|
t.tex = texture;
|
|
t.filter = filter;
|
|
t.ownTex = ownership == TakeCustomTextureOwnership;
|
|
m_textures[id] = t;
|
|
}
|
|
|
|
static const char *getClipboardText(void *)
|
|
{
|
|
static QByteArray contents;
|
|
contents = QGuiApplication::clipboard()->text().toUtf8();
|
|
return contents.constData();
|
|
}
|
|
|
|
static void setClipboardText(void *, const char *text)
|
|
{
|
|
QGuiApplication::clipboard()->setText(QString::fromUtf8(text));
|
|
}
|
|
|
|
QRhiImgui::QRhiImgui()
|
|
{
|
|
context = ImGui::CreateContext();
|
|
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
|
rebuildFontAtlas();
|
|
ImGuiIO &io(ImGui::GetIO());
|
|
io.GetClipboardTextFn = getClipboardText;
|
|
io.SetClipboardTextFn = setClipboardText;
|
|
}
|
|
|
|
QRhiImgui::~QRhiImgui()
|
|
{
|
|
ImGui::DestroyContext(static_cast<ImGuiContext *>(context));
|
|
}
|
|
|
|
void QRhiImgui::rebuildFontAtlas()
|
|
{
|
|
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
|
ImGuiIO &io(ImGui::GetIO());
|
|
unsigned char *pixels;
|
|
int w, h;
|
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &w, &h);
|
|
const QImage wrapperImg(const_cast<const uchar *>(pixels), w, h, QImage::Format_RGBA8888);
|
|
sf.fontTextureData = wrapperImg.copy();
|
|
io.Fonts->SetTexID(nullptr);
|
|
}
|
|
|
|
void QRhiImgui::rebuildFontAtlasWithFont(const QString &filename)
|
|
{
|
|
QFile f(filename);
|
|
if (!f.open(QIODevice::ReadOnly)) {
|
|
qWarning("Failed to open %s", qPrintable(filename));
|
|
return;
|
|
}
|
|
QByteArray font = f.readAll();
|
|
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
|
ImFontConfig fontCfg;
|
|
fontCfg.FontDataOwnedByAtlas = false;
|
|
ImGui::GetIO().Fonts->Clear();
|
|
ImGui::GetIO().Fonts->AddFontFromMemoryTTF(font.data(), font.size(), 20.0f, &fontCfg);
|
|
rebuildFontAtlas();
|
|
}
|
|
|
|
void QRhiImgui::nextFrame(const QSizeF &logicalOutputSize, float dpr, const QPointF &logicalOffset, FrameFunc frameFunc)
|
|
{
|
|
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
|
ImGuiIO &io(ImGui::GetIO());
|
|
|
|
const QPointF itemPixelOffset = logicalOffset * dpr;
|
|
f.outputPixelSize = (logicalOutputSize * dpr).toSize();
|
|
io.DisplaySize.x = logicalOutputSize.width();
|
|
io.DisplaySize.y = logicalOutputSize.height();
|
|
io.DisplayFramebufferScale = ImVec2(dpr, dpr);
|
|
|
|
ImGui::NewFrame();
|
|
if (frameFunc)
|
|
frameFunc();
|
|
ImGui::Render();
|
|
|
|
ImDrawData *draw = ImGui::GetDrawData();
|
|
draw->ScaleClipRects(ImVec2(dpr, dpr));
|
|
|
|
f.vbuf.resize(draw->CmdListsCount);
|
|
f.ibuf.resize(draw->CmdListsCount);
|
|
f.totalVbufSize = 0;
|
|
f.totalIbufSize = 0;
|
|
for (int n = 0; n < draw->CmdListsCount; ++n) {
|
|
const ImDrawList *cmdList = draw->CmdLists[n];
|
|
const int vbufSize = cmdList->VtxBuffer.Size * sizeof(ImDrawVert);
|
|
f.vbuf[n].offset = f.totalVbufSize;
|
|
f.totalVbufSize += vbufSize;
|
|
const int ibufSize = cmdList->IdxBuffer.Size * sizeof(ImDrawIdx);
|
|
f.ibuf[n].offset = f.totalIbufSize;
|
|
f.totalIbufSize += ibufSize;
|
|
}
|
|
f.draw.clear();
|
|
for (int n = 0; n < draw->CmdListsCount; ++n) {
|
|
const ImDrawList *cmdList = draw->CmdLists[n];
|
|
f.vbuf[n].data = QByteArray(reinterpret_cast<const char *>(cmdList->VtxBuffer.Data),
|
|
cmdList->VtxBuffer.Size * sizeof(ImDrawVert));
|
|
f.ibuf[n].data = QByteArray(reinterpret_cast<const char *>(cmdList->IdxBuffer.Data),
|
|
cmdList->IdxBuffer.Size * sizeof(ImDrawIdx));
|
|
const ImDrawIdx *indexBufOffset = nullptr;
|
|
for (int i = 0; i < cmdList->CmdBuffer.Size; ++i) {
|
|
const ImDrawCmd *cmd = &cmdList->CmdBuffer[i];
|
|
const quint32 indexOffset = f.ibuf[n].offset + quintptr(indexBufOffset);
|
|
if (!cmd->UserCallback) {
|
|
QRhiImguiRenderer::DrawCmd dc;
|
|
dc.cmdListBufferIdx = n;
|
|
dc.textureId = cmd->TextureId;
|
|
dc.indexOffset = indexOffset;
|
|
dc.elemCount = cmd->ElemCount;
|
|
dc.itemPixelOffset = itemPixelOffset;
|
|
dc.clipRect = QVector4D(cmd->ClipRect.x, cmd->ClipRect.y, cmd->ClipRect.z, cmd->ClipRect.w);
|
|
f.draw.append(dc);
|
|
} else {
|
|
cmd->UserCallback(cmdList, cmd);
|
|
}
|
|
indexBufOffset += cmd->ElemCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QRhiImgui::syncRenderer(QRhiImguiRenderer *renderer)
|
|
{
|
|
if (sf.isValid()) {
|
|
renderer->sf = sf;
|
|
sf.reset();
|
|
}
|
|
renderer->f = std::move(f);
|
|
}
|
|
|
|
static void updateKeyboardModifiers(Qt::KeyboardModifiers modifiers)
|
|
{
|
|
ImGuiIO &io(ImGui::GetIO());
|
|
io.AddKeyEvent(ImGuiKey_ModCtrl, modifiers.testFlag(Qt::ControlModifier));
|
|
io.AddKeyEvent(ImGuiKey_ModShift, modifiers.testFlag(Qt::ShiftModifier));
|
|
io.AddKeyEvent(ImGuiKey_ModAlt, modifiers.testFlag(Qt::AltModifier));
|
|
io.AddKeyEvent(ImGuiKey_ModSuper, modifiers.testFlag(Qt::MetaModifier));
|
|
}
|
|
|
|
static ImGuiKey mapKey(int k)
|
|
{
|
|
switch (k) {
|
|
case Qt::Key_Space:
|
|
return ImGuiKey_Space;
|
|
case Qt::Key_Apostrophe:
|
|
return ImGuiKey_Apostrophe;
|
|
case Qt::Key_Comma:
|
|
return ImGuiKey_Comma;
|
|
case Qt::Key_Minus:
|
|
return ImGuiKey_Minus;
|
|
case Qt::Key_Period:
|
|
return ImGuiKey_Period;
|
|
case Qt::Key_Slash:
|
|
return ImGuiKey_Slash;
|
|
case Qt::Key_0:
|
|
return ImGuiKey_0;
|
|
case Qt::Key_1:
|
|
return ImGuiKey_1;
|
|
case Qt::Key_2:
|
|
return ImGuiKey_2;
|
|
case Qt::Key_3:
|
|
return ImGuiKey_3;
|
|
case Qt::Key_4:
|
|
return ImGuiKey_4;
|
|
case Qt::Key_5:
|
|
return ImGuiKey_5;
|
|
case Qt::Key_6:
|
|
return ImGuiKey_6;
|
|
case Qt::Key_7:
|
|
return ImGuiKey_8;
|
|
case Qt::Key_8:
|
|
return ImGuiKey_8;
|
|
case Qt::Key_9:
|
|
return ImGuiKey_9;
|
|
case Qt::Key_Semicolon:
|
|
return ImGuiKey_Semicolon;
|
|
case Qt::Key_Equal:
|
|
return ImGuiKey_Equal;
|
|
case Qt::Key_A:
|
|
return ImGuiKey_A;
|
|
case Qt::Key_B:
|
|
return ImGuiKey_B;
|
|
case Qt::Key_C:
|
|
return ImGuiKey_C;
|
|
case Qt::Key_D:
|
|
return ImGuiKey_D;
|
|
case Qt::Key_E:
|
|
return ImGuiKey_E;
|
|
case Qt::Key_F:
|
|
return ImGuiKey_F;
|
|
case Qt::Key_G:
|
|
return ImGuiKey_G;
|
|
case Qt::Key_H:
|
|
return ImGuiKey_H;
|
|
case Qt::Key_I:
|
|
return ImGuiKey_I;
|
|
case Qt::Key_J:
|
|
return ImGuiKey_J;
|
|
case Qt::Key_K:
|
|
return ImGuiKey_K;
|
|
case Qt::Key_L:
|
|
return ImGuiKey_L;
|
|
case Qt::Key_M:
|
|
return ImGuiKey_M;
|
|
case Qt::Key_N:
|
|
return ImGuiKey_N;
|
|
case Qt::Key_O:
|
|
return ImGuiKey_O;
|
|
case Qt::Key_P:
|
|
return ImGuiKey_P;
|
|
case Qt::Key_Q:
|
|
return ImGuiKey_Q;
|
|
case Qt::Key_R:
|
|
return ImGuiKey_R;
|
|
case Qt::Key_S:
|
|
return ImGuiKey_S;
|
|
case Qt::Key_T:
|
|
return ImGuiKey_T;
|
|
case Qt::Key_U:
|
|
return ImGuiKey_U;
|
|
case Qt::Key_V:
|
|
return ImGuiKey_V;
|
|
case Qt::Key_W:
|
|
return ImGuiKey_W;
|
|
case Qt::Key_X:
|
|
return ImGuiKey_X;
|
|
case Qt::Key_Y:
|
|
return ImGuiKey_Y;
|
|
case Qt::Key_Z:
|
|
return ImGuiKey_Z;
|
|
case Qt::Key_BracketLeft:
|
|
return ImGuiKey_LeftBracket;
|
|
case Qt::Key_Backslash:
|
|
return ImGuiKey_Backslash;
|
|
case Qt::Key_BracketRight:
|
|
return ImGuiKey_RightBracket;
|
|
case Qt::Key_QuoteLeft:
|
|
return ImGuiKey_GraveAccent;
|
|
case Qt::Key_Escape:
|
|
return ImGuiKey_Escape;
|
|
case Qt::Key_Tab:
|
|
return ImGuiKey_Tab;
|
|
case Qt::Key_Backspace:
|
|
return ImGuiKey_Backspace;
|
|
case Qt::Key_Return:
|
|
case Qt::Key_Enter:
|
|
return ImGuiKey_Enter;
|
|
case Qt::Key_Insert:
|
|
return ImGuiKey_Insert;
|
|
case Qt::Key_Delete:
|
|
return ImGuiKey_Delete;
|
|
case Qt::Key_Pause:
|
|
return ImGuiKey_Pause;
|
|
case Qt::Key_Print:
|
|
return ImGuiKey_PrintScreen;
|
|
case Qt::Key_Home:
|
|
return ImGuiKey_Home;
|
|
case Qt::Key_End:
|
|
return ImGuiKey_End;
|
|
case Qt::Key_Left:
|
|
return ImGuiKey_LeftArrow;
|
|
case Qt::Key_Up:
|
|
return ImGuiKey_UpArrow;
|
|
case Qt::Key_Right:
|
|
return ImGuiKey_RightArrow;
|
|
case Qt::Key_Down:
|
|
return ImGuiKey_DownArrow;
|
|
case Qt::Key_PageUp:
|
|
return ImGuiKey_PageUp;
|
|
case Qt::Key_PageDown:
|
|
return ImGuiKey_PageDown;
|
|
case Qt::Key_Shift:
|
|
return ImGuiKey_LeftShift;
|
|
case Qt::Key_Control:
|
|
return ImGuiKey_LeftCtrl;
|
|
case Qt::Key_Meta:
|
|
return ImGuiKey_LeftSuper;
|
|
case Qt::Key_Alt:
|
|
return ImGuiKey_LeftAlt;
|
|
case Qt::Key_CapsLock:
|
|
return ImGuiKey_CapsLock;
|
|
case Qt::Key_NumLock:
|
|
return ImGuiKey_NumLock;
|
|
case Qt::Key_ScrollLock:
|
|
return ImGuiKey_ScrollLock;
|
|
case Qt::Key_F1:
|
|
return ImGuiKey_F1;
|
|
case Qt::Key_F2:
|
|
return ImGuiKey_F2;
|
|
case Qt::Key_F3:
|
|
return ImGuiKey_F3;
|
|
case Qt::Key_F4:
|
|
return ImGuiKey_F4;
|
|
case Qt::Key_F5:
|
|
return ImGuiKey_F5;
|
|
case Qt::Key_F6:
|
|
return ImGuiKey_F6;
|
|
case Qt::Key_F7:
|
|
return ImGuiKey_F7;
|
|
case Qt::Key_F8:
|
|
return ImGuiKey_F8;
|
|
case Qt::Key_F9:
|
|
return ImGuiKey_F9;
|
|
case Qt::Key_F10:
|
|
return ImGuiKey_F10;
|
|
case Qt::Key_F11:
|
|
return ImGuiKey_F11;
|
|
case Qt::Key_F12:
|
|
return ImGuiKey_F12;
|
|
default:
|
|
break;
|
|
}
|
|
return ImGuiKey_None;
|
|
}
|
|
|
|
bool QRhiImgui::processEvent(QEvent *event)
|
|
{
|
|
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
|
ImGuiIO &io(ImGui::GetIO());
|
|
|
|
switch (event->type()) {
|
|
case QEvent::MouseButtonPress:
|
|
{
|
|
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
|
updateKeyboardModifiers(me->modifiers());
|
|
Qt::MouseButtons buttons = me->buttons();
|
|
if (buttons.testFlag(Qt::LeftButton) && !pressedMouseButtons.testFlag(Qt::LeftButton))
|
|
io.AddMouseButtonEvent(0, true);
|
|
if (buttons.testFlag(Qt::RightButton) && !pressedMouseButtons.testFlag(Qt::RightButton))
|
|
io.AddMouseButtonEvent(1, true);
|
|
if (buttons.testFlag(Qt::MiddleButton) && !pressedMouseButtons.testFlag(Qt::MiddleButton))
|
|
io.AddMouseButtonEvent(2, true);
|
|
pressedMouseButtons = buttons;
|
|
}
|
|
return true;
|
|
|
|
case QEvent::MouseButtonRelease:
|
|
{
|
|
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
|
Qt::MouseButtons buttons = me->buttons();
|
|
if (!buttons.testFlag(Qt::LeftButton) && pressedMouseButtons.testFlag(Qt::LeftButton))
|
|
io.AddMouseButtonEvent(0, false);
|
|
if (!buttons.testFlag(Qt::RightButton) && pressedMouseButtons.testFlag(Qt::RightButton))
|
|
io.AddMouseButtonEvent(1, false);
|
|
if (!buttons.testFlag(Qt::MiddleButton) && pressedMouseButtons.testFlag(Qt::MiddleButton))
|
|
io.AddMouseButtonEvent(2, false);
|
|
pressedMouseButtons = buttons;
|
|
}
|
|
return true;
|
|
|
|
case QEvent::MouseMove:
|
|
{
|
|
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
|
const QPointF pos = me->position();
|
|
io.AddMousePosEvent(pos.x(), pos.y());
|
|
}
|
|
return true;
|
|
|
|
case QEvent::Wheel:
|
|
{
|
|
QWheelEvent *we = static_cast<QWheelEvent *>(event);
|
|
QPointF wheel(we->angleDelta().x() / 120.0f, we->angleDelta().y() / 120.0f);
|
|
io.AddMouseWheelEvent(wheel.x(), wheel.y());
|
|
}
|
|
return true;
|
|
|
|
case QEvent::KeyPress:
|
|
case QEvent::KeyRelease:
|
|
{
|
|
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
|
|
const bool down = event->type() == QEvent::KeyPress;
|
|
updateKeyboardModifiers(ke->modifiers());
|
|
io.AddKeyEvent(mapKey(ke->key()), down);
|
|
if (down && !ke->text().isEmpty()) {
|
|
const QByteArray text = ke->text().toUtf8();
|
|
io.AddInputCharactersUTF8(text.constData());
|
|
}
|
|
}
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|