mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
system: keep error dialogs open until dismissed
This commit is contained in:
@@ -1,42 +0,0 @@
|
|||||||
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
|
|
||||||
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
|
|
||||||
|
|
||||||
#include "common/common.h"
|
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
union SDL_Event;
|
|
||||||
|
|
||||||
namespace Libs::Graphics {
|
|
||||||
|
|
||||||
struct GraphicContext;
|
|
||||||
|
|
||||||
struct ImeVisualState {
|
|
||||||
bool active;
|
|
||||||
uint64_t revision;
|
|
||||||
};
|
|
||||||
|
|
||||||
void InitializeImeInput();
|
|
||||||
void ShutdownImeInput();
|
|
||||||
bool ProcessImeInput(const SDL_Event& event);
|
|
||||||
ImeVisualState GetImeVisualState() noexcept;
|
|
||||||
|
|
||||||
class ImeOverlay final {
|
|
||||||
public:
|
|
||||||
explicit ImeOverlay(GraphicContext& graphics);
|
|
||||||
~ImeOverlay();
|
|
||||||
KYTY_CLASS_NO_COPY(ImeOverlay);
|
|
||||||
|
|
||||||
[[nodiscard]] bool PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count);
|
|
||||||
void Record(vk::CommandBuffer command, vk::ImageView target);
|
|
||||||
void ReleaseVulkan();
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Impl;
|
|
||||||
std::unique_ptr<Impl> m_impl;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
|
||||||
|
|
||||||
#endif // EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
|
|
||||||
@@ -25,7 +25,7 @@ public:
|
|||||||
CommandBuffer* producer = nullptr);
|
CommandBuffer* producer = nullptr);
|
||||||
[[nodiscard]] Frame* PrepareLastFrame();
|
[[nodiscard]] Frame* PrepareLastFrame();
|
||||||
[[nodiscard]] bool IsGuestPaused() const noexcept;
|
[[nodiscard]] bool IsGuestPaused() const noexcept;
|
||||||
[[nodiscard]] bool NeedsImeRefresh() const noexcept;
|
[[nodiscard]] bool NeedsSystemOverlayRefresh() const noexcept;
|
||||||
[[nodiscard]] RenderContext& Renderer() const noexcept;
|
[[nodiscard]] RenderContext& Renderer() const noexcept;
|
||||||
void Present(Frame& frame, bool reuse = false);
|
void Present(Frame& frame, bool reuse = false);
|
||||||
void Discard(Frame& frame);
|
void Discard(Frame& frame);
|
||||||
|
|||||||
+161
-67
@@ -1,4 +1,4 @@
|
|||||||
#include "graphics/presentation/imeOverlay.h"
|
#include "graphics/presentation/systemOverlay.h"
|
||||||
|
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui_impl_vulkan.h"
|
#include "imgui_impl_vulkan.h"
|
||||||
#include "libs/controller.h"
|
#include "libs/controller.h"
|
||||||
|
#include "libs/dialog.h"
|
||||||
#include "libs/ime.h"
|
#include "libs/ime.h"
|
||||||
#include "libs/imeDialog.h"
|
#include "libs/imeDialog.h"
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ namespace {
|
|||||||
|
|
||||||
namespace CoreIme = Libs::Ime;
|
namespace CoreIme = Libs::Ime;
|
||||||
namespace DialogIme = Libs::Dialog::ImeDialog;
|
namespace DialogIme = Libs::Dialog::ImeDialog;
|
||||||
|
namespace ErrorDialog = Libs::Dialog::ErrorDialog;
|
||||||
|
|
||||||
namespace Ime {
|
namespace Ime {
|
||||||
|
|
||||||
@@ -101,6 +103,34 @@ bool HostQueueExternalInput(uint64_t generation, ExternalInput input) {
|
|||||||
|
|
||||||
} // namespace Ime
|
} // namespace Ime
|
||||||
|
|
||||||
|
enum class OverlayKind : uint8_t { None, Ime, Error };
|
||||||
|
|
||||||
|
struct OverlaySession {
|
||||||
|
OverlayKind kind = OverlayKind::None;
|
||||||
|
uint64_t generation = 0;
|
||||||
|
|
||||||
|
bool operator==(const OverlaySession&) const = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OverlaySnapshot {
|
||||||
|
OverlaySession session;
|
||||||
|
Ime::HostSnapshot ime;
|
||||||
|
ErrorDialog::HostSnapshot error;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GetOverlaySnapshot(OverlaySnapshot* snapshot) {
|
||||||
|
if (ErrorDialog::GetHostSnapshot(&snapshot->error)) {
|
||||||
|
snapshot->session = {OverlayKind::Error, snapshot->error.generation};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Ime::GetHostSnapshot(&snapshot->ime)) {
|
||||||
|
snapshot->session = {OverlayKind::Ime, snapshot->ime.generation};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
snapshot->session = {};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr size_t INPUT_QUEUE_CAPACITY = 128;
|
constexpr size_t INPUT_QUEUE_CAPACITY = 128;
|
||||||
|
|
||||||
enum class InputKind : uint8_t {
|
enum class InputKind : uint8_t {
|
||||||
@@ -113,19 +143,20 @@ enum class InputKind : uint8_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct InputEvent {
|
struct InputEvent {
|
||||||
InputKind kind;
|
InputKind kind;
|
||||||
uint64_t generation;
|
OverlaySession session;
|
||||||
int id;
|
int id;
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VisibilityUpdate {
|
struct VisibilityUpdate {
|
||||||
uint64_t generation;
|
OverlaySession session;
|
||||||
bool visible;
|
bool visible;
|
||||||
bool capture_controller;
|
bool capture_controller;
|
||||||
bool capture_keyboard;
|
bool capture_keyboard;
|
||||||
bool multiline;
|
bool text_input;
|
||||||
|
bool multiline;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::atomic<Uint32> g_visibility_event {static_cast<Uint32>(-1)};
|
std::atomic<Uint32> g_visibility_event {static_cast<Uint32>(-1)};
|
||||||
@@ -137,15 +168,14 @@ size_t g_missing_visibility_wakeups = 0;
|
|||||||
bool g_input_reset_requested = false;
|
bool g_input_reset_requested = false;
|
||||||
uint16_t g_last_external_keycode = 0;
|
uint16_t g_last_external_keycode = 0;
|
||||||
uint32_t g_last_external_status = 0;
|
uint32_t g_last_external_status = 0;
|
||||||
uint64_t g_input_generation = 0;
|
OverlaySession g_input_session;
|
||||||
bool g_input_active = false;
|
bool g_input_active = false;
|
||||||
bool g_input_controller = false;
|
bool g_input_controller = false;
|
||||||
bool g_input_keyboard = false;
|
bool g_input_keyboard = false;
|
||||||
bool g_input_multiline = false;
|
bool g_input_multiline = false;
|
||||||
bool g_input_lifecycle_active = false;
|
bool g_input_lifecycle_active = false;
|
||||||
bool g_active = false;
|
|
||||||
bool g_controller_captured = false;
|
bool g_controller_captured = false;
|
||||||
uint64_t g_generation = 0;
|
OverlaySession g_session;
|
||||||
|
|
||||||
void ClearInputEvents() {
|
void ClearInputEvents() {
|
||||||
std::scoped_lock lock(g_input_mutex);
|
std::scoped_lock lock(g_input_mutex);
|
||||||
@@ -159,7 +189,7 @@ void QueueInput(InputEvent event) {
|
|||||||
event.kind == InputKind::Axis || event.kind == InputKind::MousePosition;
|
event.kind == InputKind::Axis || event.kind == InputKind::MousePosition;
|
||||||
if (replaceable && !g_input_events.empty()) {
|
if (replaceable && !g_input_events.empty()) {
|
||||||
auto& last = g_input_events.back();
|
auto& last = g_input_events.back();
|
||||||
if (last.generation == event.generation && last.kind == event.kind && last.id == event.id) {
|
if (last.session == event.session && last.kind == event.kind && last.id == event.id) {
|
||||||
last = event;
|
last = event;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -192,34 +222,36 @@ void RefreshVisibility() {
|
|||||||
if (!g_input_lifecycle_active) {
|
if (!g_input_lifecycle_active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ime::HostSnapshot snapshot;
|
OverlaySnapshot snapshot;
|
||||||
const bool visible = Ime::GetHostSnapshot(&snapshot);
|
const bool visible = GetOverlaySnapshot(&snapshot);
|
||||||
const uint64_t generation = visible ? snapshot.generation : g_generation;
|
if (snapshot.session == g_session) {
|
||||||
if (visible == g_active && (!visible || generation == g_generation)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g_generation = generation;
|
g_session = snapshot.session;
|
||||||
bool capture_controller = false;
|
bool capture_controller = false;
|
||||||
bool capture_keyboard = false;
|
bool capture_keyboard = false;
|
||||||
|
bool text_input = false;
|
||||||
bool multiline = false;
|
bool multiline = false;
|
||||||
if (visible) {
|
if (snapshot.session.kind == OverlayKind::Error) {
|
||||||
capture_controller = (snapshot.disable_device & Ime::DISABLE_DEVICE_CONTROLLER) == 0;
|
capture_controller = true;
|
||||||
capture_keyboard = (snapshot.disable_device & Ime::DISABLE_DEVICE_EXT_KEYBOARD) == 0;
|
capture_keyboard = true;
|
||||||
multiline = (snapshot.option & Ime::OPTION_MULTILINE) != 0;
|
} else if (snapshot.session.kind == OverlayKind::Ime) {
|
||||||
|
capture_controller = (snapshot.ime.disable_device & Ime::DISABLE_DEVICE_CONTROLLER) == 0;
|
||||||
|
capture_keyboard = (snapshot.ime.disable_device & Ime::DISABLE_DEVICE_EXT_KEYBOARD) == 0;
|
||||||
|
text_input = capture_keyboard;
|
||||||
|
multiline = (snapshot.ime.option & Ime::OPTION_MULTILINE) != 0;
|
||||||
}
|
}
|
||||||
const bool was_controller = std::exchange(g_controller_captured, capture_controller);
|
const bool was_controller = std::exchange(g_controller_captured, capture_controller);
|
||||||
if (capture_controller || was_controller) {
|
if (capture_controller || was_controller) {
|
||||||
Controller::ResetInputState();
|
Controller::ResetInputState();
|
||||||
}
|
}
|
||||||
g_active = visible;
|
|
||||||
|
|
||||||
const Uint32 type = g_visibility_event.load(std::memory_order_acquire);
|
const Uint32 type = g_visibility_event.load(std::memory_order_acquire);
|
||||||
if (type != static_cast<Uint32>(-1)) {
|
if (type != static_cast<Uint32>(-1)) {
|
||||||
SDL_Event event {};
|
SDL_Event event {};
|
||||||
event.type = type;
|
event.type = type;
|
||||||
std::scoped_lock input_lock(g_input_mutex);
|
std::scoped_lock input_lock(g_input_mutex);
|
||||||
g_visibility_updates.push_back(
|
g_visibility_updates.push_back({snapshot.session, visible, capture_controller,
|
||||||
{generation, visible, capture_controller, capture_keyboard, multiline});
|
capture_keyboard, text_input, multiline});
|
||||||
if (g_missing_visibility_wakeups != 0 || SDL_PushEvent(&event) <= 0) {
|
if (g_missing_visibility_wakeups != 0 || SDL_PushEvent(&event) <= 0) {
|
||||||
g_missing_visibility_wakeups++;
|
g_missing_visibility_wakeups++;
|
||||||
}
|
}
|
||||||
@@ -383,7 +415,7 @@ void CheckVulkanResult(VkResult result) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void InitializeImeInput() {
|
void InitializeSystemOverlayInput() {
|
||||||
const Uint32 type = SDL_RegisterEvents(1);
|
const Uint32 type = SDL_RegisterEvents(1);
|
||||||
EXIT_IF(type == static_cast<Uint32>(-1));
|
EXIT_IF(type == static_cast<Uint32>(-1));
|
||||||
{
|
{
|
||||||
@@ -393,16 +425,18 @@ void InitializeImeInput() {
|
|||||||
}
|
}
|
||||||
CoreIme::SetVisibilityCallback(OnCoreVisibilityChanged);
|
CoreIme::SetVisibilityCallback(OnCoreVisibilityChanged);
|
||||||
DialogIme::SetVisibilityCallback(OnDialogVisibilityChanged);
|
DialogIme::SetVisibilityCallback(OnDialogVisibilityChanged);
|
||||||
|
ErrorDialog::SetVisibilityCallback(RefreshVisibility);
|
||||||
RefreshVisibility();
|
RefreshVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShutdownImeInput() {
|
void ShutdownSystemOverlayInput() {
|
||||||
CoreIme::SetVisibilityCallback(nullptr);
|
CoreIme::SetVisibilityCallback(nullptr);
|
||||||
DialogIme::SetVisibilityCallback(nullptr);
|
DialogIme::SetVisibilityCallback(nullptr);
|
||||||
|
ErrorDialog::SetVisibilityCallback(nullptr);
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(g_visibility_mutex);
|
std::scoped_lock lock(g_visibility_mutex);
|
||||||
g_input_lifecycle_active = false;
|
g_input_lifecycle_active = false;
|
||||||
g_active = false;
|
g_session = {};
|
||||||
if (std::exchange(g_controller_captured, false)) {
|
if (std::exchange(g_controller_captured, false)) {
|
||||||
Controller::ResetInputState();
|
Controller::ResetInputState();
|
||||||
}
|
}
|
||||||
@@ -415,6 +449,7 @@ void ShutdownImeInput() {
|
|||||||
g_missing_visibility_wakeups = 0;
|
g_missing_visibility_wakeups = 0;
|
||||||
}
|
}
|
||||||
g_input_active = false;
|
g_input_active = false;
|
||||||
|
g_input_session = {};
|
||||||
g_input_controller = false;
|
g_input_controller = false;
|
||||||
g_input_keyboard = false;
|
g_input_keyboard = false;
|
||||||
g_input_multiline = false;
|
g_input_multiline = false;
|
||||||
@@ -423,13 +458,15 @@ void ShutdownImeInput() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImeVisualState GetImeVisualState() noexcept {
|
SystemOverlayVisualState GetSystemOverlayVisualState() noexcept {
|
||||||
const auto core = CoreIme::GetVisualState();
|
const auto core = CoreIme::GetVisualState();
|
||||||
const auto dialog = DialogIme::GetVisualState();
|
const auto dialog = DialogIme::GetVisualState();
|
||||||
return {core.active || dialog.active, core.revision + dialog.revision};
|
const auto error = ErrorDialog::GetVisualState();
|
||||||
|
return {core.active || dialog.active || error.active,
|
||||||
|
core.revision + dialog.revision + error.revision};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProcessImeInput(const SDL_Event& event) {
|
bool ProcessSystemOverlayInput(const SDL_Event& event) {
|
||||||
RetryVisibilityWakeup();
|
RetryVisibilityWakeup();
|
||||||
if (event.type == g_visibility_event.load(std::memory_order_acquire)) {
|
if (event.type == g_visibility_event.load(std::memory_order_acquire)) {
|
||||||
VisibilityUpdate update {};
|
VisibilityUpdate update {};
|
||||||
@@ -444,12 +481,12 @@ bool ProcessImeInput(const SDL_Event& event) {
|
|||||||
ClearInputEvents();
|
ClearInputEvents();
|
||||||
g_last_external_keycode = 0;
|
g_last_external_keycode = 0;
|
||||||
g_last_external_status = 0;
|
g_last_external_status = 0;
|
||||||
g_input_generation = update.generation;
|
g_input_session = update.session;
|
||||||
g_input_active = update.visible;
|
g_input_active = update.visible;
|
||||||
g_input_controller = update.capture_controller;
|
g_input_controller = update.capture_controller;
|
||||||
g_input_keyboard = update.capture_keyboard;
|
g_input_keyboard = update.capture_keyboard;
|
||||||
g_input_multiline = update.multiline;
|
g_input_multiline = update.multiline;
|
||||||
if (g_input_keyboard) {
|
if (update.text_input) {
|
||||||
SDL_StartTextInput();
|
SDL_StartTextInput();
|
||||||
} else if (SDL_IsTextInputActive() == SDL_TRUE) {
|
} else if (SDL_IsTextInputActive() == SDL_TRUE) {
|
||||||
SDL_StopTextInput();
|
SDL_StopTextInput();
|
||||||
@@ -458,7 +495,7 @@ bool ProcessImeInput(const SDL_Event& event) {
|
|||||||
}
|
}
|
||||||
if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
|
if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
|
||||||
if (g_input_active && g_input_controller) {
|
if (g_input_active && g_input_controller) {
|
||||||
QueueInput({InputKind::ResetController, g_input_generation, 0, 0.0f, 0.0f});
|
QueueInput({InputKind::ResetController, g_input_session, 0, 0.0f, 0.0f});
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -466,7 +503,8 @@ bool ProcessImeInput(const SDL_Event& event) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t generation = g_input_generation;
|
const auto session = g_input_session;
|
||||||
|
const uint64_t generation = session.generation;
|
||||||
const bool controller_event = event.type == SDL_CONTROLLERBUTTONDOWN ||
|
const bool controller_event = event.type == SDL_CONTROLLERBUTTONDOWN ||
|
||||||
event.type == SDL_CONTROLLERBUTTONUP ||
|
event.type == SDL_CONTROLLERBUTTONUP ||
|
||||||
event.type == SDL_CONTROLLERAXISMOTION;
|
event.type == SDL_CONTROLLERAXISMOTION;
|
||||||
@@ -478,6 +516,13 @@ bool ProcessImeInput(const SDL_Event& event) {
|
|||||||
if (keyboard_event && !g_input_keyboard) {
|
if (keyboard_event && !g_input_keyboard) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (keyboard_event && session.kind == OverlayKind::Error) {
|
||||||
|
if (event.type == SDL_KEYDOWN && event.key.repeat == 0 &&
|
||||||
|
(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER)) {
|
||||||
|
ErrorDialog::HostAccept(generation);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_TEXTINPUT: {
|
case SDL_TEXTINPUT: {
|
||||||
const auto text = Utf8ToUtf16(event.text.text);
|
const auto text = Utf8ToUtf16(event.text.text);
|
||||||
@@ -524,31 +569,33 @@ bool ProcessImeInput(const SDL_Event& event) {
|
|||||||
case SDL_KEYUP: return true;
|
case SDL_KEYUP: return true;
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
case SDL_CONTROLLERBUTTONUP:
|
case SDL_CONTROLLERBUTTONUP:
|
||||||
QueueInput({InputKind::Button, generation, event.cbutton.button,
|
QueueInput({InputKind::Button, session, event.cbutton.button,
|
||||||
event.type == SDL_CONTROLLERBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
|
event.type == SDL_CONTROLLERBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
|
||||||
return true;
|
return true;
|
||||||
case SDL_CONTROLLERAXISMOTION:
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
QueueInput({InputKind::Axis, generation, event.caxis.axis,
|
QueueInput({InputKind::Axis, session, event.caxis.axis,
|
||||||
static_cast<float>(event.caxis.value), 0.0f});
|
static_cast<float>(event.caxis.value), 0.0f});
|
||||||
return true;
|
return true;
|
||||||
case SDL_MOUSEMOTION:
|
case SDL_MOUSEMOTION:
|
||||||
QueueInput({InputKind::MousePosition, generation, 0, static_cast<float>(event.motion.x),
|
QueueInput({InputKind::MousePosition, session, 0, static_cast<float>(event.motion.x),
|
||||||
static_cast<float>(event.motion.y)});
|
static_cast<float>(event.motion.y)});
|
||||||
return true;
|
return true;
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
case SDL_MOUSEBUTTONUP:
|
case SDL_MOUSEBUTTONUP:
|
||||||
QueueInput({InputKind::MouseButton, generation, event.button.button,
|
QueueInput({InputKind::MousePosition, session, 0, static_cast<float>(event.button.x),
|
||||||
|
static_cast<float>(event.button.y)});
|
||||||
|
QueueInput({InputKind::MouseButton, session, event.button.button,
|
||||||
event.type == SDL_MOUSEBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
|
event.type == SDL_MOUSEBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
|
||||||
return true;
|
return true;
|
||||||
case SDL_MOUSEWHEEL:
|
case SDL_MOUSEWHEEL:
|
||||||
QueueInput({InputKind::MouseWheel, generation, 0, static_cast<float>(event.wheel.x),
|
QueueInput({InputKind::MouseWheel, session, 0, static_cast<float>(event.wheel.x),
|
||||||
static_cast<float>(event.wheel.y)});
|
static_cast<float>(event.wheel.y)});
|
||||||
return true;
|
return true;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ImeOverlay::Impl {
|
struct SystemOverlay::Impl {
|
||||||
explicit Impl(GraphicContext& context): graphics(context) {}
|
explicit Impl(GraphicContext& context): graphics(context) {}
|
||||||
|
|
||||||
~Impl() {
|
~Impl() {
|
||||||
@@ -571,7 +618,7 @@ struct ImeOverlay::Impl {
|
|||||||
io.LogFilename = nullptr;
|
io.LogFilename = nullptr;
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||||
io.BackendPlatformName = "Kyty IME input";
|
io.BackendPlatformName = "Kyty system overlay input";
|
||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
auto& style = ImGui::GetStyle();
|
auto& style = ImGui::GetStyle();
|
||||||
style.WindowRounding = 10.0f;
|
style.WindowRounding = 10.0f;
|
||||||
@@ -609,7 +656,7 @@ struct ImeOverlay::Impl {
|
|||||||
vulkan_initialized = true;
|
vulkan_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrainInput(uint64_t generation) {
|
void DrainInput(OverlaySession session) {
|
||||||
std::deque<InputEvent> events;
|
std::deque<InputEvent> events;
|
||||||
bool reset = false;
|
bool reset = false;
|
||||||
{
|
{
|
||||||
@@ -626,16 +673,18 @@ struct ImeOverlay::Impl {
|
|||||||
right_stick = {};
|
right_stick = {};
|
||||||
}
|
}
|
||||||
for (const auto& event: events) {
|
for (const auto& event: events) {
|
||||||
if (event.generation != generation) {
|
if (event.session != session) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case InputKind::Button: {
|
case InputKind::Button: {
|
||||||
const bool down = event.x != 0.0f;
|
const bool down = event.x != 0.0f;
|
||||||
if (down && event.id == SDL_CONTROLLER_BUTTON_B) {
|
if (session.kind == OverlayKind::Ime && down) {
|
||||||
Ime::HostCancel(generation);
|
if (event.id == SDL_CONTROLLER_BUTTON_B) {
|
||||||
} else if (down && event.id == SDL_CONTROLLER_BUTTON_Y) {
|
Ime::HostCancel(session.generation);
|
||||||
Ime::HostBackspace(generation);
|
} else if (event.id == SDL_CONTROLLER_BUTTON_Y) {
|
||||||
|
Ime::HostBackspace(session.generation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const ImGuiKey key = ControllerButtonToKey(event.id);
|
const ImGuiKey key = ControllerButtonToKey(event.id);
|
||||||
if (key != ImGuiKey_None) {
|
if (key != ImGuiKey_None) {
|
||||||
@@ -732,7 +781,7 @@ struct ImeOverlay::Impl {
|
|||||||
focus_pending = false;
|
focus_pending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawDialog(const Ime::HostSnapshot& snapshot, vk::Extent2D extent) {
|
void DrawIme(const Ime::HostSnapshot& snapshot, vk::Extent2D extent) {
|
||||||
const ImVec2 display(static_cast<float>(extent.width), static_cast<float>(extent.height));
|
const ImVec2 display(static_cast<float>(extent.width), static_cast<float>(extent.height));
|
||||||
const bool over_2k = (snapshot.option & Ime::OPTION_USE_OVER_2K) != 0;
|
const bool over_2k = (snapshot.option & Ime::OPTION_USE_OVER_2K) != 0;
|
||||||
const float reference_width = over_2k ? 3840.0f : 1920.0f;
|
const float reference_width = over_2k ? 3840.0f : 1920.0f;
|
||||||
@@ -851,17 +900,58 @@ struct ImeOverlay::Impl {
|
|||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DrawError(const ErrorDialog::HostSnapshot& snapshot, vk::Extent2D extent) {
|
||||||
|
const ImVec2 display(static_cast<float>(extent.width), static_cast<float>(extent.height));
|
||||||
|
const float scale = std::max(std::min(display.x / 1280.0f, display.y / 720.0f), 0.5f);
|
||||||
|
ImGui::GetBackgroundDrawList()->AddRectFilled({0.0f, 0.0f}, display,
|
||||||
|
IM_COL32(0, 0, 0, 160));
|
||||||
|
ImGui::SetNextWindowPos({display.x * 0.5f, display.y * 0.5f}, ImGuiCond_Always,
|
||||||
|
{0.5f, 0.5f});
|
||||||
|
ImGui::SetNextWindowSize(
|
||||||
|
{std::max(std::min(620.0f * scale, display.x - 32.0f), 1.0f), 0.0f}, ImGuiCond_Always);
|
||||||
|
if (focus_pending) {
|
||||||
|
ImGui::SetNextWindowFocus();
|
||||||
|
}
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {24.0f * scale, 24.0f * scale});
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {12.0f * scale, 16.0f * scale});
|
||||||
|
ImGui::PushFont(nullptr, 20.0f * scale);
|
||||||
|
constexpr ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||||
|
ImGuiWindowFlags_NoSavedSettings |
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize;
|
||||||
|
ImGui::Begin("##SystemError", nullptr, flags);
|
||||||
|
ImGui::TextUnformatted("Error");
|
||||||
|
ImGui::Separator();
|
||||||
|
const auto error_code = static_cast<uint32_t>(snapshot.error_code);
|
||||||
|
ImGui::TextWrapped("%s", error_code == 0x80550006u
|
||||||
|
? "You are not signed in to PlayStation Network."
|
||||||
|
: "An error has occurred.");
|
||||||
|
ImGui::TextDisabled("Error code: 0x%08X", error_code);
|
||||||
|
const float button_width = std::min(140.0f * scale, ImGui::GetContentRegionAvail().x);
|
||||||
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - button_width) * 0.5f);
|
||||||
|
const bool accepted = ImGui::Button("OK", {button_width, 44.0f * scale});
|
||||||
|
if (focus_pending) {
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
focus_pending = false;
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::PopStyleVar(2);
|
||||||
|
if (accepted) {
|
||||||
|
ErrorDialog::HostAccept(snapshot.generation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool PrepareFrame(vk::Extent2D frame_extent, vk::Format format, uint32_t image_count) {
|
bool PrepareFrame(vk::Extent2D frame_extent, vk::Format format, uint32_t image_count) {
|
||||||
Ime::HostSnapshot snapshot;
|
OverlaySnapshot snapshot;
|
||||||
if (!Ime::GetHostSnapshot(&snapshot)) {
|
if (!GetOverlaySnapshot(&snapshot)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const uint64_t prepared_generation = snapshot.generation;
|
const auto prepared_session = snapshot.session;
|
||||||
EnsureVulkan(format, image_count);
|
EnsureVulkan(format, image_count);
|
||||||
if (generation != snapshot.generation) {
|
if (session != snapshot.session) {
|
||||||
generation = snapshot.generation;
|
session = snapshot.session;
|
||||||
focus_pending = true;
|
focus_pending = true;
|
||||||
shift = (snapshot.option & Ime::OPTION_NO_AUTO_CAPITALIZE) == 0;
|
shift = (snapshot.ime.option & Ime::OPTION_NO_AUTO_CAPITALIZE) == 0;
|
||||||
symbol_mode = false;
|
symbol_mode = false;
|
||||||
panel_offset = {};
|
panel_offset = {};
|
||||||
right_stick = {};
|
right_stick = {};
|
||||||
@@ -870,7 +960,7 @@ struct ImeOverlay::Impl {
|
|||||||
io.ClearInputKeys();
|
io.ClearInputKeys();
|
||||||
io.ClearInputMouse();
|
io.ClearInputMouse();
|
||||||
}
|
}
|
||||||
DrainInput(snapshot.generation);
|
DrainInput(snapshot.session);
|
||||||
|
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
io.DisplaySize = {static_cast<float>(frame_extent.width),
|
io.DisplaySize = {static_cast<float>(frame_extent.width),
|
||||||
@@ -883,11 +973,15 @@ struct ImeOverlay::Impl {
|
|||||||
last_frame = now;
|
last_frame = now;
|
||||||
ImGui_ImplVulkan_NewFrame();
|
ImGui_ImplVulkan_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
if (!Ime::GetHostSnapshot(&snapshot) || snapshot.generation != prepared_generation) {
|
if (!GetOverlaySnapshot(&snapshot) || snapshot.session != prepared_session) {
|
||||||
ImGui::EndFrame();
|
ImGui::EndFrame();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DrawDialog(snapshot, frame_extent);
|
if (snapshot.session.kind == OverlayKind::Error) {
|
||||||
|
DrawError(snapshot.error, frame_extent);
|
||||||
|
} else {
|
||||||
|
DrawIme(snapshot.ime, frame_extent);
|
||||||
|
}
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
extent = frame_extent;
|
extent = frame_extent;
|
||||||
return true;
|
return true;
|
||||||
@@ -934,24 +1028,24 @@ struct ImeOverlay::Impl {
|
|||||||
float button_height = 42.0f;
|
float button_height = 42.0f;
|
||||||
ImVec2 panel_offset {};
|
ImVec2 panel_offset {};
|
||||||
ImVec2 right_stick {};
|
ImVec2 right_stick {};
|
||||||
uint64_t generation = 0;
|
OverlaySession session;
|
||||||
vk::Extent2D extent {};
|
vk::Extent2D extent {};
|
||||||
std::chrono::steady_clock::time_point last_frame;
|
std::chrono::steady_clock::time_point last_frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
ImeOverlay::ImeOverlay(GraphicContext& graphics): m_impl(std::make_unique<Impl>(graphics)) {}
|
SystemOverlay::SystemOverlay(GraphicContext& graphics): m_impl(std::make_unique<Impl>(graphics)) {}
|
||||||
|
|
||||||
ImeOverlay::~ImeOverlay() = default;
|
SystemOverlay::~SystemOverlay() = default;
|
||||||
|
|
||||||
bool ImeOverlay::PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count) {
|
bool SystemOverlay::PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count) {
|
||||||
return m_impl->PrepareFrame(extent, format, image_count);
|
return m_impl->PrepareFrame(extent, format, image_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImeOverlay::Record(vk::CommandBuffer command, vk::ImageView target) {
|
void SystemOverlay::Record(vk::CommandBuffer command, vk::ImageView target) {
|
||||||
m_impl->Record(command, target);
|
m_impl->Record(command, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImeOverlay::ReleaseVulkan() {
|
void SystemOverlay::ReleaseVulkan() {
|
||||||
m_impl->ReleaseVulkan();
|
m_impl->ReleaseVulkan();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_SYSTEMOVERLAY_H_
|
||||||
|
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_SYSTEMOVERLAY_H_
|
||||||
|
|
||||||
|
#include "common/common.h"
|
||||||
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
union SDL_Event;
|
||||||
|
|
||||||
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
struct GraphicContext;
|
||||||
|
|
||||||
|
struct SystemOverlayVisualState {
|
||||||
|
bool active;
|
||||||
|
uint64_t revision;
|
||||||
|
};
|
||||||
|
|
||||||
|
void InitializeSystemOverlayInput();
|
||||||
|
void ShutdownSystemOverlayInput();
|
||||||
|
bool ProcessSystemOverlayInput(const SDL_Event& event);
|
||||||
|
SystemOverlayVisualState GetSystemOverlayVisualState() noexcept;
|
||||||
|
|
||||||
|
class SystemOverlay final {
|
||||||
|
public:
|
||||||
|
explicit SystemOverlay(GraphicContext& graphics);
|
||||||
|
~SystemOverlay();
|
||||||
|
KYTY_CLASS_NO_COPY(SystemOverlay);
|
||||||
|
|
||||||
|
[[nodiscard]] bool PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count);
|
||||||
|
void Record(vk::CommandBuffer command, vk::ImageView target);
|
||||||
|
void ReleaseVulkan();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Impl;
|
||||||
|
std::unique_ptr<Impl> m_impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Libs::Graphics
|
||||||
|
|
||||||
|
#endif // EMULATOR_SRC_GRAPHICS_PRESENTATION_SYSTEMOVERLAY_H_
|
||||||
@@ -809,7 +809,7 @@ void VideoOutDriver::Impl::PresentThread(std::stop_token token) {
|
|||||||
|
|
||||||
VblankBegin();
|
VblankBegin();
|
||||||
bool presented = m_flip_queue.Flip(0);
|
bool presented = m_flip_queue.Flip(0);
|
||||||
if (!presented && m_presenter.NeedsImeRefresh()) {
|
if (!presented && m_presenter.NeedsSystemOverlayRefresh()) {
|
||||||
if (auto* frame = m_presenter.PrepareLastFrame(); frame != nullptr) {
|
if (auto* frame = m_presenter.PrepareLastFrame(); frame != nullptr) {
|
||||||
m_presenter.Present(*frame, true);
|
m_presenter.Present(*frame, true);
|
||||||
presented = true;
|
presented = true;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
#include "graphics/host_gpu/vma.h"
|
#include "graphics/host_gpu/vma.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
#include "graphics/presentation/imeOverlay.h"
|
|
||||||
#include "graphics/presentation/presenter.h"
|
#include "graphics/presentation/presenter.h"
|
||||||
|
#include "graphics/presentation/systemOverlay.h"
|
||||||
#include "graphics/presentation/videoOut.h"
|
#include "graphics/presentation/videoOut.h"
|
||||||
#include "graphics/presentation/window/windowInternal.h"
|
#include "graphics/presentation/window/windowInternal.h"
|
||||||
|
|
||||||
@@ -293,8 +293,9 @@ public:
|
|||||||
void Create();
|
void Create();
|
||||||
void Recreate(bool surface_lost = false);
|
void Recreate(bool surface_lost = false);
|
||||||
[[nodiscard]] Status AcquireNextImage();
|
[[nodiscard]] Status AcquireNextImage();
|
||||||
[[nodiscard]] bool PrepareImeOverlay();
|
[[nodiscard]] bool PrepareSystemOverlay();
|
||||||
void RecordPresentCommands(CommandBuffer& command, VulkanImage& source, bool draw_ime_overlay);
|
void RecordPresentCommands(CommandBuffer& command, VulkanImage& source,
|
||||||
|
bool draw_system_overlay);
|
||||||
uint64_t Submit(CommandScheduler& scheduler);
|
uint64_t Submit(CommandScheduler& scheduler);
|
||||||
[[nodiscard]] Status Present();
|
[[nodiscard]] Status Present();
|
||||||
|
|
||||||
@@ -314,7 +315,7 @@ private:
|
|||||||
std::vector<vk::ImageView> m_image_views;
|
std::vector<vk::ImageView> m_image_views;
|
||||||
std::vector<vk::Semaphore> m_image_acquired;
|
std::vector<vk::Semaphore> m_image_acquired;
|
||||||
std::vector<vk::Semaphore> m_render_complete;
|
std::vector<vk::Semaphore> m_render_complete;
|
||||||
std::unique_ptr<ImeOverlay> m_ime_overlay;
|
std::unique_ptr<SystemOverlay> m_system_overlay;
|
||||||
uint32_t m_image_index = static_cast<uint32_t>(-1);
|
uint32_t m_image_index = static_cast<uint32_t>(-1);
|
||||||
uint32_t m_frame_index = 0;
|
uint32_t m_frame_index = 0;
|
||||||
};
|
};
|
||||||
@@ -361,7 +362,7 @@ struct Presenter::Impl {
|
|||||||
Swapchain swapchain;
|
Swapchain swapchain;
|
||||||
CommandScheduler present_scheduler;
|
CommandScheduler present_scheduler;
|
||||||
FramePool frames;
|
FramePool frames;
|
||||||
std::atomic<uint64_t> presented_ime_revision {0};
|
std::atomic<uint64_t> presented_overlay_revision {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
void Swapchain::Create() {
|
void Swapchain::Create() {
|
||||||
@@ -504,8 +505,8 @@ void Swapchain::Destroy() {
|
|||||||
Common::LockGuard queue_lock(graphics.queue_mutex);
|
Common::LockGuard queue_lock(graphics.queue_mutex);
|
||||||
RequireVulkanSuccess(graphics.queue.waitIdle(), "wait for swapchain queue");
|
RequireVulkanSuccess(graphics.queue.waitIdle(), "wait for swapchain queue");
|
||||||
}
|
}
|
||||||
if (m_ime_overlay != nullptr) {
|
if (m_system_overlay != nullptr) {
|
||||||
m_ime_overlay->ReleaseVulkan();
|
m_system_overlay->ReleaseVulkan();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto semaphore: m_image_acquired) {
|
for (const auto semaphore: m_image_acquired) {
|
||||||
@@ -579,15 +580,15 @@ Swapchain::Status Swapchain::AcquireNextImage() {
|
|||||||
return Status::Success;
|
return Status::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Swapchain::PrepareImeOverlay() {
|
bool Swapchain::PrepareSystemOverlay() {
|
||||||
if (m_ime_overlay == nullptr) {
|
if (m_system_overlay == nullptr) {
|
||||||
m_ime_overlay = std::make_unique<ImeOverlay>(m_window.graphic_ctx);
|
m_system_overlay = std::make_unique<SystemOverlay>(m_window.graphic_ctx);
|
||||||
}
|
}
|
||||||
return m_ime_overlay->PrepareFrame(m_extent, m_format, ImageCount());
|
return m_system_overlay->PrepareFrame(m_extent, m_format, ImageCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& source,
|
void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& source,
|
||||||
bool draw_ime_overlay) {
|
bool draw_system_overlay) {
|
||||||
if (source.state.layout != vk::ImageLayout::eTransferSrcOptimal) {
|
if (source.state.layout != vk::ImageLayout::eTransferSrcOptimal) {
|
||||||
EXIT("invalid prepared presentation image, vk_image=%p layout=%d\n",
|
EXIT("invalid prepared presentation image, vk_image=%p layout=%d\n",
|
||||||
static_cast<void*>(source.image), static_cast<int>(source.state.layout));
|
static_cast<void*>(source.image), static_cast<int>(source.state.layout));
|
||||||
@@ -635,12 +636,12 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& sourc
|
|||||||
vk::ImageMemoryBarrier to_present {};
|
vk::ImageMemoryBarrier to_present {};
|
||||||
to_present.sType = vk::StructureType::eImageMemoryBarrier;
|
to_present.sType = vk::StructureType::eImageMemoryBarrier;
|
||||||
to_present.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
to_present.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||||
to_present.dstAccessMask = draw_ime_overlay ? vk::AccessFlagBits::eColorAttachmentRead |
|
to_present.dstAccessMask = draw_system_overlay ? vk::AccessFlagBits::eColorAttachmentRead |
|
||||||
vk::AccessFlagBits::eColorAttachmentWrite
|
vk::AccessFlagBits::eColorAttachmentWrite
|
||||||
: vk::AccessFlagBits::eMemoryRead;
|
: vk::AccessFlagBits::eMemoryRead;
|
||||||
to_present.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
to_present.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||||
to_present.newLayout = draw_ime_overlay ? vk::ImageLayout::eColorAttachmentOptimal
|
to_present.newLayout = draw_system_overlay ? vk::ImageLayout::eColorAttachmentOptimal
|
||||||
: vk::ImageLayout::ePresentSrcKHR;
|
: vk::ImageLayout::ePresentSrcKHR;
|
||||||
to_present.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
to_present.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
to_present.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
to_present.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
to_present.image = m_images[m_image_index];
|
to_present.image = m_images[m_image_index];
|
||||||
@@ -649,13 +650,13 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& sourc
|
|||||||
to_present.subresourceRange.levelCount = 1;
|
to_present.subresourceRange.levelCount = 1;
|
||||||
to_present.subresourceRange.baseArrayLayer = 0;
|
to_present.subresourceRange.baseArrayLayer = 0;
|
||||||
to_present.subresourceRange.layerCount = 1;
|
to_present.subresourceRange.layerCount = 1;
|
||||||
vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
vk_command.pipelineBarrier(
|
||||||
draw_ime_overlay ? vk::PipelineStageFlagBits::eColorAttachmentOutput
|
vk::PipelineStageFlagBits::eTransfer,
|
||||||
: vk::PipelineStageFlagBits::eAllCommands,
|
draw_system_overlay ? vk::PipelineStageFlagBits::eColorAttachmentOutput
|
||||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1,
|
: vk::PipelineStageFlagBits::eAllCommands,
|
||||||
&to_present);
|
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1, &to_present);
|
||||||
if (draw_ime_overlay) {
|
if (draw_system_overlay) {
|
||||||
m_ime_overlay->Record(vk_command, m_image_views[m_image_index]);
|
m_system_overlay->Record(vk_command, m_image_views[m_image_index]);
|
||||||
to_present.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
|
to_present.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
|
||||||
to_present.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
|
to_present.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
|
||||||
to_present.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
|
to_present.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
|
||||||
@@ -762,10 +763,10 @@ bool Presenter::IsGuestPaused() const noexcept {
|
|||||||
return m_impl->window.loop.paused.load(std::memory_order_acquire);
|
return m_impl->window.loop.paused.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Presenter::NeedsImeRefresh() const noexcept {
|
bool Presenter::NeedsSystemOverlayRefresh() const noexcept {
|
||||||
const auto visual = GetImeVisualState();
|
const auto visual = GetSystemOverlayVisualState();
|
||||||
return visual.active ||
|
return visual.active ||
|
||||||
visual.revision != m_impl->presented_ime_revision.load(std::memory_order_acquire);
|
visual.revision != m_impl->presented_overlay_revision.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderContext& Presenter::Renderer() const noexcept {
|
RenderContext& Presenter::Renderer() const noexcept {
|
||||||
@@ -776,7 +777,7 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
|||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
m_impl->frames.ValidateForPresent(&frame, reuse);
|
m_impl->frames.ValidateForPresent(&frame, reuse);
|
||||||
|
|
||||||
const auto ime_visual = GetImeVisualState();
|
const auto overlay_visual = GetSystemOverlayVisualState();
|
||||||
auto& swapchain = m_impl->swapchain;
|
auto& swapchain = m_impl->swapchain;
|
||||||
for (uint32_t attempt = 0; attempt < 2; attempt++) {
|
for (uint32_t attempt = 0; attempt < 2; attempt++) {
|
||||||
auto status = swapchain.AcquireNextImage();
|
auto status = swapchain.AcquireNextImage();
|
||||||
@@ -787,8 +788,9 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
|||||||
{
|
{
|
||||||
Common::LockGuard render_lock(m_impl->renderer.GetMutex());
|
Common::LockGuard render_lock(m_impl->renderer.GetMutex());
|
||||||
auto& command = m_impl->present_scheduler.BeginCommand();
|
auto& command = m_impl->present_scheduler.BeginCommand();
|
||||||
const bool draw_ime_overlay = ime_visual.active && swapchain.PrepareImeOverlay();
|
const bool draw_system_overlay =
|
||||||
swapchain.RecordPresentCommands(command, frame.image, draw_ime_overlay);
|
overlay_visual.active && swapchain.PrepareSystemOverlay();
|
||||||
|
swapchain.RecordPresentCommands(command, frame.image, draw_system_overlay);
|
||||||
frame.present_tick = swapchain.Submit(m_impl->present_scheduler);
|
frame.present_tick = swapchain.Submit(m_impl->present_scheduler);
|
||||||
}
|
}
|
||||||
status = swapchain.Present();
|
status = swapchain.Present();
|
||||||
@@ -797,7 +799,8 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_impl->presented_ime_revision.store(ime_visual.revision, std::memory_order_release);
|
m_impl->presented_overlay_revision.store(overlay_visual.revision,
|
||||||
|
std::memory_order_release);
|
||||||
m_impl->window.UpdateTitle();
|
m_impl->window.UpdateTitle();
|
||||||
m_impl->frames.Release(&frame, true);
|
m_impl->frames.Release(&frame, true);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
#include "graphics/host_gpu/vma.h"
|
#include "graphics/host_gpu/vma.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
#include "graphics/presentation/imeOverlay.h"
|
|
||||||
#include "graphics/presentation/presenter.h"
|
#include "graphics/presentation/presenter.h"
|
||||||
|
#include "graphics/presentation/systemOverlay.h"
|
||||||
#include "graphics/presentation/videoOut.h"
|
#include "graphics/presentation/videoOut.h"
|
||||||
#include "graphics/presentation/window.h"
|
#include "graphics/presentation/window.h"
|
||||||
#include "graphics/presentation/window/windowInternal.h"
|
#include "graphics/presentation/window/windowInternal.h"
|
||||||
@@ -1139,7 +1139,7 @@ void WindowContext::RecreateSurface() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WindowContext::~WindowContext() {
|
WindowContext::~WindowContext() {
|
||||||
ShutdownImeInput();
|
ShutdownSystemOverlayInput();
|
||||||
presenter.reset();
|
presenter.reset();
|
||||||
LibKernel::Memory::InstallGpuResources(nullptr);
|
LibKernel::Memory::InstallGpuResources(nullptr);
|
||||||
render_context.reset();
|
render_context.reset();
|
||||||
|
|||||||
@@ -32,8 +32,8 @@
|
|||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
#include "graphics/host_gpu/vma.h"
|
#include "graphics/host_gpu/vma.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
#include "graphics/presentation/imeOverlay.h"
|
|
||||||
#include "graphics/presentation/renderDoc.h"
|
#include "graphics/presentation/renderDoc.h"
|
||||||
|
#include "graphics/presentation/systemOverlay.h"
|
||||||
#include "graphics/presentation/window/hostInput.h"
|
#include "graphics/presentation/window/hostInput.h"
|
||||||
#include "graphics/presentation/window/windowInternal.h"
|
#include "graphics/presentation/window/windowInternal.h"
|
||||||
#include "kytyGitVersion.h"
|
#include "kytyGitVersion.h"
|
||||||
@@ -513,7 +513,7 @@ void WindowContext::ProcessEvent(double time_s) {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ProcessImeInput(*event)) {
|
if (ProcessSystemOverlayInput(*event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -824,7 +824,7 @@ static void WindowCreate(WindowContext& context) {
|
|||||||
EXIT("%s\n", SDL_GetError());
|
EXIT("%s\n", SDL_GetError());
|
||||||
}
|
}
|
||||||
HostInputInit();
|
HostInputInit();
|
||||||
InitializeImeInput();
|
InitializeSystemOverlayInput();
|
||||||
|
|
||||||
LOGF("WindowCreate(): width = %d, height = %d\n", width, height);
|
LOGF("WindowCreate(): width = %d, height = %d\n", width, height);
|
||||||
|
|
||||||
|
|||||||
+62
-8
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
// NOLINTNEXTLINE(modernize-concat-nested-namespaces)
|
// NOLINTNEXTLINE(modernize-concat-nested-namespaces)
|
||||||
namespace Libs::Dialog {
|
namespace Libs::Dialog {
|
||||||
@@ -643,11 +644,61 @@ struct ErrorDialogParam {
|
|||||||
int32_t reserved;
|
int32_t reserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int g_error_status = STATUS_NONE;
|
static_assert(sizeof(ErrorDialogParam) == 16);
|
||||||
|
|
||||||
|
static std::mutex g_error_mutex;
|
||||||
|
static int g_error_status = STATUS_NONE;
|
||||||
|
static uint64_t g_error_generation = 0;
|
||||||
|
static uint64_t g_error_revision = 0;
|
||||||
|
static int32_t g_error_code = 0;
|
||||||
|
static void (*g_error_visibility_callback)() = nullptr;
|
||||||
|
|
||||||
|
static void SetStatus(int status, std::unique_lock<std::mutex>& lock) {
|
||||||
|
const bool visibility_changed =
|
||||||
|
(g_error_status == STATUS_RUNNING) != (status == STATUS_RUNNING);
|
||||||
|
g_error_status = status;
|
||||||
|
if (visibility_changed) {
|
||||||
|
g_error_revision++;
|
||||||
|
}
|
||||||
|
const auto callback = visibility_changed ? g_error_visibility_callback : nullptr;
|
||||||
|
lock.unlock();
|
||||||
|
if (callback != nullptr) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetHostSnapshot(HostSnapshot* snapshot) {
|
||||||
|
std::scoped_lock lock(g_error_mutex);
|
||||||
|
if (g_error_status != STATUS_RUNNING) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*snapshot = {g_error_generation, g_error_code};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VisualState GetVisualState() noexcept {
|
||||||
|
std::scoped_lock lock(g_error_mutex);
|
||||||
|
return {g_error_status == STATUS_RUNNING, g_error_revision};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetVisibilityCallback(void (*callback)()) {
|
||||||
|
std::scoped_lock lock(g_error_mutex);
|
||||||
|
g_error_visibility_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostAccept(uint64_t generation) {
|
||||||
|
std::unique_lock lock(g_error_mutex);
|
||||||
|
if (g_error_status != STATUS_RUNNING || generation != g_error_generation) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SetStatus(STATUS_FINISHED, lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int KYTY_SYSV_ABI ErrorDialogInitialize() {
|
int KYTY_SYSV_ABI ErrorDialogInitialize() {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
|
std::scoped_lock lock(g_error_mutex);
|
||||||
if (g_error_status != STATUS_NONE) {
|
if (g_error_status != STATUS_NONE) {
|
||||||
return ERROR_ALREADY_INITIALIZED;
|
return ERROR_ALREADY_INITIALIZED;
|
||||||
}
|
}
|
||||||
@@ -660,6 +711,7 @@ int KYTY_SYSV_ABI ErrorDialogInitialize() {
|
|||||||
int KYTY_SYSV_ABI ErrorDialogOpen(const void* param) {
|
int KYTY_SYSV_ABI ErrorDialogOpen(const void* param) {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
|
std::unique_lock lock(g_error_mutex);
|
||||||
if (g_error_status != STATUS_INITIALIZED && g_error_status != STATUS_FINISHED) {
|
if (g_error_status != STATUS_INITIALIZED && g_error_status != STATUS_FINISHED) {
|
||||||
return ERROR_INVALID_STATE;
|
return ERROR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
@@ -677,7 +729,9 @@ int KYTY_SYSV_ABI ErrorDialogOpen(const void* param) {
|
|||||||
"\t user_id = %d\n",
|
"\t user_id = %d\n",
|
||||||
p->size, static_cast<uint32_t>(p->error_code), p->user_id);
|
p->size, static_cast<uint32_t>(p->error_code), p->user_id);
|
||||||
|
|
||||||
g_error_status = STATUS_RUNNING;
|
g_error_code = p->error_code;
|
||||||
|
g_error_generation++;
|
||||||
|
SetStatus(STATUS_RUNNING, lock);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -685,11 +739,12 @@ int KYTY_SYSV_ABI ErrorDialogOpen(const void* param) {
|
|||||||
int KYTY_SYSV_ABI ErrorDialogClose() {
|
int KYTY_SYSV_ABI ErrorDialogClose() {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
|
std::unique_lock lock(g_error_mutex);
|
||||||
if (g_error_status != STATUS_RUNNING) {
|
if (g_error_status != STATUS_RUNNING) {
|
||||||
return ERROR_INVALID_STATE;
|
return ERROR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_error_status = STATUS_FINISHED;
|
SetStatus(STATUS_FINISHED, lock);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -697,11 +752,12 @@ int KYTY_SYSV_ABI ErrorDialogClose() {
|
|||||||
int KYTY_SYSV_ABI ErrorDialogTerminate() {
|
int KYTY_SYSV_ABI ErrorDialogTerminate() {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
|
std::unique_lock lock(g_error_mutex);
|
||||||
if (g_error_status == STATUS_NONE) {
|
if (g_error_status == STATUS_NONE) {
|
||||||
return ERROR_NOT_INITIALIZED;
|
return ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_error_status = STATUS_NONE;
|
SetStatus(STATUS_NONE, lock);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -709,16 +765,14 @@ int KYTY_SYSV_ABI ErrorDialogTerminate() {
|
|||||||
int KYTY_SYSV_ABI ErrorDialogUpdateStatus() {
|
int KYTY_SYSV_ABI ErrorDialogUpdateStatus() {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
if (g_error_status == STATUS_RUNNING) {
|
std::scoped_lock lock(g_error_mutex);
|
||||||
g_error_status = STATUS_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return g_error_status;
|
return g_error_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int KYTY_SYSV_ABI ErrorDialogGetStatus() {
|
int KYTY_SYSV_ABI ErrorDialogGetStatus() {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
|
std::scoped_lock lock(g_error_mutex);
|
||||||
return g_error_status;
|
return g_error_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,21 @@ int KYTY_SYSV_ABI MsgDialogProgressBarSetMsg(int target, const char* msg);
|
|||||||
|
|
||||||
namespace ErrorDialog {
|
namespace ErrorDialog {
|
||||||
|
|
||||||
|
struct HostSnapshot {
|
||||||
|
uint64_t generation;
|
||||||
|
int32_t error_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VisualState {
|
||||||
|
bool active;
|
||||||
|
uint64_t revision;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GetHostSnapshot(HostSnapshot* snapshot);
|
||||||
|
VisualState GetVisualState() noexcept;
|
||||||
|
void SetVisibilityCallback(void (*callback)());
|
||||||
|
bool HostAccept(uint64_t generation);
|
||||||
|
|
||||||
int KYTY_SYSV_ABI ErrorDialogInitialize();
|
int KYTY_SYSV_ABI ErrorDialogInitialize();
|
||||||
int KYTY_SYSV_ABI ErrorDialogOpen(const void* param);
|
int KYTY_SYSV_ABI ErrorDialogOpen(const void* param);
|
||||||
int KYTY_SYSV_ABI ErrorDialogClose();
|
int KYTY_SYSV_ABI ErrorDialogClose();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/stringUtils.h"
|
#include "common/stringUtils.h"
|
||||||
|
#include "libs/dialog.h"
|
||||||
#include "libs/errno.h"
|
#include "libs/errno.h"
|
||||||
#include "libs/libs.h"
|
#include "libs/libs.h"
|
||||||
#include "loader/symbolDatabase.h"
|
#include "loader/symbolDatabase.h"
|
||||||
@@ -160,7 +161,8 @@ static int KYTY_SYSV_ABI SystemServiceGetStatus(SystemServiceStatus* status) {
|
|||||||
return SYSTEM_SERVICE_ERROR_PARAMETER;
|
return SYSTEM_SERVICE_ERROR_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
*status = SystemServiceStatus();
|
*status = SystemServiceStatus();
|
||||||
|
status->is_system_ui_overlaid = Dialog::ErrorDialog::GetVisualState().active;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
#include "kernel/eventQueue.h"
|
#include "kernel/eventQueue.h"
|
||||||
#include "kernel/memory.h"
|
#include "kernel/memory.h"
|
||||||
#include "libs/agc.h"
|
#include "libs/agc.h"
|
||||||
|
#include "libs/dialog.h"
|
||||||
#include "libs/errno.h"
|
#include "libs/errno.h"
|
||||||
#include "spirv-tools/libspirv.hpp"
|
#include "spirv-tools/libspirv.hpp"
|
||||||
|
|
||||||
@@ -935,6 +936,142 @@ void Require(const char *shader_name, const char *stage, bool value,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckErrorDialogLifecycle() {
|
||||||
|
namespace ErrorDialog = Libs::Dialog::ErrorDialog;
|
||||||
|
struct Param {
|
||||||
|
int32_t size = 16;
|
||||||
|
int32_t error_code = static_cast<int32_t>(0x80550006u);
|
||||||
|
int32_t user_id = 1;
|
||||||
|
int32_t reserved = 0;
|
||||||
|
} param;
|
||||||
|
static_assert(sizeof(Param) == 16);
|
||||||
|
constexpr int invalid_state = static_cast<int>(0x80ed0005u);
|
||||||
|
constexpr int invalid_param = static_cast<int>(0x80ed0003u);
|
||||||
|
static std::vector<ErrorDialog::VisualState> notifications;
|
||||||
|
notifications.clear();
|
||||||
|
ErrorDialog::SetVisibilityCallback([] {
|
||||||
|
const auto visual = ErrorDialog::GetVisualState();
|
||||||
|
ErrorDialog::HostSnapshot snapshot{};
|
||||||
|
Require("ErrorDialog", "callback reentry",
|
||||||
|
ErrorDialog::GetHostSnapshot(&snapshot) == visual.active &&
|
||||||
|
(ErrorDialog::ErrorDialogGetStatus() == 2) == visual.active,
|
||||||
|
"visibility callback observed inconsistent dialog state");
|
||||||
|
notifications.push_back(visual);
|
||||||
|
});
|
||||||
|
const auto initial_revision = ErrorDialog::GetVisualState().revision;
|
||||||
|
const auto check_state = [&](const char *stage, int status,
|
||||||
|
size_t visibility_changes) {
|
||||||
|
const auto visual = ErrorDialog::GetVisualState();
|
||||||
|
ErrorDialog::HostSnapshot snapshot{};
|
||||||
|
Require("ErrorDialog", stage,
|
||||||
|
ErrorDialog::ErrorDialogGetStatus() == status &&
|
||||||
|
ErrorDialog::ErrorDialogUpdateStatus() == status &&
|
||||||
|
visual.active == (status == 2) &&
|
||||||
|
ErrorDialog::GetHostSnapshot(&snapshot) == visual.active &&
|
||||||
|
visual.revision == initial_revision + visibility_changes &&
|
||||||
|
notifications.size() == visibility_changes &&
|
||||||
|
(notifications.empty() ||
|
||||||
|
(notifications.back().active == visual.active &&
|
||||||
|
notifications.back().revision == visual.revision)),
|
||||||
|
"status, host visibility, or visibility notification changed "
|
||||||
|
"unexpectedly");
|
||||||
|
};
|
||||||
|
check_state("uninitialized", 0, 0);
|
||||||
|
Require("ErrorDialog", "uninitialized operations",
|
||||||
|
ErrorDialog::ErrorDialogTerminate() ==
|
||||||
|
static_cast<int>(0x80ed0001u) &&
|
||||||
|
ErrorDialog::ErrorDialogClose() == invalid_state,
|
||||||
|
"uninitialized dialog accepted termination or close");
|
||||||
|
Require("ErrorDialog", "initialize",
|
||||||
|
ErrorDialog::ErrorDialogInitialize() == 0 &&
|
||||||
|
ErrorDialog::ErrorDialogInitialize() ==
|
||||||
|
static_cast<int>(0x80ed0002u),
|
||||||
|
"initialization did not reject a second initialization");
|
||||||
|
Param invalid = param;
|
||||||
|
invalid.size--;
|
||||||
|
Require("ErrorDialog", "invalid open",
|
||||||
|
ErrorDialog::ErrorDialogOpen(nullptr) == invalid_param &&
|
||||||
|
ErrorDialog::ErrorDialogOpen(&invalid) == invalid_param &&
|
||||||
|
ErrorDialog::ErrorDialogClose() == invalid_state,
|
||||||
|
"invalid parameters or close were accepted before opening");
|
||||||
|
check_state("failed open preserves initialized state", 1, 0);
|
||||||
|
Require("ErrorDialog", "open", ErrorDialog::ErrorDialogOpen(¶m) == 0,
|
||||||
|
"valid error dialog did not open");
|
||||||
|
ErrorDialog::HostSnapshot first{};
|
||||||
|
Require("ErrorDialog", "copied error code",
|
||||||
|
ErrorDialog::GetHostSnapshot(&first) &&
|
||||||
|
first.error_code == param.error_code,
|
||||||
|
"host dialog did not expose the guest error code");
|
||||||
|
param.error_code = static_cast<int32_t>(0x8055000au);
|
||||||
|
for (int poll = 0; poll < 8; ++poll) {
|
||||||
|
check_state("polling waits for acknowledgement", 2, 1);
|
||||||
|
}
|
||||||
|
Require(
|
||||||
|
"ErrorDialog", "running failures",
|
||||||
|
ErrorDialog::ErrorDialogOpen(¶m) == invalid_state &&
|
||||||
|
!ErrorDialog::HostAccept(first.generation + 1),
|
||||||
|
"an invalid open or stale acknowledgement replaced the active dialog");
|
||||||
|
ErrorDialog::HostSnapshot current{};
|
||||||
|
Require("ErrorDialog", "failed open preserves active dialog",
|
||||||
|
ErrorDialog::GetHostSnapshot(¤t) &&
|
||||||
|
current.generation == first.generation &&
|
||||||
|
current.error_code == first.error_code,
|
||||||
|
"active error code or generation changed after a failed open");
|
||||||
|
check_state("failed operations preserve running state", 2, 1);
|
||||||
|
Require("ErrorDialog", "acknowledge",
|
||||||
|
ErrorDialog::HostAccept(first.generation),
|
||||||
|
"current host acknowledgement did not finish the dialog");
|
||||||
|
check_state("acknowledged", 3, 2);
|
||||||
|
Require("ErrorDialog", "finished operations",
|
||||||
|
!ErrorDialog::HostAccept(first.generation) &&
|
||||||
|
ErrorDialog::ErrorDialogClose() == invalid_state &&
|
||||||
|
ErrorDialog::ErrorDialogOpen(&invalid) == invalid_param,
|
||||||
|
"finished dialog accepted duplicate completion or an invalid open");
|
||||||
|
check_state("failed operations preserve finished state", 3, 2);
|
||||||
|
Require("ErrorDialog", "reopen and stale acknowledgement",
|
||||||
|
ErrorDialog::ErrorDialogOpen(¶m) == 0 &&
|
||||||
|
ErrorDialog::GetHostSnapshot(¤t) &&
|
||||||
|
current.generation != first.generation &&
|
||||||
|
current.error_code == param.error_code &&
|
||||||
|
!ErrorDialog::HostAccept(first.generation),
|
||||||
|
"reopened dialog accepted acknowledgement from its predecessor");
|
||||||
|
check_state("reopened", 2, 3);
|
||||||
|
Require("ErrorDialog", "guest close", ErrorDialog::ErrorDialogClose() == 0,
|
||||||
|
"guest close did not finish the running dialog");
|
||||||
|
check_state("closed", 3, 4);
|
||||||
|
Require("ErrorDialog", "terminate running dialog",
|
||||||
|
ErrorDialog::ErrorDialogOpen(¶m) == 0 &&
|
||||||
|
ErrorDialog::GetHostSnapshot(¤t) &&
|
||||||
|
ErrorDialog::ErrorDialogTerminate() == 0 &&
|
||||||
|
!ErrorDialog::HostAccept(current.generation),
|
||||||
|
"termination failed to remove the active dialog");
|
||||||
|
check_state("terminated", 0, 6);
|
||||||
|
Require("ErrorDialog", "reinitialize and stale acknowledgement",
|
||||||
|
ErrorDialog::ErrorDialogInitialize() == 0 &&
|
||||||
|
ErrorDialog::ErrorDialogOpen(¶m) == 0 &&
|
||||||
|
!ErrorDialog::HostAccept(current.generation),
|
||||||
|
"reinitialization reused an old dialog generation");
|
||||||
|
check_state("reinitialized", 2, 7);
|
||||||
|
Require("ErrorDialog", "final termination",
|
||||||
|
ErrorDialog::ErrorDialogTerminate() == 0 &&
|
||||||
|
ErrorDialog::ErrorDialogTerminate() ==
|
||||||
|
static_cast<int>(0x80ed0001u),
|
||||||
|
"termination did not restore the uninitialized state");
|
||||||
|
check_state("final state", 0, 8);
|
||||||
|
ErrorDialog::SetVisibilityCallback(nullptr);
|
||||||
|
Require("ErrorDialog", "lifecycle without a visibility listener",
|
||||||
|
ErrorDialog::ErrorDialogInitialize() == 0 &&
|
||||||
|
ErrorDialog::ErrorDialogOpen(¶m) == 0 &&
|
||||||
|
ErrorDialog::ErrorDialogClose() == 0 &&
|
||||||
|
ErrorDialog::ErrorDialogTerminate() == 0 &&
|
||||||
|
ErrorDialog::ErrorDialogGetStatus() == 0 &&
|
||||||
|
!ErrorDialog::GetVisualState().active &&
|
||||||
|
ErrorDialog::GetVisualState().revision == initial_revision + 10 &&
|
||||||
|
notifications.size() == 8,
|
||||||
|
"guest lifecycle depended on a registered host visibility listener");
|
||||||
|
std::printf("[host] %-32s ok\n", "ErrorDialogLifecycle");
|
||||||
|
}
|
||||||
|
|
||||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||||
template <typename Function>
|
template <typename Function>
|
||||||
void ExpectFatal(const char *name, Function function) {
|
void ExpectFatal(const char *name, Function function) {
|
||||||
@@ -27438,6 +27575,10 @@ int main(int argc, char **argv) {
|
|||||||
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
||||||
EnsureConfigInitialized();
|
EnsureConfigInitialized();
|
||||||
CheckLeastRecentlyUsedCacheOrdering();
|
CheckLeastRecentlyUsedCacheOrdering();
|
||||||
|
if (argc == 2 && std::strcmp(argv[1], "--error-dialog-only") == 0) {
|
||||||
|
CheckErrorDialogLifecycle();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--fmask-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--fmask-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
RunCase(&vulkan, ImageLoadFmaskUsesNativeSampleMapping());
|
RunCase(&vulkan, ImageLoadFmaskUsesNativeSampleMapping());
|
||||||
@@ -27768,6 +27909,7 @@ int main(int argc, char **argv) {
|
|||||||
CheckNativeImageDescriptorTypes();
|
CheckNativeImageDescriptorTypes();
|
||||||
CheckClipControlDepthClipState();
|
CheckClipControlDepthClipState();
|
||||||
CheckReferenceClockScale();
|
CheckReferenceClockScale();
|
||||||
|
CheckErrorDialogLifecycle();
|
||||||
CheckVulkan13FeatureRequirements();
|
CheckVulkan13FeatureRequirements();
|
||||||
CheckPm4AcquireMemNoOp(vulkan.RuntimeRenderer());
|
CheckPm4AcquireMemNoOp(vulkan.RuntimeRenderer());
|
||||||
CheckPm4SyntheticOcclusionCounterDump(vulkan.RuntimeRenderer());
|
CheckPm4SyntheticOcclusionCounterDump(vulkan.RuntimeRenderer());
|
||||||
|
|||||||
Reference in New Issue
Block a user