mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-06 19:45:33 +00:00
ImGuiOverlays: Double buffer input OSD and add vibration
This commit is contained in:
parent
dd7fd32501
commit
f6c7681ef0
@ -56,6 +56,31 @@ namespace ImGuiManager {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
struct InputOverlayState
|
||||||
|
{
|
||||||
|
static constexpr u32 MAX_BINDS = 32;
|
||||||
|
|
||||||
|
struct PadState
|
||||||
|
{
|
||||||
|
ControllerType ctype;
|
||||||
|
u8 port;
|
||||||
|
u8 slot;
|
||||||
|
bool multitap;
|
||||||
|
u32 icon_color;
|
||||||
|
float vibration_state[InputManager::MAX_MOTORS_PER_PAD];
|
||||||
|
float bind_state[MAX_BINDS];
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<PadState, NUM_CONTROLLER_AND_CARD_PORTS> pads;
|
||||||
|
u32 num_active_pads = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InputOverlayStateUpdateBuffer
|
||||||
|
{
|
||||||
|
u32 num_active_pads = 0;
|
||||||
|
InputOverlayState::PadState pads[0];
|
||||||
|
};
|
||||||
|
|
||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
|
|
||||||
struct DebugWindowInfo
|
struct DebugWindowInfo
|
||||||
@ -79,6 +104,7 @@ static void DrawMediaCaptureOverlay(float& position_y, float scale, float margin
|
|||||||
static void DrawFrameTimeOverlay(float& position_y, float scale, float margin, float spacing);
|
static void DrawFrameTimeOverlay(float& position_y, float scale, float margin, float spacing);
|
||||||
static void DrawEnhancementsOverlay(const GPUBackend* gpu);
|
static void DrawEnhancementsOverlay(const GPUBackend* gpu);
|
||||||
static void DrawInputsOverlay();
|
static void DrawInputsOverlay();
|
||||||
|
static void UpdateInputOverlay(void* buffer);
|
||||||
|
|
||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
|
|
||||||
@ -96,6 +122,9 @@ static constexpr const std::array<DebugWindowInfo, NUM_DEBUG_WINDOWS> s_debug_wi
|
|||||||
static std::array<ImGuiManager::AuxiliaryRenderWindowState, NUM_DEBUG_WINDOWS> s_debug_window_state = {};
|
static std::array<ImGuiManager::AuxiliaryRenderWindowState, NUM_DEBUG_WINDOWS> s_debug_window_state = {};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static InputOverlayState s_input_overlay_state = {};
|
||||||
|
|
||||||
} // namespace ImGuiManager
|
} // namespace ImGuiManager
|
||||||
|
|
||||||
static std::tuple<float, float> GetMinMax(std::span<const float> values)
|
static std::tuple<float, float> GetMinMax(std::span<const float> values)
|
||||||
@ -648,6 +677,70 @@ void ImGuiManager::DrawFrameTimeOverlay(float& position_y, float scale, float ma
|
|||||||
position_y += history_size.y + spacing;
|
position_y += history_size.y + spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImGuiManager::UpdateInputOverlay()
|
||||||
|
{
|
||||||
|
u32 num_active_pads = 0;
|
||||||
|
for (u32 port = 0; port < NUM_CONTROLLER_AND_CARD_PORTS; port++)
|
||||||
|
{
|
||||||
|
if (g_settings.controller_types[port] != ControllerType::None)
|
||||||
|
num_active_pads++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 buffer_size =
|
||||||
|
sizeof(InputOverlayStateUpdateBuffer) + (static_cast<u32>(sizeof(InputOverlayState)) * num_active_pads);
|
||||||
|
const auto& [cmd, buffer] = GPUThread::BeginASyncBufferCall(&ImGuiManager::UpdateInputOverlay, buffer_size);
|
||||||
|
|
||||||
|
InputOverlayStateUpdateBuffer* const ubuffer = static_cast<InputOverlayStateUpdateBuffer*>(buffer);
|
||||||
|
ubuffer->num_active_pads = num_active_pads;
|
||||||
|
|
||||||
|
size_t out_index = 0;
|
||||||
|
for (const u32 pad : Controller::PortDisplayOrder)
|
||||||
|
{
|
||||||
|
const Controller* controller = System::GetController(pad);
|
||||||
|
if (!controller)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const ControllerType ctype = controller->GetType();
|
||||||
|
const auto& [port, slot] = Controller::ConvertPadToPortAndSlot(pad);
|
||||||
|
const bool multitap = g_settings.IsMultitapPortEnabled(port);
|
||||||
|
InputOverlayState::PadState& pstate = ubuffer->pads[out_index++];
|
||||||
|
pstate.port = Truncate8(port);
|
||||||
|
pstate.slot = Truncate8(slot);
|
||||||
|
pstate.multitap = multitap;
|
||||||
|
pstate.ctype = ctype;
|
||||||
|
pstate.icon_color = controller->GetInputOverlayIconColor();
|
||||||
|
|
||||||
|
const Controller::ControllerInfo& cinfo = Controller::GetControllerInfo(ctype);
|
||||||
|
for (const Controller::ControllerBindingInfo& bi : cinfo.bindings)
|
||||||
|
{
|
||||||
|
const u32 bidx = bi.bind_index;
|
||||||
|
|
||||||
|
// this will leave some uninitalized, but who cares, it won't be read on the other side
|
||||||
|
if (bi.type >= InputBindingInfo::Type::Button && bi.type <= InputBindingInfo::Type::HalfAxis)
|
||||||
|
{
|
||||||
|
DebugAssert(bidx < InputOverlayState::MAX_BINDS);
|
||||||
|
pstate.bind_state[bidx] = controller->GetBindState(bidx);
|
||||||
|
}
|
||||||
|
else if (bi.type == InputBindingInfo::Type::Motor)
|
||||||
|
{
|
||||||
|
DebugAssert(bidx < InputManager::MAX_MOTORS_PER_PAD);
|
||||||
|
pstate.vibration_state[bidx] = controller->GetVibrationMotorState(bidx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUThread::PushCommand(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiManager::UpdateInputOverlay(void* buffer)
|
||||||
|
{
|
||||||
|
InputOverlayStateUpdateBuffer* const RESTRICT ubuffer = static_cast<InputOverlayStateUpdateBuffer*>(buffer);
|
||||||
|
DebugAssert(ubuffer->num_active_pads < NUM_CONTROLLER_AND_CARD_PORTS);
|
||||||
|
s_input_overlay_state.num_active_pads = ubuffer->num_active_pads;
|
||||||
|
for (u32 i = 0; i < ubuffer->num_active_pads; i++)
|
||||||
|
s_input_overlay_state.pads[i] = ubuffer->pads[i];
|
||||||
|
}
|
||||||
|
|
||||||
void ImGuiManager::DrawInputsOverlay()
|
void ImGuiManager::DrawInputsOverlay()
|
||||||
{
|
{
|
||||||
const float scale = ImGuiManager::GetGlobalScale();
|
const float scale = ImGuiManager::GetGlobalScale();
|
||||||
@ -662,16 +755,10 @@ void ImGuiManager::DrawInputsOverlay()
|
|||||||
const ImVec2& display_size = ImGui::GetIO().DisplaySize;
|
const ImVec2& display_size = ImGui::GetIO().DisplaySize;
|
||||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||||
|
|
||||||
u32 num_ports = 0;
|
|
||||||
for (u32 port = 0; port < NUM_CONTROLLER_AND_CARD_PORTS; port++)
|
|
||||||
{
|
|
||||||
if (g_settings.controller_types[port] != ControllerType::None)
|
|
||||||
num_ports++;
|
|
||||||
}
|
|
||||||
|
|
||||||
float current_x = ImFloor(margin);
|
float current_x = ImFloor(margin);
|
||||||
float current_y =
|
float current_y =
|
||||||
ImFloor(display_size.y - margin - ((static_cast<float>(num_ports) * (font->FontSize + spacing)) - spacing));
|
ImFloor(display_size.y - margin -
|
||||||
|
((static_cast<float>(s_input_overlay_state.num_active_pads) * (font->FontSize + spacing)) - spacing));
|
||||||
|
|
||||||
// This is a bit of a pain. Some of the glyphs slightly overhang/overshoot past the baseline, resulting
|
// This is a bit of a pain. Some of the glyphs slightly overhang/overshoot past the baseline, resulting
|
||||||
// in the glyphs getting clipped if we use the text height/margin as a clip point. Instead, just clamp it
|
// in the glyphs getting clipped if we use the text height/margin as a clip point. Instead, just clamp it
|
||||||
@ -680,24 +767,19 @@ void ImGuiManager::DrawInputsOverlay()
|
|||||||
|
|
||||||
SmallString text;
|
SmallString text;
|
||||||
|
|
||||||
for (const u32 pad : Controller::PortDisplayOrder)
|
for (u32 i = 0; i < s_input_overlay_state.num_active_pads; i++)
|
||||||
{
|
{
|
||||||
const Controller* controller = System::GetController(pad);
|
const InputOverlayState::PadState& pstate = s_input_overlay_state.pads[i];
|
||||||
if (!controller)
|
const Controller::ControllerInfo& cinfo = Controller::GetControllerInfo(pstate.ctype);
|
||||||
continue;
|
const char* port_label = Controller::GetPortDisplayName(pstate.port, pstate.slot, pstate.multitap);
|
||||||
|
|
||||||
const Controller::ControllerInfo& cinfo = Controller::GetControllerInfo(controller->GetType());
|
|
||||||
const auto& [port, slot] = Controller::ConvertPadToPortAndSlot(pad);
|
|
||||||
const char* port_label = Controller::GetPortDisplayName(port, slot, g_settings.IsMultitapPortEnabled(port));
|
|
||||||
|
|
||||||
float text_start_x = current_x;
|
float text_start_x = current_x;
|
||||||
if (cinfo.icon_name)
|
if (cinfo.icon_name)
|
||||||
{
|
{
|
||||||
const ImVec2 icon_size = font->CalcTextSizeA(font->FontSize, FLT_MAX, 0.0f, cinfo.icon_name);
|
const ImVec2 icon_size = font->CalcTextSizeA(font->FontSize, FLT_MAX, 0.0f, cinfo.icon_name);
|
||||||
const u32 icon_color = controller->GetInputOverlayIconColor();
|
|
||||||
dl->AddText(font, font->FontSize, ImVec2(current_x + shadow_offset, current_y + shadow_offset), shadow_color,
|
dl->AddText(font, font->FontSize, ImVec2(current_x + shadow_offset, current_y + shadow_offset), shadow_color,
|
||||||
cinfo.icon_name, nullptr, 0.0f, &clip_rect);
|
cinfo.icon_name, nullptr, 0.0f, &clip_rect);
|
||||||
dl->AddText(font, font->FontSize, ImVec2(current_x, current_y), icon_color, cinfo.icon_name, nullptr, 0.0f,
|
dl->AddText(font, font->FontSize, ImVec2(current_x, current_y), pstate.icon_color, cinfo.icon_name, nullptr, 0.0f,
|
||||||
&clip_rect);
|
&clip_rect);
|
||||||
text_start_x += icon_size.x;
|
text_start_x += icon_size.x;
|
||||||
text.format(" {}", port_label);
|
text.format(" {}", port_label);
|
||||||
@ -709,36 +791,23 @@ void ImGuiManager::DrawInputsOverlay()
|
|||||||
|
|
||||||
for (const Controller::ControllerBindingInfo& bi : cinfo.bindings)
|
for (const Controller::ControllerBindingInfo& bi : cinfo.bindings)
|
||||||
{
|
{
|
||||||
switch (bi.type)
|
if (bi.type >= InputBindingInfo::Type::Button && bi.type <= InputBindingInfo::Type::HalfAxis)
|
||||||
{
|
{
|
||||||
case InputBindingInfo::Type::Axis:
|
// axes are always shown when not near resting, buttons only shown when active
|
||||||
case InputBindingInfo::Type::HalfAxis:
|
const float value = pstate.bind_state[bi.bind_index];
|
||||||
{
|
const float threshold = (bi.type == InputBindingInfo::Type::Button) ? 0.5f : (254.0f / 255.0f);
|
||||||
// axes are always shown
|
if (value >= threshold)
|
||||||
const float value = controller->GetBindState(bi.bind_index);
|
text.append_format(" {}", bi.icon_name ? bi.icon_name : bi.name);
|
||||||
if (value >= (254.0f / 255.0f))
|
else if (value > (1.0f / 255.0f))
|
||||||
text.append_format(" {}", bi.icon_name ? bi.icon_name : bi.name);
|
text.append_format(" {}: {:.2f}", bi.icon_name ? bi.icon_name : bi.name, value);
|
||||||
else if (value > (1.0f / 255.0f))
|
}
|
||||||
text.append_format(" {}: {:.2f}", bi.icon_name ? bi.icon_name : bi.name, value);
|
else if (bi.type == InputBindingInfo::Type::Motor)
|
||||||
}
|
{
|
||||||
break;
|
const float value = pstate.vibration_state[bi.bind_index];
|
||||||
|
if (value >= 1.0f)
|
||||||
case InputBindingInfo::Type::Button:
|
text.append_format(" {}", bi.icon_name ? bi.icon_name : bi.name);
|
||||||
{
|
else if (value > 0.0f)
|
||||||
// buttons only shown when active
|
text.append_format(" {}: {:.0f}%", bi.icon_name ? bi.icon_name : bi.name, std::ceil(value * 100.0f));
|
||||||
const float value = controller->GetBindState(bi.bind_index);
|
|
||||||
if (value >= 0.5f)
|
|
||||||
text.append_format(" {}", bi.icon_name ? bi.icon_name : bi.name);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case InputBindingInfo::Type::Motor:
|
|
||||||
case InputBindingInfo::Type::Macro:
|
|
||||||
case InputBindingInfo::Type::Unknown:
|
|
||||||
case InputBindingInfo::Type::Pointer:
|
|
||||||
case InputBindingInfo::Type::RelativePointer:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ namespace ImGuiManager {
|
|||||||
|
|
||||||
static constexpr const char* LOGO_IMAGE_NAME = "images/duck.png";
|
static constexpr const char* LOGO_IMAGE_NAME = "images/duck.png";
|
||||||
|
|
||||||
|
void UpdateInputOverlay();
|
||||||
void RenderTextOverlays(const GPUBackend* gpu);
|
void RenderTextOverlays(const GPUBackend* gpu);
|
||||||
bool AreAnyDebugWindowsEnabled(const SettingsInterface& si);
|
bool AreAnyDebugWindowsEnabled(const SettingsInterface& si);
|
||||||
void RenderDebugWindows();
|
void RenderDebugWindows();
|
||||||
|
@ -2163,6 +2163,10 @@ void System::FrameDone()
|
|||||||
InputManager::PollSources();
|
InputManager::PollSources();
|
||||||
CheckForAndExitExecution();
|
CheckForAndExitExecution();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update input OSD if we're running
|
||||||
|
if (g_settings.display_show_inputs)
|
||||||
|
ImGuiManager::UpdateInputOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::GetFramePresentationParameters(GPUBackendFramePresentationParameters* frame)
|
bool System::GetFramePresentationParameters(GPUBackendFramePresentationParameters* frame)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user