RenderWidget: Hook up to ImGui

This commit is contained in:
Stenzek 2018-10-10 21:41:52 +10:00
parent 63dd91628d
commit 36ce47635b
6 changed files with 108 additions and 0 deletions

View File

@ -143,6 +143,7 @@ PRIVATE
core core
Qt5::Widgets Qt5::Widgets
uicommon uicommon
imgui
) )
if(WIN32) if(WIN32)

View File

@ -463,6 +463,9 @@
<ProjectReference Include="$(CoreDir)VideoBackends\Vulkan\Vulkan.vcxproj"> <ProjectReference Include="$(CoreDir)VideoBackends\Vulkan\Vulkan.vcxproj">
<Project>{29f29a19-f141-45ad-9679-5a2923b49da3}</Project> <Project>{29f29a19-f141-45ad-9679-5a2923b49da3}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\..\Externals\imgui\imgui.vcxproj">
<Project>{4c3b2264-ea73-4a7b-9cfe-65b0fd635ebb}</Project>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@ -17,6 +17,8 @@
#include <QScreen> #include <QScreen>
#include <QTimer> #include <QTimer>
#include "imgui.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/State.h" #include "Core/State.h"
@ -26,6 +28,7 @@
#include "DolphinQt/Resources.h" #include "DolphinQt/Resources.h"
#include "DolphinQt/Settings.h" #include "DolphinQt/Settings.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
@ -49,6 +52,8 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent)
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
SetFillBackground(SConfig::GetInstance().bRenderToMain && state == Core::State::Uninitialized); SetFillBackground(SConfig::GetInstance().bRenderToMain && state == Core::State::Uninitialized);
if (state == Core::State::Running)
SetImGuiKeyMap();
}); });
// We have to use Qt::DirectConnection here because we don't want those signals to get queued // We have to use Qt::DirectConnection here because we don't want those signals to get queued
@ -153,6 +158,8 @@ void RenderWidget::showFullScreen()
bool RenderWidget::event(QEvent* event) bool RenderWidget::event(QEvent* event)
{ {
PassEventToImGui(event);
switch (event->type()) switch (event->type())
{ {
case QEvent::Paint: case QEvent::Paint:
@ -244,3 +251,83 @@ void RenderWidget::OnFreeLookMouseMove(QMouseEvent* event)
m_last_mouse[0] = event->x(); m_last_mouse[0] = event->x();
m_last_mouse[1] = event->y(); m_last_mouse[1] = event->y();
} }
void RenderWidget::PassEventToImGui(const QEvent* event)
{
if (!Core::IsRunningAndStarted())
return;
switch (event->type())
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
{
// As the imgui KeysDown array is only 512 elements wide, and some Qt keys which
// we need to track (e.g. alt) are above this value, we mask the lower 9 bits.
// Even masked, the key codes are still unique, so conflicts aren't an issue.
// The actual text input goes through AddInputCharactersUTF8().
const QKeyEvent* key_event = static_cast<const QKeyEvent*>(event);
const bool is_down = event->type() == QEvent::KeyPress;
const int key = (key_event->key() & 0x1FF);
auto lock = g_renderer->GetImGuiLock();
if (key < ArraySize(ImGui::GetIO().KeysDown))
ImGui::GetIO().KeysDown[key] = is_down;
if (is_down)
{
auto utf8 = key_event->text().toUtf8();
ImGui::GetIO().AddInputCharactersUTF8(utf8.constData());
}
}
break;
case QEvent::MouseMove:
{
auto lock = g_renderer->GetImGuiLock();
ImGui::GetIO().MousePos.x = static_cast<const QMouseEvent*>(event)->x();
ImGui::GetIO().MousePos.y = static_cast<const QMouseEvent*>(event)->y();
}
break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
{
auto lock = g_renderer->GetImGuiLock();
const u32 button_mask = static_cast<u32>(static_cast<const QMouseEvent*>(event)->buttons());
for (size_t i = 0; i < ArraySize(ImGui::GetIO().MouseDown); i++)
ImGui::GetIO().MouseDown[i] = (button_mask & (1u << i)) != 0;
}
break;
default:
break;
}
}
void RenderWidget::SetImGuiKeyMap()
{
static const int key_map[][2] = {{ImGuiKey_Tab, Qt::Key_Tab},
{ImGuiKey_LeftArrow, Qt::Key_Left},
{ImGuiKey_RightArrow, Qt::Key_Right},
{ImGuiKey_UpArrow, Qt::Key_Up},
{ImGuiKey_DownArrow, Qt::Key_Down},
{ImGuiKey_PageUp, Qt::Key_PageUp},
{ImGuiKey_PageDown, Qt::Key_PageDown},
{ImGuiKey_Home, Qt::Key_Home},
{ImGuiKey_End, Qt::Key_End},
{ImGuiKey_Insert, Qt::Key_Insert},
{ImGuiKey_Delete, Qt::Key_Delete},
{ImGuiKey_Backspace, Qt::Key_Backspace},
{ImGuiKey_Space, Qt::Key_Space},
{ImGuiKey_Enter, Qt::Key_Enter},
{ImGuiKey_Escape, Qt::Key_Escape},
{ImGuiKey_A, Qt::Key_A},
{ImGuiKey_C, Qt::Key_C},
{ImGuiKey_V, Qt::Key_V},
{ImGuiKey_X, Qt::Key_X},
{ImGuiKey_Y, Qt::Key_Y},
{ImGuiKey_Z, Qt::Key_Z}};
auto lock = g_renderer->GetImGuiLock();
for (size_t i = 0; i < ArraySize(key_map); i++)
ImGui::GetIO().KeyMap[key_map[i][0]] = (key_map[i][1] & 0x1FF);
}

View File

@ -36,6 +36,8 @@ private:
void OnKeepOnTopChanged(bool top); void OnKeepOnTopChanged(bool top);
void SetFillBackground(bool fill); void SetFillBackground(bool fill);
void OnFreeLookMouseMove(QMouseEvent* event); void OnFreeLookMouseMove(QMouseEvent* event);
void PassEventToImGui(const QEvent* event);
void SetImGuiKeyMap();
void dragEnterEvent(QDragEnterEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override; void dropEvent(QDropEvent* event) override;

View File

@ -812,9 +812,18 @@ void Renderer::DrawImGui()
} }
} }
std::unique_lock<std::mutex> Renderer::GetImGuiLock()
{
return std::unique_lock<std::mutex>(m_imgui_mutex);
}
void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,
u64 ticks) u64 ticks)
{ {
// Hold the imgui lock while we're presenting.
// It's only to prevent races on inputs anyway, at this point.
std::unique_lock<std::mutex> imgui_lock(m_imgui_mutex);
const AspectMode suggested = g_ActiveConfig.suggested_aspect_mode; const AspectMode suggested = g_ActiveConfig.suggested_aspect_mode;
if (suggested == AspectMode::Analog || suggested == AspectMode::AnalogWide) if (suggested == AspectMode::Analog || suggested == AspectMode::AnalogWide)
{ {

View File

@ -188,6 +188,11 @@ public:
virtual std::unique_ptr<VideoCommon::AsyncShaderCompiler> CreateAsyncShaderCompiler(); virtual std::unique_ptr<VideoCommon::AsyncShaderCompiler> CreateAsyncShaderCompiler();
// Returns a lock for the ImGui mutex, enabling data structures to be modified from outside.
// Use with care, only non-drawing functions should be called from outside the video thread,
// as the drawing is tied to a "frame".
std::unique_lock<std::mutex> GetImGuiLock();
protected: protected:
std::tuple<int, int> CalculateTargetScale(int x, int y) const; std::tuple<int, int> CalculateTargetScale(int x, int y) const;
bool CalculateTargetSize(); bool CalculateTargetSize();
@ -243,6 +248,7 @@ protected:
std::unique_ptr<NativeVertexFormat> m_imgui_vertex_format; std::unique_ptr<NativeVertexFormat> m_imgui_vertex_format;
std::vector<std::unique_ptr<AbstractTexture>> m_imgui_textures; std::vector<std::unique_ptr<AbstractTexture>> m_imgui_textures;
std::unique_ptr<AbstractPipeline> m_imgui_pipeline; std::unique_ptr<AbstractPipeline> m_imgui_pipeline;
std::mutex m_imgui_mutex;
u64 m_imgui_last_frame_time; u64 m_imgui_last_frame_time;
private: private: