GPUDevice: Move software cursor to ImGuiManager
This commit is contained in:
parent
dd01c75f26
commit
259193a8e5
|
@ -69,7 +69,7 @@ std::optional<u32> Controller::GetAnalogInputBytes() const
|
|||
|
||||
void Controller::LoadSettings(SettingsInterface& si, const char* section) {}
|
||||
|
||||
bool Controller::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
|
||||
bool Controller::GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ public:
|
|||
virtual void LoadSettings(SettingsInterface& si, const char* section);
|
||||
|
||||
/// Returns the software cursor to use for this controller, if any.
|
||||
virtual bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode);
|
||||
virtual bool GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode);
|
||||
|
||||
/// Creates a new controller of the specified type.
|
||||
static std::unique_ptr<Controller> Create(ControllerType type, u32 index);
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "guncon.h"
|
||||
#include "gpu.h"
|
||||
#include "host.h"
|
||||
#include "resources.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "util/gpu_device.h"
|
||||
|
@ -12,6 +11,7 @@
|
|||
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
|
@ -19,7 +19,9 @@ Log_SetChannel(GunCon);
|
|||
|
||||
static constexpr std::array<u8, static_cast<size_t>(GunCon::Button::Count)> s_button_indices = {{13, 3, 14}};
|
||||
|
||||
GunCon::GunCon(u32 index) : Controller(index) {}
|
||||
GunCon::GunCon(u32 index) : Controller(index)
|
||||
{
|
||||
}
|
||||
|
||||
GunCon::~GunCon() = default;
|
||||
|
||||
|
@ -248,22 +250,10 @@ void GunCon::LoadSettings(SettingsInterface& si, const char* section)
|
|||
{
|
||||
Controller::LoadSettings(si, section);
|
||||
|
||||
std::string path = si.GetStringValue(section, "CrosshairImagePath");
|
||||
if (path != m_crosshair_image_path)
|
||||
{
|
||||
m_crosshair_image_path = std::move(path);
|
||||
if (m_crosshair_image_path.empty() || !m_crosshair_image.LoadFromFile(m_crosshair_image_path.c_str()))
|
||||
{
|
||||
m_crosshair_image.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
m_crosshair_image_path = si.GetStringValue(section, "CrosshairImagePath");
|
||||
#ifndef __ANDROID__
|
||||
if (!m_crosshair_image.IsValid())
|
||||
{
|
||||
m_crosshair_image.SetPixels(Resources::CROSSHAIR_IMAGE_WIDTH, Resources::CROSSHAIR_IMAGE_HEIGHT,
|
||||
Resources::CROSSHAIR_IMAGE_DATA.data());
|
||||
}
|
||||
if (m_crosshair_image_path.empty())
|
||||
m_crosshair_image_path = Path::Combine(EmuFolders::Resources, "images/crosshair.png");
|
||||
#endif
|
||||
|
||||
m_crosshair_image_scale = si.GetFloatValue(section, "CrosshairScale", 1.0f);
|
||||
|
@ -271,12 +261,12 @@ void GunCon::LoadSettings(SettingsInterface& si, const char* section)
|
|||
m_x_scale = si.GetFloatValue(section, "XScale", 1.0f);
|
||||
}
|
||||
|
||||
bool GunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
|
||||
bool GunCon::GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode)
|
||||
{
|
||||
if (!m_crosshair_image.IsValid())
|
||||
if (m_crosshair_image_path.empty())
|
||||
return false;
|
||||
|
||||
*image = &m_crosshair_image;
|
||||
*image_path = m_crosshair_image_path;
|
||||
*image_scale = m_crosshair_image_scale;
|
||||
*relative_mode = false;
|
||||
return true;
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
bool DoState(StateWrapper& sw, bool apply_input_state) override;
|
||||
|
||||
void LoadSettings(SettingsInterface& si, const char* section) override;
|
||||
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) override;
|
||||
bool GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode) override;
|
||||
|
||||
float GetBindState(u32 index) const override;
|
||||
void SetBindState(u32 index, float value) override;
|
||||
|
@ -56,7 +56,6 @@ private:
|
|||
YMSB
|
||||
};
|
||||
|
||||
Common::RGBA8Image m_crosshair_image;
|
||||
std::string m_crosshair_image_path;
|
||||
float m_crosshair_image_scale = 1.0f;
|
||||
float m_x_scale = 1.0f;
|
||||
|
|
|
@ -349,6 +349,7 @@ void Host::RenderDisplay(bool skip_present)
|
|||
FullscreenUI::Render();
|
||||
ImGuiManager::RenderTextOverlays();
|
||||
ImGuiManager::RenderOSDMessages();
|
||||
ImGuiManager::RenderSoftwareCursors();
|
||||
}
|
||||
|
||||
// Debug windows are always rendered, otherwise mouse input breaks on skip.
|
||||
|
|
|
@ -215,7 +215,7 @@ void PlayStationMouse::LoadSettings(SettingsInterface& si, const char* section)
|
|||
m_use_relative_mode = si.GetBoolValue(section, "RelativeMouseMode", false);
|
||||
}
|
||||
|
||||
bool PlayStationMouse::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
|
||||
bool PlayStationMouse::GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode)
|
||||
{
|
||||
*relative_mode = m_use_relative_mode;
|
||||
return m_use_relative_mode;
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
bool Transfer(const u8 data_in, u8* data_out) override;
|
||||
|
||||
void LoadSettings(SettingsInterface& si, const char* section) override;
|
||||
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) override;
|
||||
bool GetSoftwareCursor(std::string* image_path, float* image_scale, bool* relative_mode) override;
|
||||
|
||||
private:
|
||||
void UpdatePosition();
|
||||
|
|
|
@ -4624,11 +4624,11 @@ void System::UpdateSoftwareCursor()
|
|||
if (!IsValid())
|
||||
{
|
||||
Host::SetMouseMode(false, false);
|
||||
g_gpu_device->ClearSoftwareCursor();
|
||||
ImGuiManager::ClearSoftwareCursor(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const Common::RGBA8Image* image = nullptr;
|
||||
std::string image_path;
|
||||
float image_scale = 1.0f;
|
||||
bool relative_mode = false;
|
||||
bool hide_cursor = false;
|
||||
|
@ -4636,7 +4636,7 @@ void System::UpdateSoftwareCursor()
|
|||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||
{
|
||||
Controller* controller = System::GetController(i);
|
||||
if (controller && controller->GetSoftwareCursor(&image, &image_scale, &relative_mode))
|
||||
if (controller && controller->GetSoftwareCursor(&image_path, &image_scale, &relative_mode))
|
||||
{
|
||||
hide_cursor = true;
|
||||
break;
|
||||
|
@ -4645,15 +4645,10 @@ void System::UpdateSoftwareCursor()
|
|||
|
||||
Host::SetMouseMode(relative_mode, hide_cursor);
|
||||
|
||||
if (image && image->IsValid())
|
||||
{
|
||||
g_gpu_device->SetSoftwareCursor(image->GetPixels(), image->GetWidth(), image->GetHeight(), image->GetPitch(),
|
||||
image_scale);
|
||||
}
|
||||
if (!image_path.empty())
|
||||
ImGuiManager::SetSoftwareCursor(0, std::move(image_path), image_scale);
|
||||
else
|
||||
{
|
||||
g_gpu_device->ClearSoftwareCursor();
|
||||
}
|
||||
ImGuiManager::ClearSoftwareCursor(0);
|
||||
}
|
||||
|
||||
void System::RequestDisplaySize(float scale /*= 0.0f*/)
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include "fmt/format.h"
|
||||
#include "imgui.h"
|
||||
#include "stb_image.h"
|
||||
#include "stb_image_resize.h"
|
||||
#include "stb_image_write.h"
|
||||
|
||||
|
@ -432,13 +431,10 @@ bool GPUDevice::CreateResources()
|
|||
std::unique_ptr<GPUShader> display_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateDisplayVertexShader());
|
||||
std::unique_ptr<GPUShader> display_fs =
|
||||
CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(true));
|
||||
std::unique_ptr<GPUShader> cursor_fs =
|
||||
CreateShader(GPUShaderStage::Fragment, shadergen.GenerateDisplayFragmentShader(false));
|
||||
if (!display_vs || !display_fs || !cursor_fs)
|
||||
if (!display_vs || !display_fs)
|
||||
return false;
|
||||
GL_OBJECT_NAME(display_vs, "Display Vertex Shader");
|
||||
GL_OBJECT_NAME(display_fs, "Display Fragment Shader");
|
||||
GL_OBJECT_NAME(cursor_fs, "Cursor Fragment Shader");
|
||||
|
||||
plconfig.vertex_shader = display_vs.get();
|
||||
plconfig.fragment_shader = display_fs.get();
|
||||
|
@ -446,12 +442,6 @@ bool GPUDevice::CreateResources()
|
|||
return false;
|
||||
GL_OBJECT_NAME(m_display_pipeline, "Display Pipeline");
|
||||
|
||||
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
|
||||
plconfig.fragment_shader = cursor_fs.get();
|
||||
if (!(m_cursor_pipeline = CreatePipeline(plconfig)))
|
||||
return false;
|
||||
GL_OBJECT_NAME(m_cursor_pipeline, "Cursor Pipeline");
|
||||
|
||||
std::unique_ptr<GPUShader> imgui_vs = CreateShader(GPUShaderStage::Vertex, shadergen.GenerateImGuiVertexShader());
|
||||
std::unique_ptr<GPUShader> imgui_fs = CreateShader(GPUShaderStage::Fragment, shadergen.GenerateImGuiFragmentShader());
|
||||
if (!imgui_vs || !imgui_fs)
|
||||
|
@ -472,6 +462,7 @@ bool GPUDevice::CreateResources()
|
|||
plconfig.input_layout.vertex_stride = sizeof(ImDrawVert);
|
||||
plconfig.vertex_shader = imgui_vs.get();
|
||||
plconfig.fragment_shader = imgui_fs.get();
|
||||
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
|
||||
|
||||
m_imgui_pipeline = CreatePipeline(plconfig);
|
||||
if (!m_imgui_pipeline)
|
||||
|
@ -486,12 +477,9 @@ bool GPUDevice::CreateResources()
|
|||
|
||||
void GPUDevice::DestroyResources()
|
||||
{
|
||||
m_cursor_texture.reset();
|
||||
|
||||
m_imgui_font_texture.reset();
|
||||
m_imgui_pipeline.reset();
|
||||
|
||||
m_cursor_pipeline.reset();
|
||||
m_display_pipeline.reset();
|
||||
m_imgui_pipeline.reset();
|
||||
|
||||
|
@ -862,61 +850,6 @@ float GPUDevice::GetAndResetAccumulatedGPUTime()
|
|||
return 0.0f;
|
||||
}
|
||||
|
||||
void GPUDevice::SetSoftwareCursor(std::unique_ptr<GPUTexture> texture, float scale /*= 1.0f*/)
|
||||
{
|
||||
if (texture)
|
||||
texture->MakeReadyForSampling();
|
||||
|
||||
m_cursor_texture = std::move(texture);
|
||||
m_cursor_texture_scale = scale;
|
||||
}
|
||||
|
||||
bool GPUDevice::SetSoftwareCursor(const void* pixels, u32 width, u32 height, u32 stride, float scale /*= 1.0f*/)
|
||||
{
|
||||
std::unique_ptr<GPUTexture> tex =
|
||||
CreateTexture(width, height, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8, pixels, stride, false);
|
||||
if (!tex)
|
||||
return false;
|
||||
|
||||
SetSoftwareCursor(std::move(tex), scale);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPUDevice::SetSoftwareCursor(const char* path, float scale /*= 1.0f*/)
|
||||
{
|
||||
auto fp = FileSystem::OpenManagedCFile(path, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int width, height, file_channels;
|
||||
u8* pixel_data = stbi_load_from_file(fp.get(), &width, &height, &file_channels, 4);
|
||||
if (!pixel_data)
|
||||
{
|
||||
const char* error_reason = stbi_failure_reason();
|
||||
Log_ErrorPrintf("Failed to load image from '%s': %s", path, error_reason ? error_reason : "unknown error");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<GPUTexture> tex =
|
||||
CreateTexture(static_cast<u32>(width), static_cast<u32>(height), 1, 1, 1, GPUTexture::Type::Texture,
|
||||
GPUTexture::Format::RGBA8, pixel_data, sizeof(u32) * static_cast<u32>(width), false);
|
||||
stbi_image_free(pixel_data);
|
||||
if (!tex)
|
||||
return false;
|
||||
|
||||
Log_InfoPrintf("Loaded %dx%d image from '%s' for software cursor", width, height, path);
|
||||
SetSoftwareCursor(std::move(tex), scale);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPUDevice::ClearSoftwareCursor()
|
||||
{
|
||||
m_cursor_texture.reset();
|
||||
m_cursor_texture_scale = 1.0f;
|
||||
}
|
||||
|
||||
bool GPUDevice::IsUsingLinearFiltering() const
|
||||
{
|
||||
return g_settings.display_linear_filtering;
|
||||
|
@ -962,7 +895,6 @@ bool GPUDevice::Render(bool skip_present)
|
|||
SetViewportAndScissor(0, 0, GetWindowWidth(), GetWindowHeight());
|
||||
|
||||
RenderImGui();
|
||||
RenderSoftwareCursor();
|
||||
|
||||
EndPresent();
|
||||
return true;
|
||||
|
@ -1054,27 +986,6 @@ bool GPUDevice::RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 wid
|
|||
}
|
||||
}
|
||||
|
||||
void GPUDevice::RenderSoftwareCursor()
|
||||
{
|
||||
if (!HasSoftwareCursor())
|
||||
return;
|
||||
|
||||
const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect();
|
||||
RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get());
|
||||
}
|
||||
|
||||
void GPUDevice::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture)
|
||||
{
|
||||
SetPipeline(m_display_pipeline.get());
|
||||
SetTextureSampler(0, texture, m_linear_sampler.get());
|
||||
|
||||
const float uniforms[4] = {0.0f, 0.0f, 1.0f, 1.0f};
|
||||
PushUniformBuffer(uniforms, sizeof(uniforms));
|
||||
|
||||
SetViewportAndScissor(left, top, width, height);
|
||||
Draw(3, 0);
|
||||
}
|
||||
|
||||
void GPUDevice::CalculateDrawRect(s32 window_width, s32 window_height, float* out_left, float* out_top,
|
||||
float* out_width, float* out_height, float* out_left_padding, float* out_top_padding,
|
||||
float* out_scale, float* out_x_scale, bool apply_aspect_ratio /* = true */) const
|
||||
|
@ -1193,25 +1104,6 @@ std::tuple<s32, s32, s32, s32> GPUDevice::CalculateDrawRect(s32 window_width, s3
|
|||
static_cast<s32>(width), static_cast<s32>(height));
|
||||
}
|
||||
|
||||
std::tuple<s32, s32, s32, s32> GPUDevice::CalculateSoftwareCursorDrawRect() const
|
||||
{
|
||||
return CalculateSoftwareCursorDrawRect(m_mouse_position_x, m_mouse_position_y);
|
||||
}
|
||||
|
||||
std::tuple<s32, s32, s32, s32> GPUDevice::CalculateSoftwareCursorDrawRect(s32 cursor_x, s32 cursor_y) const
|
||||
{
|
||||
const float scale = m_window_info.surface_scale * m_cursor_texture_scale;
|
||||
const u32 cursor_extents_x = static_cast<u32>(static_cast<float>(m_cursor_texture->GetWidth()) * scale * 0.5f);
|
||||
const u32 cursor_extents_y = static_cast<u32>(static_cast<float>(m_cursor_texture->GetHeight()) * scale * 0.5f);
|
||||
|
||||
const s32 out_left = cursor_x - cursor_extents_x;
|
||||
const s32 out_top = cursor_y - cursor_extents_y;
|
||||
const s32 out_width = cursor_extents_x * 2u;
|
||||
const s32 out_height = cursor_extents_y * 2u;
|
||||
|
||||
return std::tie(out_left, out_top, out_width, out_height);
|
||||
}
|
||||
|
||||
std::tuple<float, float> GPUDevice::ConvertWindowCoordinatesToDisplayCoordinates(s32 window_x, s32 window_y,
|
||||
s32 window_width,
|
||||
s32 window_height) const
|
||||
|
|
|
@ -638,18 +638,6 @@ public:
|
|||
/// Returns the amount of GPU time utilized since the last time this method was called.
|
||||
virtual float GetAndResetAccumulatedGPUTime();
|
||||
|
||||
/// Sets the software cursor to the specified texture. Ownership of the texture is transferred.
|
||||
void SetSoftwareCursor(std::unique_ptr<GPUTexture> texture, float scale = 1.0f);
|
||||
|
||||
/// Sets the software cursor to the specified image.
|
||||
bool SetSoftwareCursor(const void* pixels, u32 width, u32 height, u32 stride, float scale = 1.0f);
|
||||
|
||||
/// Sets the software cursor to the specified path (png image).
|
||||
bool SetSoftwareCursor(const char* path, float scale = 1.0f);
|
||||
|
||||
/// Disables the software cursor.
|
||||
void ClearSoftwareCursor();
|
||||
|
||||
/// Helper function for computing the draw rectangle in a larger window.
|
||||
std::tuple<s32, s32, s32, s32> CalculateDrawRect(s32 window_width, s32 window_height,
|
||||
bool apply_aspect_ratio = true) const;
|
||||
|
@ -705,7 +693,6 @@ protected:
|
|||
bool m_debug_device = false;
|
||||
|
||||
private:
|
||||
ALWAYS_INLINE bool HasSoftwareCursor() const { return static_cast<bool>(m_cursor_texture); }
|
||||
ALWAYS_INLINE bool HasDisplayTexture() const { return (m_display_texture != nullptr); }
|
||||
|
||||
void OpenShaderCache(const std::string_view& base_path, u32 version);
|
||||
|
@ -719,17 +706,11 @@ private:
|
|||
float* out_height, float* out_left_padding, float* out_top_padding, float* out_scale,
|
||||
float* out_x_scale, bool apply_aspect_ratio = true) const;
|
||||
|
||||
std::tuple<s32, s32, s32, s32> CalculateSoftwareCursorDrawRect() const;
|
||||
std::tuple<s32, s32, s32, s32> CalculateSoftwareCursorDrawRect(s32 cursor_x, s32 cursor_y) const;
|
||||
|
||||
void RenderImGui();
|
||||
|
||||
void RenderSoftwareCursor();
|
||||
|
||||
bool RenderDisplay(GPUFramebuffer* target, s32 left, s32 top, s32 width, s32 height, GPUTexture* texture,
|
||||
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
|
||||
bool linear_filter);
|
||||
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture);
|
||||
|
||||
u64 m_last_frame_displayed_time = 0;
|
||||
|
||||
|
@ -755,10 +736,6 @@ private:
|
|||
std::unique_ptr<GPUPipeline> m_imgui_pipeline;
|
||||
std::unique_ptr<GPUTexture> m_imgui_font_texture;
|
||||
|
||||
std::unique_ptr<GPUPipeline> m_cursor_pipeline;
|
||||
std::unique_ptr<GPUTexture> m_cursor_texture;
|
||||
float m_cursor_texture_scale = 1.0f;
|
||||
|
||||
bool m_display_changed = false;
|
||||
|
||||
std::unique_ptr<PostProcessingChain> m_post_processing_chain;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "common/assert.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/image.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/timer.h"
|
||||
|
@ -28,6 +29,17 @@
|
|||
Log_SetChannel(ImGuiManager);
|
||||
|
||||
namespace ImGuiManager {
|
||||
struct SoftwareCursor
|
||||
{
|
||||
std::string image_path;
|
||||
std::unique_ptr<GPUTexture> texture;
|
||||
u32 color;
|
||||
float scale;
|
||||
float extent_x;
|
||||
float extent_y;
|
||||
std::pair<float, float> pos;
|
||||
};
|
||||
|
||||
static void SetStyle();
|
||||
static void SetKeyMap();
|
||||
static bool LoadFontData();
|
||||
|
@ -37,6 +49,10 @@ static ImFont* AddFixedFont(float size);
|
|||
static bool AddIconFonts(float size);
|
||||
static void AcquirePendingOSDMessages();
|
||||
static void DrawOSDMessages();
|
||||
static void CreateSoftwareCursorTextures();
|
||||
static void UpdateSoftwareCursorTexture(u32 index);
|
||||
static void DestroySoftwareCursorTextures();
|
||||
static void DrawSoftwareCursor(const SoftwareCursor& sc, const std::pair<float, float>& pos);
|
||||
} // namespace ImGuiManager
|
||||
|
||||
static float s_global_prescale = 1.0f; // before window scale
|
||||
|
@ -63,6 +79,20 @@ static std::atomic_bool s_imgui_wants_mouse{false};
|
|||
// mapping of host key -> imgui key
|
||||
static std::unordered_map<u32, ImGuiKey> s_imgui_key_map;
|
||||
|
||||
struct OSDMessage
|
||||
{
|
||||
std::string key;
|
||||
std::string text;
|
||||
std::chrono::steady_clock::time_point time;
|
||||
float duration;
|
||||
};
|
||||
|
||||
static std::deque<OSDMessage> s_osd_active_messages;
|
||||
static std::deque<OSDMessage> s_osd_posted_messages;
|
||||
static std::mutex s_osd_messages_lock;
|
||||
|
||||
static std::array<ImGuiManager::SoftwareCursor, InputManager::MAX_SOFTWARE_CURSORS> s_software_cursors = {};
|
||||
|
||||
void ImGuiManager::SetFontPath(std::string path)
|
||||
{
|
||||
s_font_path = std::move(path);
|
||||
|
@ -125,11 +155,15 @@ bool ImGuiManager::Initialize(float global_scale)
|
|||
ImGui::GetIO().Fonts->ClearTexData();
|
||||
|
||||
NewFrame();
|
||||
|
||||
CreateSoftwareCursorTextures();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiManager::Shutdown()
|
||||
{
|
||||
DestroySoftwareCursorTextures();
|
||||
|
||||
if (ImGui::GetCurrentContext())
|
||||
ImGui::DestroyContext();
|
||||
|
||||
|
@ -541,18 +575,6 @@ bool ImGuiManager::HasFullscreenFonts()
|
|||
return (s_medium_font && s_large_font);
|
||||
}
|
||||
|
||||
struct OSDMessage
|
||||
{
|
||||
std::string key;
|
||||
std::string text;
|
||||
std::chrono::steady_clock::time_point time;
|
||||
float duration;
|
||||
};
|
||||
|
||||
static std::deque<OSDMessage> s_osd_active_messages;
|
||||
static std::deque<OSDMessage> s_osd_posted_messages;
|
||||
static std::mutex s_osd_messages_lock;
|
||||
|
||||
void Host::AddOSDMessage(std::string message, float duration /*= 2.0f*/)
|
||||
{
|
||||
AddKeyedOSDMessage(std::string(), std::move(message), duration);
|
||||
|
@ -634,9 +656,9 @@ void ImGuiManager::AcquirePendingOSDMessages()
|
|||
OSDMessage& new_msg = s_osd_posted_messages.front();
|
||||
std::deque<OSDMessage>::iterator iter;
|
||||
if (!new_msg.key.empty() && (iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(),
|
||||
[&new_msg](const OSDMessage& other) {
|
||||
return new_msg.key == other.key;
|
||||
})) != s_osd_active_messages.end())
|
||||
[&new_msg](const OSDMessage& other) {
|
||||
return new_msg.key == other.key;
|
||||
})) != s_osd_active_messages.end())
|
||||
{
|
||||
iter->text = std::move(new_msg.text);
|
||||
iter->duration = new_msg.duration;
|
||||
|
@ -842,3 +864,110 @@ bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, float value
|
|||
ImGui::GetIO().AddKeyAnalogEvent(key_map[static_cast<u32>(key)], (value > 0.0f), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiManager::CreateSoftwareCursorTextures()
|
||||
{
|
||||
for (u32 i = 0; i < InputManager::MAX_POINTER_DEVICES; i++)
|
||||
{
|
||||
if (!s_software_cursors[i].image_path.empty())
|
||||
UpdateSoftwareCursorTexture(i);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiManager::DestroySoftwareCursorTextures()
|
||||
{
|
||||
for (u32 i = 0; i < InputManager::MAX_POINTER_DEVICES; i++)
|
||||
{
|
||||
s_software_cursors[i].texture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiManager::UpdateSoftwareCursorTexture(u32 index)
|
||||
{
|
||||
SoftwareCursor& sc = s_software_cursors[index];
|
||||
if (sc.image_path.empty())
|
||||
{
|
||||
sc.texture.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
Common::RGBA8Image image;
|
||||
if (!image.LoadFromFile(sc.image_path.c_str()))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to load software cursor %u image '%s'", index, sc.image_path.c_str());
|
||||
return;
|
||||
}
|
||||
sc.texture = g_gpu_device->CreateTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture,
|
||||
GPUTexture::Format::RGBA8, image.GetPixels(), image.GetPitch());
|
||||
if (!sc.texture)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to upload %ux%u software cursor %u image '%s'", image.GetWidth(), image.GetHeight(), index,
|
||||
sc.image_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
sc.extent_x = std::ceil(static_cast<float>(image.GetWidth()) * sc.scale * s_global_scale) / 2.0f;
|
||||
sc.extent_y = std::ceil(static_cast<float>(image.GetHeight()) * sc.scale * s_global_scale) / 2.0f;
|
||||
}
|
||||
|
||||
void ImGuiManager::DrawSoftwareCursor(const SoftwareCursor& sc, const std::pair<float, float>& pos)
|
||||
{
|
||||
if (!sc.texture)
|
||||
return;
|
||||
|
||||
const ImVec2 min(pos.first - sc.extent_x, pos.second - sc.extent_y);
|
||||
const ImVec2 max(pos.first + sc.extent_x, pos.second + sc.extent_y);
|
||||
|
||||
ImDrawList* dl = ImGui::GetForegroundDrawList();
|
||||
|
||||
dl->AddImage(reinterpret_cast<ImTextureID>(sc.texture.get()), min, max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
|
||||
sc.color);
|
||||
}
|
||||
|
||||
void ImGuiManager::RenderSoftwareCursors()
|
||||
{
|
||||
// This one's okay to race, worst that happens is we render the wrong number of cursors for a frame.
|
||||
const u32 pointer_count = InputManager::MAX_POINTER_DEVICES;
|
||||
for (u32 i = 0; i < pointer_count; i++)
|
||||
DrawSoftwareCursor(s_software_cursors[i], InputManager::GetPointerAbsolutePosition(i));
|
||||
|
||||
for (u32 i = InputManager::MAX_POINTER_DEVICES; i < InputManager::MAX_SOFTWARE_CURSORS; i++)
|
||||
DrawSoftwareCursor(s_software_cursors[i], s_software_cursors[i].pos);
|
||||
}
|
||||
|
||||
void ImGuiManager::SetSoftwareCursor(u32 index, std::string image_path, float image_scale, u32 multiply_color)
|
||||
{
|
||||
DebugAssert(index < std::size(s_software_cursors));
|
||||
SoftwareCursor& sc = s_software_cursors[index];
|
||||
sc.color = multiply_color | 0xFF000000;
|
||||
if (sc.image_path == image_path && sc.scale == image_scale)
|
||||
return;
|
||||
|
||||
const bool is_hiding_or_showing = (image_path.empty() != sc.image_path.empty());
|
||||
sc.image_path = std::move(image_path);
|
||||
sc.scale = image_scale;
|
||||
if (g_gpu_device)
|
||||
UpdateSoftwareCursorTexture(index);
|
||||
|
||||
// Hide the system cursor when we activate a software cursor.
|
||||
if (is_hiding_or_showing && index == 0)
|
||||
InputManager::UpdateHostMouseMode();
|
||||
}
|
||||
|
||||
bool ImGuiManager::HasSoftwareCursor(u32 index)
|
||||
{
|
||||
return (index < s_software_cursors.size() && !s_software_cursors[index].image_path.empty());
|
||||
}
|
||||
|
||||
void ImGuiManager::ClearSoftwareCursor(u32 index)
|
||||
{
|
||||
SetSoftwareCursor(index, std::string(), 0.0f, 0);
|
||||
}
|
||||
|
||||
void ImGuiManager::SetSoftwareCursorPosition(u32 index, float pos_x, float pos_y)
|
||||
{
|
||||
DebugAssert(index >= InputManager::MAX_POINTER_DEVICES);
|
||||
SoftwareCursor& sc = s_software_cursors[index];
|
||||
sc.pos.first = pos_x;
|
||||
sc.pos.second = pos_y;
|
||||
}
|
||||
|
|
|
@ -85,6 +85,17 @@ bool ProcessHostKeyEvent(InputBindingKey key, float value);
|
|||
|
||||
/// Called on the CPU thread when any input event fires. Allows imgui to take over controller navigation.
|
||||
bool ProcessGenericInputEvent(GenericInputBinding key, float value);
|
||||
|
||||
/// Sets an image and scale for a software cursor. Software cursors can be used for things like crosshairs.
|
||||
void SetSoftwareCursor(u32 index, std::string image_path, float image_scale, u32 multiply_color = 0xFFFFFF);
|
||||
bool HasSoftwareCursor(u32 index);
|
||||
void ClearSoftwareCursor(u32 index);
|
||||
|
||||
/// Sets the position of a software cursor, used when we have relative coordinates such as controllers.
|
||||
void SetSoftwareCursorPosition(u32 index, float pos_x, float pos_y);
|
||||
|
||||
/// Adds software cursors to ImGui render list.
|
||||
void RenderSoftwareCursors();
|
||||
} // namespace ImGuiManager
|
||||
|
||||
namespace Host {
|
||||
|
|
|
@ -1026,6 +1026,12 @@ void InputManager::GenerateRelativeMouseEvents()
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<float, float> InputManager::GetPointerAbsolutePosition(u32 index)
|
||||
{
|
||||
return std::make_pair(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::X)],
|
||||
s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::Y)]);
|
||||
}
|
||||
|
||||
void InputManager::UpdatePointerAbsolutePosition(u32 index, float x, float y)
|
||||
{
|
||||
const float dx = x - std::exchange(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::X)], x);
|
||||
|
@ -1046,6 +1052,11 @@ void InputManager::UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis,
|
|||
std::memory_order_release);
|
||||
}
|
||||
|
||||
void InputManager::UpdateHostMouseMode()
|
||||
{
|
||||
// TODO: Move from System to here.
|
||||
}
|
||||
|
||||
bool InputManager::IsUsingRawInput()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
|
|
|
@ -174,6 +174,11 @@ static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
|
|||
|
||||
/// Maximum number of host mouse devices.
|
||||
static constexpr u32 MAX_POINTER_DEVICES = 1;
|
||||
static constexpr u32 MAX_POINTER_BUTTONS = 3;
|
||||
|
||||
/// Maximum number of software cursors. We allocate an extra two for controllers with
|
||||
/// positioning data from the controller instead of a mouse.
|
||||
static constexpr u32 MAX_SOFTWARE_CURSORS = MAX_POINTER_BUTTONS + 2;
|
||||
|
||||
/// Number of macro buttons per controller.
|
||||
static constexpr u32 NUM_MACRO_BUTTONS_PER_CONTROLLER = 4;
|
||||
|
@ -302,6 +307,9 @@ void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensi
|
|||
/// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes.
|
||||
void PauseVibration();
|
||||
|
||||
/// Reads absolute pointer position.
|
||||
std::pair<float, float> GetPointerAbsolutePosition(u32 index);
|
||||
|
||||
/// Updates absolute pointer position. Can call from UI thread, use when the host only reports absolute coordinates.
|
||||
void UpdatePointerAbsolutePosition(u32 index, float x, float y);
|
||||
|
||||
|
@ -309,6 +317,9 @@ void UpdatePointerAbsolutePosition(u32 index, float x, float y);
|
|||
/// reporting.
|
||||
void UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input = false);
|
||||
|
||||
/// Updates host mouse mode (relative/cursor hiding).
|
||||
void UpdateHostMouseMode();
|
||||
|
||||
/// Sets the state of the specified macro button.
|
||||
void SetMacroButtonState(u32 pad, u32 index, bool state);
|
||||
|
||||
|
|
Loading…
Reference in New Issue