From 5dbe2c4f4845c0c5833c3edfd6f4118b9879d73f Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 14 Oct 2024 14:09:25 +0300 Subject: [PATCH] Revert "Devtools - Inspect regs/User data/Shader disassembly (#1358)" This reverts commit c0d9014ca5d60b796724e03ee1389d1fa1d875f3. --- CMakeLists.txt | 10 - src/core/debug_state.cpp | 65 +- src/core/debug_state.h | 46 +- src/core/devtools/layer.cpp | 111 +- src/core/devtools/options.cpp | 24 - src/core/devtools/options.h | 21 - src/core/devtools/widget/cmd_list.cpp | 560 ++-- src/core/devtools/widget/cmd_list.h | 67 +- src/core/devtools/widget/common.h | 100 - src/core/devtools/widget/frame_dump.cpp | 87 +- src/core/devtools/widget/frame_dump.h | 6 +- src/core/devtools/widget/reg_popup.cpp | 182 -- src/core/devtools/widget/reg_popup.h | 38 - src/core/devtools/widget/reg_view.cpp | 305 --- src/core/devtools/widget/reg_view.h | 50 - src/core/devtools/widget/text_editor.cpp | 2334 ----------------- src/core/devtools/widget/text_editor.h | 408 --- src/core/libraries/dialogs/error_dialog.cpp | 2 +- src/core/libraries/gnmdriver/gnmdriver.cpp | 31 +- src/core/libraries/gnmdriver/gnmdriver.h | 2 +- .../save_data/dialog/savedatadialog_ui.cpp | 4 +- src/core/libraries/system/msgdialog_ui.cpp | 2 +- src/imgui/imgui_std.h | 7 +- src/imgui/renderer/texture_manager.cpp | 6 - src/video_core/amdgpu/liverpool.cpp | 27 - src/video_core/amdgpu/liverpool.h | 6 +- src/video_core/amdgpu/pm4_cmds.h | 1 - 27 files changed, 287 insertions(+), 4215 deletions(-) delete mode 100644 src/core/devtools/options.cpp delete mode 100644 src/core/devtools/options.h delete mode 100644 src/core/devtools/widget/common.h delete mode 100644 src/core/devtools/widget/reg_popup.cpp delete mode 100644 src/core/devtools/widget/reg_popup.h delete mode 100644 src/core/devtools/widget/reg_view.cpp delete mode 100644 src/core/devtools/widget/reg_view.h delete mode 100644 src/core/devtools/widget/text_editor.cpp delete mode 100644 src/core/devtools/widget/text_editor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 781e93e10..37021746d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,25 +354,15 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp set(DEV_TOOLS src/core/devtools/layer.cpp src/core/devtools/layer.h - src/core/devtools/options.cpp - src/core/devtools/options.h src/core/devtools/gcn/gcn_context_regs.cpp src/core/devtools/gcn/gcn_op_names.cpp src/core/devtools/gcn/gcn_shader_regs.cpp src/core/devtools/widget/cmd_list.cpp src/core/devtools/widget/cmd_list.h - src/core/devtools/widget/common.h src/core/devtools/widget/frame_dump.cpp src/core/devtools/widget/frame_dump.h src/core/devtools/widget/frame_graph.cpp src/core/devtools/widget/frame_graph.h - src/core/devtools/widget/imgui_memory_editor.h - src/core/devtools/widget/reg_popup.cpp - src/core/devtools/widget/reg_popup.h - src/core/devtools/widget/reg_view.cpp - src/core/devtools/widget/reg_view.h - src/core/devtools/widget/text_editor.cpp - src/core/devtools/widget/text_editor.h ) set(COMMON src/common/logging/backend.cpp diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 93b00285d..050143e6e 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -1,16 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include - #include "common/assert.h" #include "common/native_clock.h" #include "common/singleton.h" #include "debug_state.h" -#include "devtools/widget/common.h" +#include "libraries/kernel/event_queues.h" #include "libraries/kernel/time_management.h" #include "libraries/system/msgdialog.h" -#include "video_core/amdgpu/pm4_cmds.h" using namespace DebugStateType; @@ -98,68 +95,8 @@ void DebugStateImpl::ResumeGuestThreads() { } void DebugStateImpl::RequestFrameDump(s32 count) { - ASSERT(!DumpingCurrentFrame()); gnm_frame_dump_request_count = count; frame_dump_list.clear(); frame_dump_list.resize(count); waiting_submit_pause = true; } - -void DebugStateImpl::PushQueueDump(QueueDump dump) { - ASSERT(DumpingCurrentFrame()); - std::unique_lock lock{frame_dump_list_mutex}; - auto& frame = GetFrameDump(); - { // Find draw calls - auto data = std::span{dump.data}; - auto initial_data = data.data(); - while (!data.empty()) { - const auto* header = reinterpret_cast(data.data()); - const auto type = header->type; - if (type == 2) { - data = data.subspan(1); - } else if (type != 3) { - UNREACHABLE(); - } - const AmdGpu::PM4ItOpcode opcode = header->opcode; - if (Core::Devtools::Widget::IsDrawCall(opcode)) { - const auto offset = - reinterpret_cast(header) - reinterpret_cast(initial_data); - const auto addr = dump.base_addr + offset; - waiting_reg_dumps.emplace(addr, &frame); - waiting_reg_dumps_dbg.emplace( - addr, - fmt::format("#{} h({}) queue {} {} {}", - frame_dump_list.size() - gnm_frame_dump_request_count, addr, - magic_enum::enum_name(dump.type), dump.submit_num, dump.num2)); - } - data = data.subspan(header->NumWords() + 1); - } - } - frame.queues.push_back(std::move(dump)); -} - -void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, - const AmdGpu::Liverpool::Regs& regs) { - std::scoped_lock lock{frame_dump_list_mutex}; - const auto it = waiting_reg_dumps.find(header_addr); - if (it == waiting_reg_dumps.end()) { - return; - } - auto& frame = *it->second; - waiting_reg_dumps.erase(it); - waiting_reg_dumps_dbg.erase(waiting_reg_dumps_dbg.find(header_addr)); - auto& dump = frame.regs[header_addr - base_addr]; - dump.regs = regs; - for (int i = 0; i < RegDump::MaxShaderStages; i++) { - if (regs.stage_enable.IsStageEnabled(i)) { - auto stage = regs.ProgramForStage(i); - if (stage->address_lo != 0) { - auto code = stage->Code(); - dump.stages[i] = ShaderDump{ - .user_data = *stage, - .code = std::vector{code.begin(), code.end()}, - }; - } - } - } -} diff --git a/src/core/debug_state.h b/src/core/debug_state.h index 26dfa202e..00c687fa5 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -5,14 +5,10 @@ #include #include -#include -#include #include #include #include "common/types.h" -#include "video_core/amdgpu/liverpool.h" -#include "video_core/renderer_vulkan/vk_pipeline_cache.h" #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -46,23 +42,10 @@ struct QueueDump { u32 submit_num; u32 num2; // acb: queue_num; else: buffer_in_submit std::vector data; - uintptr_t base_addr; -}; - -struct ShaderDump { - Vulkan::Liverpool::ShaderProgram user_data{}; - std::vector code{}; -}; - -struct RegDump { - static constexpr size_t MaxShaderStages = 5; - Vulkan::Liverpool::Regs regs{}; - std::array stages{}; }; struct FrameDump { std::vector queues; - std::unordered_map regs; // address -> reg dump }; class DebugStateImpl { @@ -78,24 +61,15 @@ class DebugStateImpl { std::atomic_int32_t gnm_frame_count = 0; s32 gnm_frame_dump_request_count = -1; - std::unordered_map waiting_reg_dumps; - std::unordered_map waiting_reg_dumps_dbg; bool waiting_submit_pause = false; bool should_show_frame_dump = false; - std::shared_mutex frame_dump_list_mutex; + std::mutex frame_dump_list_mutex; std::vector frame_dump_list{}; std::queue debug_message_popup; public: - void ShowDebugMessage(std::string message) { - if (message.empty()) { - return; - } - debug_message_popup.push(std::move(message)); - } - void AddCurrentThreadToGuestList(); void RemoveCurrentThreadFromGuestList(); @@ -125,11 +99,6 @@ public: return gnm_frame_dump_request_count > 0; } - bool DumpingCurrentReg() { - std::shared_lock lock{frame_dump_list_mutex}; - return !waiting_reg_dumps.empty(); - } - bool ShouldPauseInSubmit() const { return waiting_submit_pause && gnm_frame_dump_request_count == 0; } @@ -140,10 +109,17 @@ public: return frame_dump_list[frame_dump_list.size() - gnm_frame_dump_request_count]; } - void PushQueueDump(QueueDump dump); + void PushQueueDump(QueueDump dump) { + std::unique_lock lock{frame_dump_list_mutex}; + GetFrameDump().queues.push_back(std::move(dump)); + } - void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, - const AmdGpu::Liverpool::Regs& regs); + void ShowDebugMessage(std::string message) { + if (message.empty()) { + return; + } + debug_message_popup.push(std::move(message)); + } }; } // namespace DebugStateType diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 17ef43bbc..0c7e85e4c 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -10,7 +10,6 @@ #include "imgui/imgui_std.h" #include "imgui_internal.h" #include "layer.h" -#include "options.h" #include "widget/frame_dump.h" #include "widget/frame_graph.h" @@ -19,9 +18,10 @@ using namespace Core::Devtools; using L = Core::Devtools::Layer; static bool show_simple_fps = false; - static float fps_scale = 1.0f; + static bool show_advanced_debug = false; + static int dump_frame_count = 1; static Widget::FrameGraph frame_graph; @@ -29,16 +29,12 @@ static std::vector frame_viewers; static float debug_popup_timing = 3.0f; -static bool just_opened_options = false; - void L::DrawMenuBar() { const auto& ctx = *GImGui; const auto& io = ctx.IO; auto isSystemPaused = DebugState.IsGuestThreadsPaused(); - bool open_popup_options = false; - if (BeginMainMenuBar()) { if (BeginMenu("Options")) { if (MenuItemEx("Emulator Paused", nullptr, nullptr, isSystemPaused)) { @@ -59,7 +55,6 @@ void L::DrawMenuBar() { } ImGui::EndMenu(); } - open_popup_options = MenuItem("Options"); ImGui::EndMenu(); } EndMainMenuBar(); @@ -79,11 +74,6 @@ void L::DrawMenuBar() { } } } - - if (open_popup_options) { - OpenPopup("GPU Tools Options"); - just_opened_options = true; - } } void L::DrawAdvanced() { @@ -101,19 +91,13 @@ void L::DrawAdvanced() { ->AddText({10.0f, io.DisplaySize.y - 40.0f}, IM_COL32_WHITE, "Emulator paused"); } - if (DebugState.should_show_frame_dump && DebugState.waiting_reg_dumps.empty()) { + if (DebugState.should_show_frame_dump) { DebugState.should_show_frame_dump = false; std::unique_lock lock{DebugState.frame_dump_list_mutex}; while (!DebugState.frame_dump_list.empty()) { - const auto& frame_dump = DebugState.frame_dump_list.back(); - frame_viewers.emplace_back(frame_dump); + auto frame_dump = std::move(DebugState.frame_dump_list.back()); DebugState.frame_dump_list.pop_back(); - } - static bool first_time = true; - if (first_time) { - first_time = false; - DebugState.ShowDebugMessage("Tip: You can shift+click any\n" - "popup to open a new window"); + frame_viewers.emplace_back(frame_dump); } } @@ -149,30 +133,6 @@ void L::DrawAdvanced() { debug_popup_timing = 3.0f; } } - - bool close_popup_options = true; - if (BeginPopupModal("GPU Tools Options", &close_popup_options, - ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { - static char disassembly_cli[512]; - - if (just_opened_options) { - just_opened_options = false; - auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1); - disassembly_cli[s] = '\0'; - } - - InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli)); - if (IsItemHovered()) { - SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")"); - } - - if (Button("Save")) { - Options.disassembly_cli = disassembly_cli; - SaveIniSettingsToDisk(io.IniFilename); - CloseCurrentPopup(); - } - EndPopup(); - } } void L::DrawSimple() { @@ -180,54 +140,26 @@ void L::DrawSimple() { Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); } -static void LoadSettings(const char* line) { - int i; - float f; - if (sscanf(line, "fps_scale=%f", &f) == 1) { - fps_scale = f; - return; - } - if (sscanf(line, "show_advanced_debug=%d", &i) == 1) { - show_advanced_debug = i != 0; - return; - } - if (sscanf(line, "show_frame_graph=%d", &i) == 1) { - frame_graph.is_open = i != 0; - return; - } - if (sscanf(line, "dump_frame_count=%d", &i) == 1) { - dump_frame_count = i; - return; - } -} - void L::SetupSettings() { frame_graph.is_open = true; - using SettingLoader = void (*)(const char*); - ImGuiSettingsHandler handler{}; handler.TypeName = "DevtoolsLayer"; handler.TypeHash = ImHashStr(handler.TypeName); handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) { - if (std::string_view("Data") == name) { - static_assert(std::is_same_v); - return (void*)&LoadSettings; - } - if (std::string_view("CmdList") == name) { - static_assert( - std::is_same_v); - return (void*)&Widget::CmdListViewer::LoadConfig; - } - if (std::string_view("Options") == name) { - static_assert(std::is_same_v); - return (void*)&LoadOptionsConfig; - } - return (void*)nullptr; + return std::string_view("Data") == name ? (void*)1 : nullptr; }; - handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void* handle, const char* line) { - if (handle != nullptr) { - reinterpret_cast(handle)(line); + handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void*, const char* line) { + int v; + float f; + if (sscanf(line, "fps_scale=%f", &f) == 1) { + fps_scale = f; + } else if (sscanf(line, "show_advanced_debug=%d", &v) == 1) { + show_advanced_debug = v != 0; + } else if (sscanf(line, "show_frame_graph=%d", &v) == 1) { + frame_graph.is_open = v != 0; + } else if (sscanf(line, "dump_frame_count=%d", &v) == 1) { + dump_frame_count = v; } }; handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { @@ -237,19 +169,12 @@ void L::SetupSettings() { buf->appendf("show_frame_graph=%d\n", frame_graph.is_open); buf->appendf("dump_frame_count=%d\n", dump_frame_count); buf->append("\n"); - buf->appendf("[%s][CmdList]\n", handler->TypeName); - Widget::CmdListViewer::SerializeConfig(buf); - buf->append("\n"); - buf->appendf("[%s][Options]\n", handler->TypeName); - SerializeOptionsConfig(buf); - buf->append("\n"); }; AddSettingsHandler(&handler); const ImGuiID dock_id = ImHashStr("FrameDumpDock"); DockBuilderAddNode(dock_id, 0); - DockBuilderSetNodePos(dock_id, ImVec2{450.0, 150.0}); - DockBuilderSetNodeSize(dock_id, ImVec2{400.0, 500.0}); + DockBuilderSetNodePos(dock_id, ImVec2{50.0, 50.0}); DockBuilderFinish(dock_id); } diff --git a/src/core/devtools/options.cpp b/src/core/devtools/options.cpp deleted file mode 100644 index 82fa6e87e..000000000 --- a/src/core/devtools/options.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "options.h" - -namespace Core::Devtools { - -TOptions Options; - -void LoadOptionsConfig(const char* line) { - char str[512]; - if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) { - Options.disassembly_cli = str; - return; - } -} - -void SerializeOptionsConfig(ImGuiTextBuffer* buf) { - buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str()); -} - -} // namespace Core::Devtools diff --git a/src/core/devtools/options.h b/src/core/devtools/options.h deleted file mode 100644 index 9d291d768..000000000 --- a/src/core/devtools/options.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -struct ImGuiTextBuffer; - -namespace Core::Devtools { - -struct TOptions { - std::string disassembly_cli; -}; - -extern TOptions Options; - -void LoadOptionsConfig(const char* line); -void SerializeOptionsConfig(ImGuiTextBuffer* buf); - -} // namespace Core::Devtools diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index f5d31efef..012891c37 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -32,26 +32,6 @@ const char* GetOpCodeName(u32 op); namespace Core::Devtools::Widget { -static bool group_batches = true; -static bool show_markers = false; - -void CmdListViewer::LoadConfig(const char* line) { - int i; - if (sscanf(line, "group_batches=%d", &i) == 1) { - group_batches = i != 0; - return; - } - if (sscanf(line, "show_markers=%d", &i) == 1) { - show_markers = i != 0; - return; - } -} - -void CmdListViewer::SerializeConfig(ImGuiTextBuffer* buf) { - buf->appendf("group_batches=%d\n", group_batches); - buf->appendf("show_markers=%d\n", show_markers); -} - template static HdrType GetNext(HdrType this_pm4, uint32_t n) { HdrType curr_pm4 = this_pm4; @@ -63,11 +43,10 @@ static HdrType GetNext(HdrType this_pm4, uint32_t n) { return curr_pm4; } -void ParsePolygonControl(u32 value, bool begin_table) { +static void ParsePolygonControl(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("PA_SU_SC_MODE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("PA_SU_SC_MODE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("CULL_FRONT"); @@ -147,17 +126,14 @@ void ParsePolygonControl(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.multi_prim_ib_ena.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseAaConfig(u32 value, bool begin_table) { +static void ParseAaConfig(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("PA_SC_AA_CONFIG", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("PA_SC_AA_CONFIG", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("MSAA_NUM_SAMPLES"); @@ -188,17 +164,14 @@ void ParseAaConfig(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.detail_to_exposed_mode.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseViewportControl(u32 value, bool begin_table) { +static void ParseViewportControl(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("PA_CL_VTE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("PA_CL_VTE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("VPORT_X_SCALE_ENA"); @@ -259,17 +232,14 @@ void ParseViewportControl(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.perfcounter_ref.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseColorControl(u32 value, bool begin_table) { +static void ParseColorControl(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("CB_COLOR_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("CB_COLOR_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("DISABLE_DUAL_QUAD__VI"); @@ -294,17 +264,14 @@ void ParseColorControl(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.rop3.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseColor0Info(u32 value, bool begin_table) { +static void ParseColor0Info(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("CB_COLOR_INFO", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("CB_COLOR_INFO", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("ENDIAN"); @@ -413,17 +380,14 @@ void ParseColor0Info(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.cmask_addr_type.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseColor0Attrib(u32 value, bool begin_table) { +static void ParseColor0Attrib(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("CB_COLOR_ATTRIB", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("CB_COLOR_ATTRIB", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("TILE_MODE_INDEX"); @@ -460,17 +424,14 @@ void ParseColor0Attrib(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.force_dst_alpha_1.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseBlendControl(u32 value, bool begin_table) { +static void ParseBlendControl(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("CB_BLEND_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("CB_BLEND_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("COLOR_SRCBLEND"); @@ -529,17 +490,14 @@ void ParseBlendControl(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.disable_rop3.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseDepthRenderControl(u32 value, bool begin_table) { +static void ParseDepthRenderControl(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("DB_RENDER_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("DB_RENDER_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("DEPTH_CLEAR_ENABLE"); @@ -600,17 +558,14 @@ void ParseDepthRenderControl(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.decompress_enable.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseDepthControl(u32 value, bool begin_table) { +static void ParseDepthControl(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("STENCIL_ENABLE"); @@ -673,17 +628,14 @@ void ParseDepthControl(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.disable_color_writes_on_depth_pass.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseEqaa(u32 value, bool begin_table) { +static void ParseEqaa(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("MAX_ANCHOR_SAMPLES"); @@ -756,17 +708,14 @@ void ParseEqaa(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.enable_postz_overrasterization.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } -void ParseZInfo(u32 value, bool begin_table) { +static void ParseZInfo(u32 value) { auto const reg = reinterpret_cast(value); - if (!begin_table || - BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("FORMAT"); @@ -827,41 +776,40 @@ void ParseZInfo(u32 value, bool begin_table) { TableSetColumnIndex(1); Text("%X", reg.zrange_precision.Value()); - if (begin_table) { - EndTable(); - } + EndTable(); } } void CmdListViewer::OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body) { using namespace std::string_view_literals; -#define NOP_PAYLOAD \ - P(PUSH_MARKER, 0x68750001) \ - P(POP_MARKER, 0x68750002) \ - P(SET_MARKER, 0x68750003) \ - P(SET_VSHARP, 0x68750004) \ - P(SET_TSHARP, 0x68750005) \ - P(SET_SSHARP, 0x68750006) \ - P(ACB_SUBMIT_MRK, 0x68750013) \ - P(SET_USER_DATA, 0x6875000D) \ - P(PATCHED_FLIP, 0x68750776) \ - P(PREPARE_FLIP, 0x68750777) \ - P(PREPARE_FLIP_LABEL, 0x68750778) \ - P(PREPARE_FLIP_INTERRUPT, 0x68750780) \ - P(PREPARE_FLIP_INTERRUPT_LABEL, 0x68750781) \ - P(ALLOC_ALIGN8, 0x68753000) - - auto get_nop_payload_text = [](u32 const nop_payload) { + enum class NOP_PAYLOAD : u32 { + ACB_SUBMIT_MRK = 0x68750013, + ALLOC_ALIGN8 = 0x68753000, + PUSH_MARKER = 0x68750001, + SET_VSHARP = 0x68750004, + SET_TSHARP = 0x68750005, + SET_SSHARP = 0x68750006, + SET_USER_DATA = 0x6875000d, + }; + auto get_noppayload_text = [](NOP_PAYLOAD const nop_payload) { switch (nop_payload) { -#define P(name, value) \ - case value: \ - return #name##sv; - NOP_PAYLOAD -#undef P - default: - return ""sv; + case NOP_PAYLOAD::ACB_SUBMIT_MRK: + return "ACB_SUBMIT_MRK"sv; + case NOP_PAYLOAD::ALLOC_ALIGN8: + return "ALLOC_ALIGN8"sv; + case NOP_PAYLOAD::PUSH_MARKER: + return "PUSH_MARKER"sv; + case NOP_PAYLOAD::SET_VSHARP: + return "SET_VSHARP"sv; + case NOP_PAYLOAD::SET_TSHARP: + return "SET_TSHARP"sv; + case NOP_PAYLOAD::SET_SSHARP: + return "SET_SSHARP"sv; + case NOP_PAYLOAD::SET_USER_DATA: + return "SET_USER_DATA"sv; } + return ""sv; }; Separator(); @@ -874,7 +822,7 @@ void CmdListViewer::OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body) for (unsigned i = 0; i < pkt->header.count + 1; ++i) { Text("%02X: %08X", i, payload[i]); if ((payload[i] & 0xffff0000) == 0x68750000) { - const auto& e = get_nop_payload_text(payload[i]); + const auto& e = get_noppayload_text((NOP_PAYLOAD)payload[i]); if (!e.empty()) { SameLine(); Text("(%s)", e.data()); @@ -888,7 +836,7 @@ void CmdListViewer::OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* b Separator(); BeginGroup(); - // auto const* pkt = reinterpret_cast(header); + auto const* pkt = reinterpret_cast(header); Text("BASE_INDEX: %08X", body[0]); Text("ADDRESS0 : %08X", body[1]); Text("ADDRESS1 : %08X", body[2]); @@ -1077,31 +1025,20 @@ void CmdListViewer::OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* EndGroup(); } -CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump, - const std::vector& cmd_list, uintptr_t _base_addr, - std::string _name) - : frame_dump(_frame_dump), base_addr(_base_addr), name(std::move(_name)) { +CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector& cmd_list) + : parent(parent) { using namespace AmdGpu; cmdb_addr = (uintptr_t)cmd_list.data(); cmdb_size = cmd_list.size() * sizeof(u32); - cmdb_view_name = fmt::format("[GFX] Command buffer {}###cmdview_hex_{}", this->name, cmdb_addr); - cmdb_view.Open = false; - cmdb_view.ReadOnly = true; - auto const* pm4_hdr = reinterpret_cast(cmdb_addr); size_t processed_size = 0; size_t prev_offset = 0; - u32 batch_id = 0; std::string marker{}; - if (cmdb_size > 0) { - events.emplace_back(BatchBegin{.id = 0}); - } - while (processed_size < cmdb_size) { auto* next_pm4_hdr = GetNext(pm4_hdr, 1); auto processed_len = @@ -1111,28 +1048,22 @@ CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump, if (pm4_hdr->type == PM4Type3Header::TYPE) { auto const* pm4_t3 = reinterpret_cast(pm4_hdr); - auto opcode = pm4_t3->opcode; - if (opcode == PM4ItOpcode::Nop) { + if (pm4_t3->opcode == PM4ItOpcode::Nop) { auto const* it_body = reinterpret_cast(pm4_hdr + 1); - switch (it_body[0]) { - case PM4CmdNop::PayloadType::DebugSetMarker: + if (it_body[0] == 0x68750001) { marker = std::string{(char*)&it_body[1]}; - break; - case PM4CmdNop::PayloadType::DebugMarkerPush: - events.emplace_back(PushMarker{ - .name = std::string{(char*)&it_body[1]}, - }); - break; - case PM4CmdNop::PayloadType::DebugMarkerPop: - events.emplace_back(PopMarker{}); - break; - default: - break; } } - if (IsDrawCall(opcode)) { + if (pm4_t3->opcode == PM4ItOpcode::DispatchDirect || + pm4_t3->opcode == PM4ItOpcode::DispatchIndirect || + pm4_t3->opcode == PM4ItOpcode::DrawIndex2 || + pm4_t3->opcode == PM4ItOpcode::DrawIndexAuto || + pm4_t3->opcode == PM4ItOpcode::DrawIndexOffset2 || + pm4_t3->opcode == PM4ItOpcode::DrawIndexIndirect + // ... + ) { // All these commands are terminated by NOP at the end, so // it is safe to skip it to be even with CP // next_pm4_hdr = get_next(next_pm4_hdr, 1); @@ -1140,17 +1071,15 @@ CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump, // processed_len += nop_len; // processed_size += nop_len; - events.emplace_back(BatchInfo{ - .id = batch_id++, - .marker = marker, - .start_addr = prev_offset, - .end_addr = processed_size, - .command_addr = processed_size - processed_len, - .type = opcode, + batches.emplace_back(BatchInfo{ + marker, + prev_offset, + processed_size, + processed_size - processed_len, + pm4_t3->opcode, }); prev_offset = processed_size; marker.clear(); - events.emplace_back(BatchBegin{.id = batch_id}); } } @@ -1159,58 +1088,18 @@ CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump, // state batch (last) if (processed_size - prev_offset > 0) { - events.emplace_back(BatchInfo{ - .id = batch_id++, - .marker = marker, - .start_addr = prev_offset, - .end_addr = processed_size, - .command_addr = 0, - .type = static_cast(0xFF), + batches.emplace_back(BatchInfo{ + marker, + prev_offset, + processed_size, + 0, + static_cast(0xFF), }); } - if (!events.empty() && std::holds_alternative(events.back())) { - events.pop_back(); - } } void CmdListViewer::Draw() { - const auto& ctx = *GetCurrentContext(); - - if (batch_view.open) { - batch_view.Draw(); - } - for (auto it = extra_batch_view.begin(); it != extra_batch_view.end();) { - if (!it->open) { - it = extra_batch_view.erase(it); - continue; - } - it->Draw(); - ++it; - } - - if (cmdb_view.Open) { - MemoryEditor::Sizes s; - cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr); - SetNextWindowSize({s.WindowWidth, s.WindowWidth * 0.6f}, ImGuiCond_FirstUseEver); - SetNextWindowSizeConstraints({0.0f}, {s.WindowWidth, FLT_MAX}); - if (Begin(cmdb_view_name.c_str(), &cmdb_view.Open, - ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings)) { - cmdb_view.DrawContents((void*)cmdb_addr, cmdb_size, base_addr); - if (cmdb_view.ContentsWidthChanged) { - cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr); - SetWindowSize({s.WindowWidth, s.WindowWidth * 0.6f}); - } - } - End(); - } - - PushID(name.c_str()); if (BeginChild("cmd_queue", {})) { - - Checkbox("Group batches", &group_batches); - SameLine(); - Checkbox("Show markers", &show_markers); - char queue_name[32]{}; if (vqid < 254) { std::snprintf(queue_name, sizeof(queue_name), "%s %d", vqid > 254 ? "GFX" : "ASC", @@ -1222,240 +1111,113 @@ void CmdListViewer::Draw() { Text("queue : %s", queue_name); Text("base addr: %08llX", cmdb_addr); SameLine(); - if (SmallButton("Memory >")) { - cmdb_view.Open ^= true; + if (SmallButton(">")) { + parent->cmdb_view.Open ^= true; } Text("size : %04llX", cmdb_size); Separator(); - if (TreeNode("Batches")) { - int tree_depth = 0; - int tree_depth_show = 0; + char batch_hdr[128]; + for (int batch_id = 0; batch_id < batches.size(); ++batch_id) { + auto processed_size = 0ull; + auto const* pm4_hdr = + reinterpret_cast(cmdb_addr + batches[batch_id].start_addr); - u32 last_batch_id = ~0u; - if (!events.empty() && std::holds_alternative(events.back())) { - last_batch_id = std::get(events.back()).id; + sprintf(batch_hdr, "%08llX: batch-%03d | %s", cmdb_addr + batches[batch_id].start_addr, + batch_id, batches[batch_id].marker.c_str()); + + if (batch_id == batch_bp) { // highlight batch at breakpoint + PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f}); } - u32 batch_id = ~0u; - u32 current_highlight_batch = ~0u; + if (batches[batch_id].type == static_cast(0xFF) || + CollapsingHeader(batch_hdr)) { + auto const batch_sz = batches[batch_id].end_addr - batches[batch_id].start_addr; + while (processed_size < batch_sz) { + AmdGpu::PM4ItOpcode op{0xFFu}; - int id = 0; - PushID(0); - for (const auto& event : events) { - PopID(); - PushID(id++); + if (pm4_hdr->type == AmdGpu::PM4Type3Header::TYPE) { + auto const* pm4_t3 = + reinterpret_cast(pm4_hdr); + op = pm4_t3->opcode; - if (std::holds_alternative(event)) { - batch_id = std::get(event).id; - } + static char header_name[128]; + sprintf(header_name, "%08llX: %s", + cmdb_addr + batches[batch_id].start_addr + processed_size, + Gcn::GetOpCodeName((u32)op)); - if (show_markers) { - if (std::holds_alternative(event)) { - if (tree_depth_show >= tree_depth) { - auto& marker = std::get(event); - bool show = TreeNode(&event, "%s", marker.name.c_str()); - if (show) { - tree_depth_show++; - } - } - tree_depth++; - continue; - } - if (std::holds_alternative(event)) { - if (tree_depth_show >= tree_depth) { - tree_depth_show--; - TreePop(); - } - tree_depth--; - continue; - } - if (tree_depth_show < tree_depth) { - continue; - } - } + if (TreeNode(header_name)) { + bool just_opened = IsItemToggledOpen(); + if (BeginTable("split", 1)) { + TableNextColumn(); + Text("size: %d", pm4_hdr->count + 1); - if (!std::holds_alternative(event)) { - continue; - } - - auto& batch = std::get(event); - auto const* pm4_hdr = - reinterpret_cast(cmdb_addr + batch.start_addr); - - char batch_hdr[128]; - if (batch.type == static_cast(0xFF)) { - snprintf(batch_hdr, sizeof(batch_hdr), "State batch"); - } else if (!batch.marker.empty()) { - snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s | %s", - cmdb_addr + batch.start_addr, batch.id, - Gcn::GetOpCodeName(static_cast(batch.type)), - batch.marker.c_str()); - } else { - snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s", - cmdb_addr + batch.start_addr, batch.id, - Gcn::GetOpCodeName(static_cast(batch.type))); - } - - if (batch.id == batch_bp) { // highlight batch at breakpoint - PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f}); - } - if (batch.id == highlight_batch) { - PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); - } - - const auto open_batch_view = [&, this] { - if (frame_dump->regs.contains(batch.command_addr)) { - auto data = frame_dump->regs.at(batch.command_addr); - if (GetIO().KeyShift) { - auto& pop = extra_batch_view.emplace_back(); - pop.SetData(data, batch_id); - pop.open = true; - } else { - batch_view.SetData(data, batch_id); - batch_view.open = true; - } - } - }; - - bool show_batch_content = true; - - if (group_batches) { - show_batch_content = - CollapsingHeader(batch_hdr, ImGuiTreeNodeFlags_AllowOverlap); - SameLine(GetContentRegionAvail().x - 40.0f); - if (Button("->", {40.0f, 0.0f})) { - open_batch_view(); - } - } - - if (show_batch_content) { - auto processed_size = 0ull; - auto bb = ctx.LastItemData.Rect; - if (group_batches) { - Indent(); - } - auto const batch_sz = batch.end_addr - batch.start_addr; - - while (processed_size < batch_sz) { - AmdGpu::PM4ItOpcode op{0xFFu}; - - if (pm4_hdr->type == AmdGpu::PM4Type3Header::TYPE) { - auto const* pm4_t3 = - reinterpret_cast(pm4_hdr); - op = pm4_t3->opcode; - - char header_name[128]; - sprintf(header_name, "%08llX: %s", - cmdb_addr + batch.start_addr + processed_size, - Gcn::GetOpCodeName((u32)op)); - - bool open_pm4 = TreeNode(header_name); - if (!group_batches) { - if (IsDrawCall(op)) { - SameLine(GetContentRegionAvail().x - 40.0f); - if (Button("->", {40.0f, 0.0f})) { - open_batch_view(); - } - } - if (IsItemHovered() && ctx.IO.KeyShift) { - if (BeginTooltip()) { - Text("Batch %d", batch_id); - EndTooltip(); - } - } - } - if (open_pm4) { - if (IsItemToggledOpen()) { + if (just_opened) { // Editor - cmdb_view.GotoAddrAndHighlight( + parent->cmdb_view.GotoAddrAndHighlight( reinterpret_cast(pm4_hdr) - cmdb_addr, reinterpret_cast(pm4_hdr) - cmdb_addr + (pm4_hdr->count + 2) * 4); } - if (BeginTable("split", 1)) { - TableNextColumn(); - Text("size: %d", pm4_hdr->count + 1); + auto const* it_body = + reinterpret_cast(pm4_hdr + 1); - auto const* it_body = - reinterpret_cast(pm4_hdr + 1); - - switch (op) { - case AmdGpu::PM4ItOpcode::Nop: { - OnNop(pm4_t3, it_body); - break; - } - case AmdGpu::PM4ItOpcode::SetBase: { - OnSetBase(pm4_t3, it_body); - break; - } - case AmdGpu::PM4ItOpcode::SetContextReg: { - OnSetContextReg(pm4_t3, it_body); - break; - } - case AmdGpu::PM4ItOpcode::SetShReg: { - OnSetShReg(pm4_t3, it_body); - break; - } - case AmdGpu::PM4ItOpcode::DispatchDirect: { - OnDispatch(pm4_t3, it_body); - break; - } - default: { - auto const* payload = &it_body[0]; - for (unsigned i = 0; i < pm4_hdr->count + 1; ++i) { - Text("%02X: %08X", i, payload[i]); - } - } - } - - EndTable(); + switch (op) { + case AmdGpu::PM4ItOpcode::Nop: { + OnNop(pm4_t3, it_body); + break; } - TreePop(); + case AmdGpu::PM4ItOpcode::SetBase: { + OnSetBase(pm4_t3, it_body); + break; + } + case AmdGpu::PM4ItOpcode::SetContextReg: { + OnSetContextReg(pm4_t3, it_body); + break; + } + case AmdGpu::PM4ItOpcode::SetShReg: { + OnSetShReg(pm4_t3, it_body); + break; + } + case AmdGpu::PM4ItOpcode::DispatchDirect: { + OnDispatch(pm4_t3, it_body); + break; + } + default: { + auto const* payload = &it_body[0]; + for (unsigned i = 0; i < pm4_hdr->count + 1; ++i) { + Text("%02X: %08X", i, payload[i]); + } + } + } + + EndTable(); } - - } else { - Text(""); + TreePop(); } - - auto const* next_pm4_hdr = GetNext(pm4_hdr, 1); - auto const processed_len = reinterpret_cast(next_pm4_hdr) - - reinterpret_cast(pm4_hdr); - pm4_hdr = next_pm4_hdr; - processed_size += processed_len; + } else { + Text(""); } - if (group_batches) { - Unindent(); - }; - bb = {{0.0f, bb.Max.y}, ctx.LastItemData.Rect.Max}; - if (bb.Contains(ctx.IO.MousePos)) { - current_highlight_batch = batch.id; - } - } - - if (batch.id == highlight_batch) { - PopStyleColor(); - } - - if (batch.id == batch_bp) { - PopStyleColor(); - } - - if (batch.id == last_batch_id) { - Separator(); + auto const* next_pm4_hdr = GetNext(pm4_hdr, 1); + auto const processed_len = reinterpret_cast(next_pm4_hdr) - + reinterpret_cast(pm4_hdr); + pm4_hdr = next_pm4_hdr; + processed_size += processed_len; } } - PopID(); - highlight_batch = current_highlight_batch; + if (batch_id == batch_bp) { + PopStyleColor(); + } - TreePop(); + if (batch_id == batches.size() - 2) { + Separator(); + } } } EndChild(); - PopID(); } } // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/cmd_list.h b/src/core/devtools/widget/cmd_list.h index 971c8fffe..a6ecd9323 100644 --- a/src/core/devtools/widget/cmd_list.h +++ b/src/core/devtools/widget/cmd_list.h @@ -5,14 +5,10 @@ #pragma once -#include #include -#include -#include "common.h" #include "common/types.h" -#include "imgui_memory_editor.h" -#include "reg_view.h" +#include "video_core/buffer_cache/buffer_cache.h" namespace AmdGpu { union PM4Type3Header; @@ -23,50 +19,43 @@ namespace Core::Devtools::Widget { class FrameDumpViewer; -void ParsePolygonControl(u32 value, bool begin_table = true); -void ParseAaConfig(u32 value, bool begin_table = true); -void ParseViewportControl(u32 value, bool begin_table = true); -void ParseColorControl(u32 value, bool begin_table = true); -void ParseColor0Info(u32 value, bool begin_table = true); -void ParseColor0Attrib(u32 value, bool begin_table = true); -void ParseBlendControl(u32 value, bool begin_table = true); -void ParseDepthRenderControl(u32 value, bool begin_table = true); -void ParseDepthControl(u32 value, bool begin_table = true); -void ParseEqaa(u32 value, bool begin_table = true); -void ParseZInfo(u32 value, bool begin_table = true); - class CmdListViewer { + /* + * Generic PM4 header + */ + union PM4Header { + struct { + u32 reserved : 16; + u32 count : 14; + u32 type : 2; // PM4_TYPE + }; + u32 u32All; + }; + struct BatchInfo { + std::string marker{}; + size_t start_addr; + size_t end_addr; + size_t command_addr; + AmdGpu::PM4ItOpcode type; + bool bypass{false}; + }; - DebugStateType::FrameDump* frame_dump; - - uintptr_t base_addr; - std::string name; - std::vector events{}; + FrameDumpViewer* parent; + std::vector batches{}; uintptr_t cmdb_addr; size_t cmdb_size; - std::string cmdb_view_name; - MemoryEditor cmdb_view; - int batch_bp{-1}; int vqid{255}; - u32 highlight_batch{~0u}; - RegView batch_view; - std::vector extra_batch_view; - - static void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body); - static void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body); - static void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body); - static void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body); - static void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body); + void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body); + void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body); + void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body); + void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body); + void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body); public: - static void LoadConfig(const char* line); - static void SerializeConfig(ImGuiTextBuffer* buf); - - explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector& cmd_list, - uintptr_t base_addr = 0, std::string name = ""); + explicit CmdListViewer(FrameDumpViewer* parent, const std::vector& cmd_list); void Draw(); }; diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h deleted file mode 100644 index 701d16399..000000000 --- a/src/core/devtools/widget/common.h +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include - -#include "common/types.h" -#include "video_core/amdgpu/pm4_opcodes.h" - -namespace Core::Devtools::Widget { - -/* - * Generic PM4 header - */ -union PM4Header { - struct { - u32 reserved : 16; - u32 count : 14; - u32 type : 2; // PM4_TYPE - }; - u32 u32All; -}; - -struct PushMarker { - std::string name{}; -}; - -struct PopMarker {}; - -struct BatchBegin { - u32 id; -}; - -struct BatchInfo { - u32 id; - std::string marker{}; - size_t start_addr; - size_t end_addr; - size_t command_addr; - AmdGpu::PM4ItOpcode type; - bool bypass{false}; -}; - -using GPUEvent = std::variant; - -template -void DrawRow(const char* text, const char* fmt, Args... args) { - ImGui::TableNextColumn(); - ImGui::TextUnformatted(text); - ImGui::TableNextColumn(); - char buf[128]; - snprintf(buf, sizeof(buf), fmt, args...); - ImGui::TextUnformatted(buf); -} - -template -void DrawEnumRow(const char* text, T value) { - DrawRow(text, "%X (%s)", V(value), magic_enum::enum_name(value).data()); -} - -template -void DrawMultipleRow(const char* text, const char* fmt, V arg, Extra&&... extra_args) { - DrawRow(text, fmt, arg); - if constexpr (sizeof...(extra_args) > 0) { - DrawMultipleRow(std::forward(extra_args)...); - } -} - -template -static void DoTooltip(const char* str_id, Args&&... args) { - if (ImGui::BeginTooltip()) { - if (ImGui::BeginTable(str_id, 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { - DrawMultipleRow(std::forward(args)...); - ImGui::EndTable(); - } - ImGui::EndTooltip(); - } -} - -static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) { - using AmdGpu::PM4ItOpcode; - switch (opcode) { - case PM4ItOpcode::DrawIndex2: - case PM4ItOpcode::DrawIndexOffset2: - case PM4ItOpcode::DrawIndexAuto: - case PM4ItOpcode::DrawIndirect: - case PM4ItOpcode::DrawIndexIndirect: - case PM4ItOpcode::DispatchDirect: - case PM4ItOpcode::DispatchIndirect: - return true; - default: - return false; - } -} - -} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 29b5cb8ee..d27bab90a 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -36,8 +36,7 @@ static std::array small_int_to_str(const s32 i) { namespace Core::Devtools::Widget { -FrameDumpViewer::FrameDumpViewer(const FrameDump& _frame_dump) - : frame_dump(std::make_shared(_frame_dump)) { +FrameDumpViewer::FrameDumpViewer(FrameDump _frame_dump) : frame_dump(std::move(_frame_dump)) { static int unique_id = 0; id = unique_id++; @@ -45,19 +44,20 @@ FrameDumpViewer::FrameDumpViewer(const FrameDump& _frame_dump) selected_submit_num = 0; selected_queue_num2 = 0; - cmd_list_viewer.reserve(frame_dump->queues.size()); - for (const auto& cmd : frame_dump->queues) { - const auto fname = - fmt::format("{}_{}_{:02}_{:02}", id, magic_enum::enum_name(selected_queue_type), - selected_submit_num, selected_queue_num2); - cmd_list_viewer.emplace_back(frame_dump.get(), cmd.data, cmd.base_addr, fname); - if (cmd.type == QueueType::dcb && cmd.submit_num == 0 && cmd.num2 == 0) { - selected_cmd = static_cast(cmd_list_viewer.size() - 1); + cmd_list_viewer.reserve(frame_dump.queues.size()); + for (const auto& cmd : frame_dump.queues) { + cmd_list_viewer.emplace_back(this, cmd.data); + if (cmd.type == QueueType::dcb && cmd.submit_num == selected_submit_num && + cmd.num2 == selected_queue_num2) { + selected_cmd = cmd_list_viewer.size() - 1; } } + + cmdb_view.Open = false; + cmdb_view.ReadOnly = true; } -FrameDumpViewer::~FrameDumpViewer() = default; +FrameDumpViewer::~FrameDumpViewer() {} void FrameDumpViewer::Draw() { if (!is_open) { @@ -66,11 +66,11 @@ void FrameDumpViewer::Draw() { char name[32]; snprintf(name, sizeof(name), "Frame #%d dump", id); + static ImGuiID dock_id = ImHashStr("FrameDumpDock"); + SetNextWindowDockID(dock_id, ImGuiCond_Appearing); if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) { if (IsWindowAppearing()) { auto window = GetCurrentWindow(); - static ImGuiID dock_id = ImHashStr("FrameDumpDock"); - SetWindowDock(window, dock_id, ImGuiCond_Once | ImGuiCond_FirstUseEver); SetWindowSize(window, ImVec2{470.0f, 600.0f}); } BeginGroup(); @@ -89,30 +89,12 @@ void FrameDumpViewer::Draw() { EndCombo(); } SameLine(); - BeginDisabled(selected_cmd == -1); - if (SmallButton("Dump cmd")) { - auto now_time = fmt::localtime(std::time(nullptr)); - const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time, - magic_enum::enum_name(selected_queue_type), - selected_submit_num, selected_queue_num2); - Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write); - const auto& data = frame_dump->queues[selected_cmd].data; - if (file.IsOpen()) { - DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname)); - file.Write(data); - } else { - DebugState.ShowDebugMessage(fmt::format("Failed to save {}", fname)); - LOG_ERROR(Core, "Failed to open file {}", fname); - } - } - EndDisabled(); - TextEx("Submit num"); SameLine(); if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(), ImGuiComboFlags_WidthFitPreview)) { std::array available_submits{}; - for (const auto& cmd : frame_dump->queues) { + for (const auto& cmd : frame_dump.queues) { if (cmd.type == selected_queue_type) { available_submits[cmd.submit_num] = true; } @@ -137,7 +119,7 @@ void FrameDumpViewer::Draw() { if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(), ImGuiComboFlags_WidthFitPreview)) { std::array available_queues{}; - for (const auto& cmd : frame_dump->queues) { + for (const auto& cmd : frame_dump.queues) { if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) { available_queues[cmd.num2] = true; } @@ -152,16 +134,34 @@ void FrameDumpViewer::Draw() { } } if (selected) { - const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) { + const auto it = std::ranges::find_if(frame_dump.queues, [&](const auto& cmd) { return cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num && cmd.num2 == selected_queue_num2; }); - if (it != frame_dump->queues.end()) { - selected_cmd = static_cast(std::distance(frame_dump->queues.begin(), it)); + if (it != frame_dump.queues.end()) { + selected_cmd = std::distance(frame_dump.queues.begin(), it); } } EndCombo(); } + SameLine(); + BeginDisabled(selected_cmd == -1); + if (SmallButton("Dump cmd")) { + auto now_time = fmt::localtime(std::time(nullptr)); + const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time, + magic_enum::enum_name(selected_queue_type), + selected_submit_num, selected_queue_num2); + Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write); + auto& data = frame_dump.queues[selected_cmd].data; + if (file.IsOpen()) { + DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname)); + file.Write(data); + } else { + DebugState.ShowDebugMessage(fmt::format("Failed to save {}", fname)); + LOG_ERROR(Core, "Failed to open file {}", fname); + } + } + EndDisabled(); EndGroup(); if (selected_cmd != -1) { @@ -169,6 +169,21 @@ void FrameDumpViewer::Draw() { } } End(); + + if (cmdb_view.Open && selected_cmd != -1) { + auto& cmd = frame_dump.queues[selected_cmd].data; + auto cmd_size = cmd.size() * sizeof(u32); + MemoryEditor::Sizes s; + cmdb_view.CalcSizes(s, cmd_size, (size_t)cmd.data()); + SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX)); + + char name[64]; + snprintf(name, sizeof(name), "[GFX] Command buffer %d###cmdbuf_hex_%d", id, id); + if (Begin(name, &cmdb_view.Open, ImGuiWindowFlags_NoScrollbar)) { + cmdb_view.DrawContents(cmd.data(), cmd_size, (size_t)cmd.data()); + } + End(); + } } } // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/frame_dump.h b/src/core/devtools/widget/frame_dump.h index 2b3ff2411..d9d11f825 100644 --- a/src/core/devtools/widget/frame_dump.h +++ b/src/core/devtools/widget/frame_dump.h @@ -8,6 +8,7 @@ #include "cmd_list.h" #include "core/debug_state.h" +#include "imgui_memory_editor.h" namespace Core::Devtools::Widget { @@ -16,10 +17,11 @@ class CmdListViewer; class FrameDumpViewer { friend class CmdListViewer; - std::shared_ptr frame_dump; + DebugStateType::FrameDump frame_dump; int id; std::vector cmd_list_viewer; + MemoryEditor cmdb_view; DebugStateType::QueueType selected_queue_type; s32 selected_submit_num; @@ -29,7 +31,7 @@ class FrameDumpViewer { public: bool is_open = true; - explicit FrameDumpViewer(const DebugStateType::FrameDump& frame_dump); + explicit FrameDumpViewer(DebugStateType::FrameDump frame_dump); ~FrameDumpViewer(); diff --git a/src/core/devtools/widget/reg_popup.cpp b/src/core/devtools/widget/reg_popup.cpp deleted file mode 100644 index d012437c3..000000000 --- a/src/core/devtools/widget/reg_popup.cpp +++ /dev/null @@ -1,182 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "reg_popup.h" - -#include -#include -#include - -#include "cmd_list.h" -#include "common.h" - -using namespace ImGui; -using magic_enum::enum_name; - -namespace Core::Devtools::Widget { - -void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) { - if (BeginTable("COLOR_BUFFER", 2, ImGuiTableFlags_Borders)) { - TableNextRow(); - - // clang-format off - - DrawMultipleRow( - "BASE_ADDR", "%X", buffer.base_address, - "PITCH.TILE_MAX", "%X", buffer.pitch.tile_max, - "PITCH.FMASK_TILE_MAX", "%X", buffer.pitch.fmask_tile_max, - "SLICE.TILE_MAX", "%X", buffer.slice.tile_max, - "VIEW.SLICE_START", "%X", buffer.view.slice_start, - "VIEW.SLICE_MAX", "%X", buffer.view.slice_max - ); - - TableNextRow(); - TableNextColumn(); - if (TreeNode("Color0Info")) { - TableNextRow(); - TableNextColumn(); - ParseColor0Info(buffer.info.u32all, false); - TreePop(); - } - - TableNextRow(); - TableNextColumn(); - if (TreeNode("Color0Attrib")) { - TableNextRow(); - TableNextColumn(); - ParseColor0Attrib(buffer.attrib.u32all, false); - TreePop(); - } - - TableNextRow(); - DrawMultipleRow( - "CMASK_BASE_EXT", "%X", buffer.cmask_base_address, - "FMASK_BASE_EXT", "%X", buffer.fmask_base_address, - "FMASK_SLICE.TILE_MAX", "%X", buffer.fmask_slice.tile_max, - "CLEAR_WORD0", "%X", buffer.clear_word0, - "CLEAR_WORD1", "%X", buffer.clear_word1 - ); - - DrawMultipleRow( - "Pitch()", "%X", buffer.Pitch(), - "Height()", "%X", buffer.Height(), - "Address()", "%X", buffer.Address(), - "CmaskAddress", "%X", buffer.CmaskAddress(), - "FmaskAddress", "%X", buffer.FmaskAddress(), - "NumSamples()", "%X", buffer.NumSamples(), - "NumSlices()", "%X", buffer.NumSlices(), - "GetColorSliceSize()", "%X", buffer.GetColorSliceSize() - ); - - auto tiling_mode = buffer.GetTilingMode(); - auto num_format = buffer.NumFormat(); - DrawEnumRow("GetTilingMode()", tiling_mode); - DrawRow("IsTiled()", "%X", buffer.IsTiled()); - DrawEnumRow("NumFormat()", num_format); - - // clang-format on - - EndTable(); - } -} - -void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) { - const auto& [depth_buffer, depth_control] = depth_data; - - SeparatorText("Depth buffer"); - - if (BeginTable("DEPTH_BUFFER", 2, ImGuiTableFlags_Borders)) { - TableNextRow(); - - // clang-format off - DrawEnumRow("Z_INFO.FORMAT", depth_buffer.z_info.format.Value()); - DrawMultipleRow( - "Z_INFO.NUM_SAMPLES", "%X", depth_buffer.z_info.num_samples, - "Z_INFO.TILE_SPLIT", "%X", depth_buffer.z_info.tile_split, - "Z_INFO.TILE_MODE_INDEX", "%X", depth_buffer.z_info.tile_mode_index, - "Z_INFO.DECOMPRESS_ON_N_ZPLANES", "%X", depth_buffer.z_info.decompress_on_n_zplanes, - "Z_INFO.ALLOW_EXPCLEAR", "%X", depth_buffer.z_info.allow_expclear, - "Z_INFO.READ_SIZE", "%X", depth_buffer.z_info.read_size, - "Z_INFO.TILE_SURFACE_EN", "%X", depth_buffer.z_info.tile_surface_en, - "Z_INFO.CLEAR_DISALLOWED", "%X", depth_buffer.z_info.clear_disallowed, - "Z_INFO.ZRANGE_PRECISION", "%X", depth_buffer.z_info.zrange_precision - ); - - DrawEnumRow("STENCIL_INFO.FORMAT", depth_buffer.stencil_info.format.Value()); - - DrawMultipleRow( - "Z_READ_BASE", "%X", depth_buffer.z_read_base, - "STENCIL_READ_BASE", "%X", depth_buffer.stencil_read_base, - "Z_WRITE_BASE", "%X", depth_buffer.z_write_base, - "STENCIL_WRITE_BASE", "%X", depth_buffer.stencil_write_base, - "DEPTH_SIZE.PITCH_TILE_MAX", "%X", depth_buffer.depth_size.pitch_tile_max, - "DEPTH_SIZE.HEIGHT_TILE_MAX", "%X", depth_buffer.depth_size.height_tile_max, - "DEPTH_SLICE.TILE_MAX", "%X", depth_buffer.depth_slice.tile_max, - "Pitch()", "%X", depth_buffer.Pitch(), - "Height()", "%X", depth_buffer.Height(), - "Address()", "%X", depth_buffer.Address(), - "NumSamples()", "%X", depth_buffer.NumSamples(), - "NumBits()", "%X", depth_buffer.NumBits(), - "GetDepthSliceSize()", "%X", depth_buffer.GetDepthSliceSize() - ); - // clang-format on - EndTable(); - } - SeparatorText("Depth control"); - if (BeginTable("DEPTH_CONTROL", 2, ImGuiTableFlags_Borders)) { - TableNextRow(); - - // clang-format off - DrawMultipleRow( - "STENCIL_ENABLE", "%X", depth_control.stencil_enable, - "DEPTH_ENABLE", "%X", depth_control.depth_enable, - "DEPTH_WRITE_ENABLE", "%X", depth_control.depth_write_enable, - "DEPTH_BOUNDS_ENABLE", "%X", depth_control.depth_bounds_enable - ); - DrawEnumRow("DEPTH_FUNC", depth_control.depth_func.Value()); - DrawRow("BACKFACE_ENABLE", "%X", depth_control.backface_enable); - DrawEnumRow("STENCIL_FUNC", depth_control.stencil_ref_func.Value()); - DrawEnumRow("STENCIL_FUNC_BF", depth_control.stencil_bf_func.Value()); - DrawMultipleRow( - "ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", "%X", depth_control.enable_color_writes_on_depth_fail, - "DISABLE_COLOR_WRITES_ON_DEPTH_PASS", "%X", depth_control.disable_color_writes_on_depth_pass - ); - // clang-format on - - EndTable(); - } -} - -RegPopup::RegPopup() { - static int unique_id = 0; - id = unique_id++; -} - -void RegPopup::SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id) { - this->data = color_buffer; - this->title = fmt::format("Batch #{} CB #{}", batch_id, cb_id); -} - -void RegPopup::SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer, - AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id) { - this->data = std::make_tuple(depth_buffer, depth_control); - this->title = fmt::format("Batch #{} Depth", batch_id); -} - -void RegPopup::Draw() { - - char name[128]; - snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id); - - SetNextWindowSize({250.0f, 300.0f}, ImGuiCond_FirstUseEver); - if (Begin(name, &open, ImGuiWindowFlags_NoSavedSettings)) { - if (const auto* buffer = std::get_if(&data)) { - DrawColorBuffer(*buffer); - } else if (const auto* depth_data = std::get_if(&data)) { - DrawDepthBuffer(*depth_data); - } - } - End(); -} - -} // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/reg_popup.h b/src/core/devtools/widget/reg_popup.h deleted file mode 100644 index ba4224d73..000000000 --- a/src/core/devtools/widget/reg_popup.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "common/types.h" -#include "video_core/renderer_vulkan/liverpool_to_vk.h" - -namespace Core::Devtools::Widget { - -class RegPopup { - int id; - - using DepthBuffer = std::tuple; - - std::variant data; - std::string title{}; - - void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer); - - void DrawDepthBuffer(const DepthBuffer& depth_data); - -public: - bool open = false; - - RegPopup(); - - void SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id); - - void SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer, - AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id); - - void Draw(); -}; - -} // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp deleted file mode 100644 index 2e8bb8f54..000000000 --- a/src/core/devtools/widget/reg_view.cpp +++ /dev/null @@ -1,305 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "common/io_file.h" -#include "core/devtools/options.h" -#include "imgui_internal.h" -#include "reg_view.h" - -#if defined(_WIN32) -#define popen _popen -#define pclose _pclose -#endif - -using namespace ImGui; -using magic_enum::enum_name; - -static std::optional exec_cli(const char* cli) { - std::array buffer{}; - std::string output; - const auto f = popen(cli, "r"); - if (!f) { - pclose(f); - return {}; - } - while (fgets(buffer.data(), buffer.size(), f)) { - output += buffer.data(); - } - pclose(f); - return output; -} - -namespace Core::Devtools::Widget { - -void RegView::ProcessShader(int shader_id) { - auto shader = data.stages[shader_id]; - - std::string shader_dis; - - if (Options.disassembly_cli.empty()) { - shader_dis = "No disassembler set"; - } else { - auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; - - constexpr std::string_view src_arg = "{src}"; - std::string cli = Options.disassembly_cli; - const auto pos = cli.find(src_arg); - if (pos == std::string::npos) { - DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument"); - } else { - cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); - Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); - file.Write(shader.code); - file.Close(); - - auto result = exec_cli(cli.c_str()); - shader_dis = result.value_or("Could not disassemble shader"); - if (shader_dis.empty()) { - shader_dis = "Disassembly empty or failed"; - } - - std::filesystem::remove(bin_path); - } - } - - MemoryEditor hex_view; - hex_view.Open = true; - hex_view.ReadOnly = true; - hex_view.Cols = 16; - hex_view.OptShowAscii = false; - - TextEditor dis_view; - dis_view.SetPalette(TextEditor::GetDarkPalette()); - dis_view.SetReadOnly(true); - dis_view.SetText(shader_dis); - - ShaderCache cache{ - .hex_view = hex_view, - .dis_view = dis_view, - .user_data = shader.user_data.user_data, - }; - shader_decomp.emplace(shader_id, std::move(cache)); -} -void RegView::SelectShader(int id) { - selected_shader = id; - if (!shader_decomp.contains(id)) { - ProcessShader(id); - } -} - -void RegView::DrawRegs() { - const auto& regs = data.regs; - - if (BeginTable("REGS", 2, ImGuiTableFlags_Borders)) { - - auto& s = regs.screen_scissor; - DrawRow("Scissor", "(%d, %d, %d, %d)", s.top_left_x, s.top_left_y, s.bottom_right_x, - s.bottom_right_y); - - auto cc_mode = regs.color_control.mode.Value(); - DrawRow("Color control", "%X (%s)", cc_mode, enum_name(cc_mode).data()); - - const auto open_new_popup = [&](int cb, auto... args) { - if (GetIO().KeyShift) { - auto& pop = extra_reg_popup.emplace_back(); - pop.SetData(args...); - pop.open = true; - } else if (last_selected_cb == cb && default_reg_popup.open) { - default_reg_popup.open = false; - } else { - last_selected_cb = cb; - default_reg_popup.SetData(args...); - if (!default_reg_popup.open) { - default_reg_popup.open = true; - auto popup_pos = - GetCurrentContext()->LastItemData.Rect.Max + ImVec2(5.0f, 0.0f); - SetNextWindowPos(popup_pos, ImGuiCond_Always); - default_reg_popup.Draw(); - } - } - }; - - for (int cb = 0; cb < AmdGpu::Liverpool::NumColorBuffers; ++cb) { - PushID(cb); - - TableNextRow(); - TableNextColumn(); - - const auto& buffer = regs.color_buffers[cb]; - - Text("Color buffer %d", cb); - TableNextColumn(); - if (!buffer || !regs.color_target_mask.GetMask(cb)) { - TextUnformatted("N/A"); - } else { - const char* text = last_selected_cb == cb && default_reg_popup.open ? "x" : "->"; - if (SmallButton(text)) { - open_new_popup(cb, buffer, batch_id, cb); - } - } - - PopID(); - } - - TableNextRow(); - TableNextColumn(); - TextUnformatted("Depth buffer"); - TableNextColumn(); - if (regs.depth_buffer.Address() == 0 || !regs.depth_control.depth_enable) { - TextUnformatted("N/A"); - } else { - constexpr auto depth_id = 0xF3; - const char* text = last_selected_cb == depth_id && default_reg_popup.open ? "x" : "->"; - if (SmallButton(text)) { - open_new_popup(depth_id, regs.depth_buffer, regs.depth_control, batch_id); - } - } - - EndTable(); - } -} - -RegView::RegView() { - static int unique_id = 0; - id = unique_id++; - - char name[128]; - snprintf(name, sizeof(name), "BatchView###reg_dump_%d", id); - SetNextWindowPos({400.0f, 200.0f}); - SetNextWindowSize({450.0f, 500.0f}); - ImGuiID root_dock_id; - Begin(name); - { - char dock_name[64]; - snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id); - root_dock_id = ImHashStr(dock_name); - DockSpace(root_dock_id); - } - End(); - - ImGuiID up1, down1; - - DockBuilderRemoveNodeChildNodes(root_dock_id); - DockBuilderSplitNode(root_dock_id, ImGuiDir_Up, 0.2f, &up1, &down1); - - snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id); - DockBuilderDockWindow(name, up1); - - snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id); - DockBuilderDockWindow(name, down1); - - snprintf(name, sizeof(name), "Disassembly###reg_dump_%d/disassembly", id); - DockBuilderDockWindow(name, down1); - - DockBuilderFinish(root_dock_id); -} - -void RegView::SetData(DebugStateType::RegDump data, u32 batch_id) { - this->data = std::move(data); - this->batch_id = batch_id; - // clear cache - selected_shader = -1; - shader_decomp.clear(); - default_reg_popup.open = false; - extra_reg_popup.clear(); -} - -void RegView::Draw() { - - char name[128]; - snprintf(name, sizeof(name), "BatchView %u###reg_dump_%d", batch_id, id); - if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) { - const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"}; - - if (BeginMenuBar()) { - if (BeginMenu("Stage")) { - for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; i++) { - if (data.regs.stage_enable.IsStageEnabled(i)) { - bool selected = selected_shader == i; - if (Selectable(names[i], &selected)) { - SelectShader(i); - } - } - } - ImGui::EndMenu(); - } - if (BeginMenu("Windows")) { - Checkbox("Registers", &show_registers); - Checkbox("User data", &show_user_data); - Checkbox("Disassembly", &show_disassembly); - ImGui::EndMenu(); - } - EndMenuBar(); - } - - char dock_name[64]; - snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id); - auto root_dock_id = ImHashStr(dock_name); - DockSpace(root_dock_id); - } - End(); - - auto get_shader = [&]() -> ShaderCache* { - auto shader_cache = shader_decomp.find(selected_shader); - if (shader_cache == shader_decomp.end()) { - return nullptr; - } - return &shader_cache->second; - }; - - if (show_user_data) { - snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id); - if (Begin(name, &show_user_data)) { - auto shader = get_shader(); - if (!shader) { - Text("Select a stage"); - } else { - shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size()); - } - } - End(); - } - - if (show_disassembly) { - snprintf(name, sizeof(name), "Disassembly###reg_dump_%d/disassembly", id); - if (Begin(name, &show_disassembly)) { - auto shader = get_shader(); - if (!shader) { - Text("Select a stage"); - } else { - shader->dis_view.Render("Disassembly", GetContentRegionAvail()); - } - } - End(); - } - - if (show_registers) { - snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id); - if (Begin(name, &show_registers)) { - DrawRegs(); - } - End(); - } - - if (default_reg_popup.open) { - default_reg_popup.Draw(); - } - for (auto it = extra_reg_popup.begin(); it != extra_reg_popup.end();) { - if (!it->open) { - it = extra_reg_popup.erase(it); - continue; - } - it->Draw(); - ++it; - } -} - -} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/reg_view.h b/src/core/devtools/widget/reg_view.h deleted file mode 100644 index 67ab1e04f..000000000 --- a/src/core/devtools/widget/reg_view.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once -#include "core/debug_state.h" -#include "imgui_memory_editor.h" -#include "reg_popup.h" -#include "text_editor.h" - -namespace Core::Devtools::Widget { - -struct ShaderCache { - MemoryEditor hex_view; - TextEditor dis_view; - Vulkan::Liverpool::UserData user_data; -}; - -class RegView { - int id; - - DebugStateType::RegDump data; - u32 batch_id{~0u}; - - std::unordered_map shader_decomp; - int selected_shader{-1}; - RegPopup default_reg_popup; - int last_selected_cb{-1}; - std::vector extra_reg_popup; - - bool show_registers{true}; - bool show_user_data{true}; - bool show_disassembly{true}; - - void ProcessShader(int shader_id); - - void SelectShader(int shader_id); - - void DrawRegs(); - -public: - bool open = false; - - RegView(); - - void SetData(DebugStateType::RegDump data, u32 batch_id); - - void Draw(); -}; - -} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/text_editor.cpp b/src/core/devtools/widget/text_editor.cpp deleted file mode 100644 index f447e45a2..000000000 --- a/src/core/devtools/widget/text_editor.cpp +++ /dev/null @@ -1,2334 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2017 BalazsJako -// SPDX-License-Identifier: MIT - -// source: https://github.com/BalazsJako/ImGuiColorTextEdit - -#include -#include -#include -#include -#include - -#include "text_editor.h" - -#define IMGUI_DEFINE_MATH_OPERATORS -#include "imgui.h" // for imGui::GetCurrentWindow() - -// TODO -// - multiline comments vs single-line: latter is blocking start of a ML - -namespace Core::Devtools::Widget { - -template -bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) { - for (; first1 != last1 && first2 != last2; ++first1, ++first2) { - if (!p(*first1, *first2)) - return false; - } - return first1 == last1 && first2 == last2; -} - -TextEditor::TextEditor() - : mLineSpacing(1.0f), mUndoIndex(0), mTabSize(4), mOverwrite(false), mReadOnly(false), - mWithinRender(false), mScrollToCursor(false), mScrollToTop(false), mTextChanged(false), - mColorizerEnabled(true), mTextStart(20.0f), mLeftMargin(10), mCursorPositionChanged(false), - mColorRangeMin(0), mColorRangeMax(0), mSelectionMode(SelectionMode::Normal), - mCheckComments(true), mLastClick(-1.0f), mHandleKeyboardInputs(true), - mHandleMouseInputs(true), mIgnoreImGuiChild(false), mShowWhitespaces(true), - mStartTime(std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count()) { - SetPalette(GetDarkPalette()); - mLines.push_back(Line()); -} - -TextEditor::~TextEditor() {} - -void TextEditor::SetLanguageDefinition(const LanguageDefinition& aLanguageDef) { - mLanguageDefinition = aLanguageDef; - mRegexList.clear(); - - for (auto& r : mLanguageDefinition.mTokenRegexStrings) - mRegexList.push_back( - std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second)); - - Colorize(); -} - -void TextEditor::SetPalette(const Palette& aValue) { - mPaletteBase = aValue; -} - -std::string TextEditor::GetText(const Coordinates& aStart, const Coordinates& aEnd) const { - std::string result; - - auto lstart = aStart.mLine; - auto lend = aEnd.mLine; - auto istart = GetCharacterIndex(aStart); - auto iend = GetCharacterIndex(aEnd); - size_t s = 0; - - for (size_t i = lstart; i < lend; i++) - s += mLines[i].size(); - - result.reserve(s + s / 8); - - while (istart < iend || lstart < lend) { - if (lstart >= (int)mLines.size()) - break; - - auto& line = mLines[lstart]; - if (istart < (int)line.size()) { - result += line[istart].mChar; - istart++; - } else { - istart = 0; - ++lstart; - result += '\n'; - } - } - - return result; -} - -TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const { - return SanitizeCoordinates(mState.mCursorPosition); -} - -TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates& aValue) const { - auto line = aValue.mLine; - auto column = aValue.mColumn; - if (line >= (int)mLines.size()) { - if (mLines.empty()) { - line = 0; - column = 0; - } else { - line = (int)mLines.size() - 1; - column = GetLineMaxColumn(line); - } - return Coordinates(line, column); - } else { - column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line)); - return Coordinates(line, column); - } -} - -// https://en.wikipedia.org/wiki/UTF-8 -// We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code -// sequence (non-10xxxxxx code) -static int UTF8CharLength(TextEditor::Char c) { - if ((c & 0xFE) == 0xFC) - return 6; - if ((c & 0xFC) == 0xF8) - return 5; - if ((c & 0xF8) == 0xF0) - return 4; - else if ((c & 0xF0) == 0xE0) - return 3; - else if ((c & 0xE0) == 0xC0) - return 2; - return 1; -} - -// "Borrowed" from ImGui source -static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) { - if (c < 0x80) { - buf[0] = (char)c; - return 1; - } - if (c < 0x800) { - if (buf_size < 2) - return 0; - buf[0] = (char)(0xc0 + (c >> 6)); - buf[1] = (char)(0x80 + (c & 0x3f)); - return 2; - } - if (c >= 0xdc00 && c < 0xe000) { - return 0; - } - if (c >= 0xd800 && c < 0xdc00) { - if (buf_size < 4) - return 0; - buf[0] = (char)(0xf0 + (c >> 18)); - buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); - buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); - buf[3] = (char)(0x80 + ((c) & 0x3f)); - return 4; - } - // else if (c < 0x10000) - { - if (buf_size < 3) - return 0; - buf[0] = (char)(0xe0 + (c >> 12)); - buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); - buf[2] = (char)(0x80 + ((c) & 0x3f)); - return 3; - } -} - -void TextEditor::Advance(Coordinates& aCoordinates) const { - if (aCoordinates.mLine < (int)mLines.size()) { - auto& line = mLines[aCoordinates.mLine]; - auto cindex = GetCharacterIndex(aCoordinates); - - if (cindex + 1 < (int)line.size()) { - auto delta = UTF8CharLength(line[cindex].mChar); - cindex = std::min(cindex + delta, (int)line.size() - 1); - } else { - ++aCoordinates.mLine; - cindex = 0; - } - aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex); - } -} - -void TextEditor::DeleteRange(const Coordinates& aStart, const Coordinates& aEnd) { - ASSERT(aEnd >= aStart); - ASSERT(!mReadOnly); - - // printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn); - - if (aEnd == aStart) - return; - - auto start = GetCharacterIndex(aStart); - auto end = GetCharacterIndex(aEnd); - - if (aStart.mLine == aEnd.mLine) { - auto& line = mLines[aStart.mLine]; - auto n = GetLineMaxColumn(aStart.mLine); - if (aEnd.mColumn >= n) - line.erase(line.begin() + start, line.end()); - else - line.erase(line.begin() + start, line.begin() + end); - } else { - auto& firstLine = mLines[aStart.mLine]; - auto& lastLine = mLines[aEnd.mLine]; - - firstLine.erase(firstLine.begin() + start, firstLine.end()); - lastLine.erase(lastLine.begin(), lastLine.begin() + end); - - if (aStart.mLine < aEnd.mLine) - firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); - - if (aStart.mLine < aEnd.mLine) - RemoveLine(aStart.mLine + 1, aEnd.mLine + 1); - } - - mTextChanged = true; -} - -int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char* aValue) { - ASSERT(!mReadOnly); - - int cindex = GetCharacterIndex(aWhere); - int totalLines = 0; - while (*aValue != '\0') { - ASSERT(!mLines.empty()); - - if (*aValue == '\r') { - // skip - ++aValue; - } else if (*aValue == '\n') { - if (cindex < (int)mLines[aWhere.mLine].size()) { - auto& newLine = InsertLine(aWhere.mLine + 1); - auto& line = mLines[aWhere.mLine]; - newLine.insert(newLine.begin(), line.begin() + cindex, line.end()); - line.erase(line.begin() + cindex, line.end()); - } else { - InsertLine(aWhere.mLine + 1); - } - ++aWhere.mLine; - aWhere.mColumn = 0; - cindex = 0; - ++totalLines; - ++aValue; - } else { - auto& line = mLines[aWhere.mLine]; - auto d = UTF8CharLength(*aValue); - while (d-- > 0 && *aValue != '\0') - line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default)); - ++aWhere.mColumn; - } - - mTextChanged = true; - } - - return totalLines; -} - -void TextEditor::AddUndo(UndoRecord& aValue) { - ASSERT(!mReadOnly); - // printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d] (@%d.%d)\n", - // aValue.mBefore.mCursorPosition.mLine, aValue.mBefore.mCursorPosition.mColumn, - // aValue.mAdded.c_str(), aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn, - // aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn, aValue.mRemoved.c_str(), - // aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn, aValue.mRemovedEnd.mLine, - // aValue.mRemovedEnd.mColumn, aValue.mAfter.mCursorPosition.mLine, - // aValue.mAfter.mCursorPosition.mColumn - // ); - - mUndoBuffer.resize((size_t)(mUndoIndex + 1)); - mUndoBuffer.back() = aValue; - ++mUndoIndex; -} - -TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition) const { - ImVec2 origin = ImGui::GetCursorScreenPos(); - ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y); - - int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y)); - - int columnCoord = 0; - - if (lineNo >= 0 && lineNo < (int)mLines.size()) { - auto& line = mLines.at(lineNo); - - int columnIndex = 0; - float columnX = 0.0f; - - while ((size_t)columnIndex < line.size()) { - float columnWidth = 0.0f; - - if (line[columnIndex].mChar == '\t') { - float spaceSize = - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x; - float oldX = columnX; - float newColumnX = - (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * - (float(mTabSize) * spaceSize); - columnWidth = newColumnX - oldX; - if (mTextStart + columnX + columnWidth * 0.5f > local.x) - break; - columnX = newColumnX; - columnCoord = (columnCoord / mTabSize) * mTabSize + mTabSize; - columnIndex++; - } else { - char buf[7]; - auto d = UTF8CharLength(line[columnIndex].mChar); - int i = 0; - while (i < 6 && d-- > 0) - buf[i++] = line[columnIndex++].mChar; - buf[i] = '\0'; - columnWidth = - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x; - if (mTextStart + columnX + columnWidth * 0.5f > local.x) - break; - columnX += columnWidth; - columnCoord++; - } - } - } - - return SanitizeCoordinates(Coordinates(lineNo, columnCoord)); -} - -TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates& aFrom) const { - Coordinates at = aFrom; - if (at.mLine >= (int)mLines.size()) - return at; - - auto& line = mLines[at.mLine]; - auto cindex = GetCharacterIndex(at); - - if (cindex >= (int)line.size()) - return at; - - while (cindex > 0 && isspace(line[cindex].mChar)) - --cindex; - - auto cstart = (PaletteIndex)line[cindex].mColorIndex; - while (cindex > 0) { - auto c = line[cindex].mChar; - if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx - { - if (c <= 32 && isspace(c)) { - cindex++; - break; - } - if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex) - break; - } - --cindex; - } - return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); -} - -TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates& aFrom) const { - Coordinates at = aFrom; - if (at.mLine >= (int)mLines.size()) - return at; - - auto& line = mLines[at.mLine]; - auto cindex = GetCharacterIndex(at); - - if (cindex >= (int)line.size()) - return at; - - bool prevspace = (bool)isspace(line[cindex].mChar); - auto cstart = (PaletteIndex)line[cindex].mColorIndex; - while (cindex < (int)line.size()) { - auto c = line[cindex].mChar; - auto d = UTF8CharLength(c); - if (cstart != (PaletteIndex)line[cindex].mColorIndex) - break; - - if (prevspace != !!isspace(c)) { - if (isspace(c)) - while (cindex < (int)line.size() && isspace(line[cindex].mChar)) - ++cindex; - break; - } - cindex += d; - } - return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex)); -} - -TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates& aFrom) const { - Coordinates at = aFrom; - if (at.mLine >= (int)mLines.size()) - return at; - - // skip to the next non-word character - auto cindex = GetCharacterIndex(aFrom); - bool isword = false; - bool skip = false; - if (cindex < (int)mLines[at.mLine].size()) { - auto& line = mLines[at.mLine]; - isword = isalnum(line[cindex].mChar); - skip = isword; - } - - while (!isword || skip) { - if (at.mLine >= mLines.size()) { - auto l = std::max(0, (int)mLines.size() - 1); - return Coordinates(l, GetLineMaxColumn(l)); - } - - auto& line = mLines[at.mLine]; - if (cindex < (int)line.size()) { - isword = isalnum(line[cindex].mChar); - - if (isword && !skip) - return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); - - if (!isword) - skip = false; - - cindex++; - } else { - cindex = 0; - ++at.mLine; - skip = false; - isword = false; - } - } - - return at; -} - -int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const { - if (aCoordinates.mLine >= mLines.size()) - return -1; - auto& line = mLines[aCoordinates.mLine]; - int c = 0; - int i = 0; - for (; i < line.size() && c < aCoordinates.mColumn;) { - if (line[i].mChar == '\t') - c = (c / mTabSize) * mTabSize + mTabSize; - else - ++c; - i += UTF8CharLength(line[i].mChar); - } - return i; -} - -int TextEditor::GetCharacterColumn(int aLine, int aIndex) const { - if (aLine >= mLines.size()) - return 0; - auto& line = mLines[aLine]; - int col = 0; - int i = 0; - while (i < aIndex && i < (int)line.size()) { - auto c = line[i].mChar; - i += UTF8CharLength(c); - if (c == '\t') - col = (col / mTabSize) * mTabSize + mTabSize; - else - col++; - } - return col; -} - -int TextEditor::GetLineCharacterCount(int aLine) const { - if (aLine >= mLines.size()) - return 0; - auto& line = mLines[aLine]; - int c = 0; - for (unsigned i = 0; i < line.size(); c++) - i += UTF8CharLength(line[i].mChar); - return c; -} - -int TextEditor::GetLineMaxColumn(int aLine) const { - if (aLine >= mLines.size()) - return 0; - auto& line = mLines[aLine]; - int col = 0; - for (unsigned i = 0; i < line.size();) { - auto c = line[i].mChar; - if (c == '\t') - col = (col / mTabSize) * mTabSize + mTabSize; - else - col++; - i += UTF8CharLength(c); - } - return col; -} - -bool TextEditor::IsOnWordBoundary(const Coordinates& aAt) const { - if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0) - return true; - - auto& line = mLines[aAt.mLine]; - auto cindex = GetCharacterIndex(aAt); - if (cindex >= (int)line.size()) - return true; - - if (mColorizerEnabled) - return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex; - - return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar); -} - -void TextEditor::RemoveLine(int aStart, int aEnd) { - ASSERT(!mReadOnly); - ASSERT(aEnd >= aStart); - ASSERT(mLines.size() > (size_t)(aEnd - aStart)); - - ErrorMarkers etmp; - for (auto& i : mErrorMarkers) { - ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first, i.second); - if (e.first >= aStart && e.first <= aEnd) - continue; - etmp.insert(e); - } - mErrorMarkers = std::move(etmp); - - Breakpoints btmp; - for (auto i : mBreakpoints) { - if (i >= aStart && i <= aEnd) - continue; - btmp.insert(i >= aStart ? i - 1 : i); - } - mBreakpoints = std::move(btmp); - - mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd); - ASSERT(!mLines.empty()); - - mTextChanged = true; -} - -void TextEditor::RemoveLine(int aIndex) { - ASSERT(!mReadOnly); - ASSERT(mLines.size() > 1); - - ErrorMarkers etmp; - for (auto& i : mErrorMarkers) { - ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first, i.second); - if (e.first - 1 == aIndex) - continue; - etmp.insert(e); - } - mErrorMarkers = std::move(etmp); - - Breakpoints btmp; - for (auto i : mBreakpoints) { - if (i == aIndex) - continue; - btmp.insert(i >= aIndex ? i - 1 : i); - } - mBreakpoints = std::move(btmp); - - mLines.erase(mLines.begin() + aIndex); - ASSERT(!mLines.empty()); - - mTextChanged = true; -} - -TextEditor::Line& TextEditor::InsertLine(int aIndex) { - ASSERT(!mReadOnly); - - auto& result = *mLines.insert(mLines.begin() + aIndex, Line()); - - ErrorMarkers etmp; - for (auto& i : mErrorMarkers) - etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second)); - mErrorMarkers = std::move(etmp); - - Breakpoints btmp; - for (auto i : mBreakpoints) - btmp.insert(i >= aIndex ? i + 1 : i); - mBreakpoints = std::move(btmp); - - return result; -} - -std::string TextEditor::GetWordUnderCursor() const { - auto c = GetCursorPosition(); - return GetWordAt(c); -} - -std::string TextEditor::GetWordAt(const Coordinates& aCoords) const { - auto start = FindWordStart(aCoords); - auto end = FindWordEnd(aCoords); - - std::string r; - - auto istart = GetCharacterIndex(start); - auto iend = GetCharacterIndex(end); - - for (auto it = istart; it < iend; ++it) - r.push_back(mLines[aCoords.mLine][it].mChar); - - return r; -} - -ImU32 TextEditor::GetGlyphColor(const Glyph& aGlyph) const { - if (!mColorizerEnabled) - return mPalette[(int)PaletteIndex::Default]; - if (aGlyph.mComment) - return mPalette[(int)PaletteIndex::Comment]; - if (aGlyph.mMultiLineComment) - return mPalette[(int)PaletteIndex::MultiLineComment]; - auto const color = mPalette[(int)aGlyph.mColorIndex]; - if (aGlyph.mPreprocessor) { - const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor]; - const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2; - const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2; - const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2; - const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2; - return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24)); - } - return color; -} - -void TextEditor::HandleKeyboardInputs() { - ImGuiIO& io = ImGui::GetIO(); - auto shift = io.KeyShift; - auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; - auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; - - if (ImGui::IsWindowFocused()) { - if (ImGui::IsWindowHovered()) - ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); - // ImGui::CaptureKeyboardFromApp(true); - - io.WantCaptureKeyboard = true; - io.WantTextInput = true; - - if (!IsReadOnly() && ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Z))) - Undo(); - else if (!IsReadOnly() && !ctrl && !shift && alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) - Undo(); - else if (!IsReadOnly() && ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y))) - Redo(); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow))) - MoveUp(1, shift); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow))) - MoveDown(1, shift); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow))) - MoveLeft(1, shift, ctrl); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow))) - MoveRight(1, shift, ctrl); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp))) - MoveUp(GetPageSize() - 4, shift); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown))) - MoveDown(GetPageSize() - 4, shift); - else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) - MoveTop(shift); - else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) - MoveBottom(shift); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) - MoveHome(shift); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) - MoveEnd(shift); - else if (!IsReadOnly() && !ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) - Delete(); - else if (!IsReadOnly() && !ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) - Backspace(); - else if (!ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) - mOverwrite ^= true; - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) - Copy(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) - Copy(); - else if (!IsReadOnly() && !ctrl && shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) - Paste(); - else if (!IsReadOnly() && ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V))) - Paste(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_X))) - Cut(); - else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) - Cut(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_A))) - SelectAll(); - else if (!IsReadOnly() && !ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter))) - EnterCharacter('\n', false); - else if (!IsReadOnly() && !ctrl && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab))) - EnterCharacter('\t', shift); - - if (!IsReadOnly() && !io.InputQueueCharacters.empty()) { - for (int i = 0; i < io.InputQueueCharacters.Size; i++) { - auto c = io.InputQueueCharacters[i]; - if (c != 0 && (c == '\n' || c >= 32)) - EnterCharacter(c, shift); - } - io.InputQueueCharacters.resize(0); - } - } -} - -void TextEditor::HandleMouseInputs() { - ImGuiIO& io = ImGui::GetIO(); - auto shift = io.KeyShift; - auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; - auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; - - if (ImGui::IsWindowHovered()) { - if (!shift && !alt) { - auto click = ImGui::IsMouseClicked(0); - auto doubleClick = ImGui::IsMouseDoubleClicked(0); - auto t = ImGui::GetTime(); - auto tripleClick = click && !doubleClick && - (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime); - - /* - Left mouse button triple click - */ - - if (tripleClick) { - if (!ctrl) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = - ScreenPosToCoordinates(ImGui::GetMousePos()); - mSelectionMode = SelectionMode::Line; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); - } - - mLastClick = -1.0f; - } - - /* - Left mouse button double click - */ - - else if (doubleClick) { - if (!ctrl) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = - ScreenPosToCoordinates(ImGui::GetMousePos()); - if (mSelectionMode == SelectionMode::Line) - mSelectionMode = SelectionMode::Normal; - else - mSelectionMode = SelectionMode::Word; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); - } - - mLastClick = (float)ImGui::GetTime(); - } - - /* - Left mouse button click - */ - else if (click) { - mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = - ScreenPosToCoordinates(ImGui::GetMousePos()); - if (ctrl) - mSelectionMode = SelectionMode::Word; - else - mSelectionMode = SelectionMode::Normal; - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); - - mLastClick = (float)ImGui::GetTime(); - } - // Mouse left button dragging (=> update selection) - else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { - io.WantCaptureMouse = true; - mState.mCursorPosition = mInteractiveEnd = - ScreenPosToCoordinates(ImGui::GetMousePos()); - SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); - } - } - } -} - -void TextEditor::Render() { - /* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/ - const float fontSize = - ImGui::GetFont() - ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr) - .x; - mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing); - - /* Update palette with the current alpha from style */ - for (int i = 0; i < (int)PaletteIndex::Max; ++i) { - auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]); - color.w *= ImGui::GetStyle().Alpha; - mPalette[i] = ImGui::ColorConvertFloat4ToU32(color); - } - - ASSERT(mLineBuffer.empty()); - - auto contentSize = ImGui::GetWindowContentRegionMax(); - auto drawList = ImGui::GetWindowDrawList(); - float longest(mTextStart); - - if (mScrollToTop) { - mScrollToTop = false; - ImGui::SetScrollY(0.f); - } - - ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); - auto scrollX = ImGui::GetScrollX(); - auto scrollY = ImGui::GetScrollY(); - - auto lineNo = (int)floor(scrollY / mCharAdvance.y); - auto globalLineMax = (int)mLines.size(); - auto lineMax = - std::max(0, std::min((int)mLines.size() - 1, - lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y))); - - // Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width - char buf[16]; - snprintf(buf, 16, " %d ", globalLineMax); - mTextStart = ImGui::GetFont() - ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr) - .x + - mLeftMargin; - - if (!mLines.empty()) { - float spaceSize = - ImGui::GetFont() - ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr) - .x; - - while (lineNo <= lineMax) { - ImVec2 lineStartScreenPos = - ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y); - ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y); - - auto& line = mLines[lineNo]; - longest = std::max( - mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), - longest); - auto columnNo = 0; - Coordinates lineStartCoord(lineNo, 0); - Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo)); - - // Draw selection for the current line - float sstart = -1.0f; - float ssend = -1.0f; - - ASSERT(mState.mSelectionStart <= mState.mSelectionEnd); - if (mState.mSelectionStart <= lineEndCoord) - sstart = mState.mSelectionStart > lineStartCoord - ? TextDistanceToLineStart(mState.mSelectionStart) - : 0.0f; - if (mState.mSelectionEnd > lineStartCoord) - ssend = TextDistanceToLineStart( - mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord); - - if (mState.mSelectionEnd.mLine > lineNo) - ssend += mCharAdvance.x; - - if (sstart != -1 && ssend != -1 && sstart < ssend) { - ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y); - ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, - lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]); - } - - // Draw breakpoints - auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y); - - if (mBreakpoints.count(lineNo + 1) != 0) { - auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, - lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]); - } - - // Draw error markers - auto errorIt = mErrorMarkers.find(lineNo + 1); - if (errorIt != mErrorMarkers.end()) { - auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, - lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::ErrorMarker]); - - if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end)) { - ImGui::BeginTooltip(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); - ImGui::Text("Error at line %d:", errorIt->first); - ImGui::PopStyleColor(); - ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f)); - ImGui::Text("%s", errorIt->second.c_str()); - ImGui::PopStyleColor(); - ImGui::EndTooltip(); - } - } - - // Draw line number (right aligned) - snprintf(buf, 16, "%d ", lineNo + 1); - - auto lineNoWidth = - ImGui::GetFont() - ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr) - .x; - drawList->AddText( - ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), - mPalette[(int)PaletteIndex::LineNumber], buf); - - if (mState.mCursorPosition.mLine == lineNo) { - auto focused = ImGui::IsWindowFocused(); - - // Highlight the current line (where the cursor is) - if (!HasSelection()) { - auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y); - drawList->AddRectFilled( - start, end, - mPalette[(int)(focused ? PaletteIndex::CurrentLineFill - : PaletteIndex::CurrentLineFillInactive)]); - drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], - 1.0f); - } - - // Render the cursor - if (focused) { - auto timeEnd = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - auto elapsed = timeEnd - mStartTime; - if (elapsed > 400) { - float width = 1.0f; - auto cindex = GetCharacterIndex(mState.mCursorPosition); - float cx = TextDistanceToLineStart(mState.mCursorPosition); - - if (mOverwrite && cindex < (int)line.size()) { - auto c = line[cindex].mChar; - if (c == '\t') { - auto x = (1.0f + - std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * - (float(mTabSize) * spaceSize); - width = x - cx; - } else { - char buf2[2]; - buf2[0] = line[cindex].mChar; - buf2[1] = '\0'; - width = - ImGui::GetFont() - ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2) - .x; - } - } - ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); - ImVec2 cend(textScreenPos.x + cx + width, - lineStartScreenPos.y + mCharAdvance.y); - drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); - if (elapsed > 800) - mStartTime = timeEnd; - } - } - } - - // Render colorized text - auto prevColor = - line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]); - ImVec2 bufferOffset; - - for (int i = 0; i < line.size();) { - auto& glyph = line[i]; - auto color = GetGlyphColor(glyph); - - if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') && - !mLineBuffer.empty()) { - const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, - textScreenPos.y + bufferOffset.y); - drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); - auto textSize = - ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, - mLineBuffer.c_str(), nullptr, nullptr); - bufferOffset.x += textSize.x; - mLineBuffer.clear(); - } - prevColor = color; - - if (glyph.mChar == '\t') { - auto oldX = bufferOffset.x; - bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / - (float(mTabSize) * spaceSize))) * - (float(mTabSize) * spaceSize); - ++i; - - if (mShowWhitespaces) { - const auto s = ImGui::GetFontSize(); - const auto x1 = textScreenPos.x + oldX + 1.0f; - const auto x2 = textScreenPos.x + bufferOffset.x - 1.0f; - const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; - const ImVec2 p1(x1, y); - const ImVec2 p2(x2, y); - const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f); - const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f); - drawList->AddLine(p1, p2, 0x90909090); - drawList->AddLine(p2, p3, 0x90909090); - drawList->AddLine(p2, p4, 0x90909090); - } - } else if (glyph.mChar == ' ') { - if (mShowWhitespaces) { - const auto s = ImGui::GetFontSize(); - const auto x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f; - const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; - drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4); - } - bufferOffset.x += spaceSize; - i++; - } else { - auto l = UTF8CharLength(glyph.mChar); - while (l-- > 0) - mLineBuffer.push_back(line[i++].mChar); - } - ++columnNo; - } - - if (!mLineBuffer.empty()) { - const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, - textScreenPos.y + bufferOffset.y); - drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); - mLineBuffer.clear(); - } - - ++lineNo; - } - - // Draw a tooltip on known identifiers/preprocessor symbols - if (ImGui::IsMousePosValid()) { - auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos())); - if (!id.empty()) { - auto it = mLanguageDefinition.mIdentifiers.find(id); - if (it != mLanguageDefinition.mIdentifiers.end()) { - ImGui::BeginTooltip(); - ImGui::TextUnformatted(it->second.mDeclaration.c_str()); - ImGui::EndTooltip(); - } else { - auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id); - if (pi != mLanguageDefinition.mPreprocIdentifiers.end()) { - ImGui::BeginTooltip(); - ImGui::TextUnformatted(pi->second.mDeclaration.c_str()); - ImGui::EndTooltip(); - } - } - } - } - } - - ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y)); - - if (mScrollToCursor) { - EnsureCursorVisible(); - ImGui::SetWindowFocus(); - mScrollToCursor = false; - } -} - -void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) { - mWithinRender = true; - mTextChanged = false; - mCursorPositionChanged = false; - - ImGui::PushStyleColor(ImGuiCol_ChildBg, - ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background])); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); - if (!mIgnoreImGuiChild) - ImGui::BeginChild(aTitle, aSize, aBorder, - ImGuiWindowFlags_HorizontalScrollbar | - ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove); - - if (mHandleKeyboardInputs) { - HandleKeyboardInputs(); - ImGui::PushAllowKeyboardFocus(true); - } - - if (mHandleMouseInputs) - HandleMouseInputs(); - - ColorizeInternal(); - Render(); - - if (mHandleKeyboardInputs) - ImGui::PopAllowKeyboardFocus(); - - if (!mIgnoreImGuiChild) - ImGui::EndChild(); - - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - - mWithinRender = false; -} - -void TextEditor::SetText(const std::string& aText) { - mLines.clear(); - mLines.emplace_back(Line()); - for (auto chr : aText) { - if (chr == '\r') { - // ignore the carriage return character - } else if (chr == '\n') - mLines.emplace_back(Line()); - else { - mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default)); - } - } - - mTextChanged = true; - mScrollToTop = true; - - mUndoBuffer.clear(); - mUndoIndex = 0; - - Colorize(); -} - -void TextEditor::SetTextLines(const std::vector& aLines) { - mLines.clear(); - - if (aLines.empty()) { - mLines.emplace_back(Line()); - } else { - mLines.resize(aLines.size()); - - for (size_t i = 0; i < aLines.size(); ++i) { - const std::string& aLine = aLines[i]; - - mLines[i].reserve(aLine.size()); - for (size_t j = 0; j < aLine.size(); ++j) - mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default)); - } - } - - mTextChanged = true; - mScrollToTop = true; - - mUndoBuffer.clear(); - mUndoIndex = 0; - - Colorize(); -} - -void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { - ASSERT(!mReadOnly); - - UndoRecord u; - - u.mBefore = mState; - - if (HasSelection()) { - if (aChar == '\t' && mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine) { - - auto start = mState.mSelectionStart; - auto end = mState.mSelectionEnd; - auto originalEnd = end; - - if (start > end) - std::swap(start, end); - start.mColumn = 0; - // end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0; - if (end.mColumn == 0 && end.mLine > 0) - --end.mLine; - if (end.mLine >= (int)mLines.size()) - end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1; - end.mColumn = GetLineMaxColumn(end.mLine); - - // if (end.mColumn >= GetLineMaxColumn(end.mLine)) - // end.mColumn = GetLineMaxColumn(end.mLine) - 1; - - u.mRemovedStart = start; - u.mRemovedEnd = end; - u.mRemoved = GetText(start, end); - - bool modified = false; - - for (int i = start.mLine; i <= end.mLine; i++) { - auto& line = mLines[i]; - if (aShift) { - if (!line.empty()) { - if (line.front().mChar == '\t') { - line.erase(line.begin()); - modified = true; - } else { - for (int j = 0; - j < mTabSize && !line.empty() && line.front().mChar == ' '; j++) { - line.erase(line.begin()); - modified = true; - } - } - } - } else { - line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background)); - modified = true; - } - } - - if (modified) { - start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0)); - Coordinates rangeEnd; - if (originalEnd.mColumn != 0) { - end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine)); - rangeEnd = end; - u.mAdded = GetText(start, end); - } else { - end = Coordinates(originalEnd.mLine, 0); - rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1)); - u.mAdded = GetText(start, rangeEnd); - } - - u.mAddedStart = start; - u.mAddedEnd = rangeEnd; - u.mAfter = mState; - - mState.mSelectionStart = start; - mState.mSelectionEnd = end; - AddUndo(u); - - mTextChanged = true; - - EnsureCursorVisible(); - } - - return; - } // c == '\t' - else { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - DeleteSelection(); - } - } // HasSelection - - auto coord = GetActualCursorCoordinates(); - u.mAddedStart = coord; - - ASSERT(!mLines.empty()); - - if (aChar == '\n') { - InsertLine(coord.mLine + 1); - auto& line = mLines[coord.mLine]; - auto& newLine = mLines[coord.mLine + 1]; - - if (mLanguageDefinition.mAutoIndentation) - for (size_t it = 0; - it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it) - newLine.push_back(line[it]); - - const size_t whitespaceSize = newLine.size(); - auto cindex = GetCharacterIndex(coord); - newLine.insert(newLine.end(), line.begin() + cindex, line.end()); - line.erase(line.begin() + cindex, line.begin() + line.size()); - SetCursorPosition( - Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize))); - u.mAdded = (char)aChar; - } else { - char buf[7]; - int e = ImTextCharToUtf8(buf, 7, aChar); - if (e > 0) { - buf[e] = '\0'; - auto& line = mLines[coord.mLine]; - auto cindex = GetCharacterIndex(coord); - - if (mOverwrite && cindex < (int)line.size()) { - auto d = UTF8CharLength(line[cindex].mChar); - - u.mRemovedStart = mState.mCursorPosition; - u.mRemovedEnd = - Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d)); - - while (d-- > 0 && cindex < (int)line.size()) { - u.mRemoved += line[cindex].mChar; - line.erase(line.begin() + cindex); - } - } - - for (auto p = buf; *p != '\0'; p++, ++cindex) - line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default)); - u.mAdded = buf; - - SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex))); - } else - return; - } - - mTextChanged = true; - - u.mAddedEnd = GetActualCursorCoordinates(); - u.mAfter = mState; - - AddUndo(u); - - Colorize(coord.mLine - 1, 3); - EnsureCursorVisible(); -} - -void TextEditor::SetReadOnly(bool aValue) { - mReadOnly = aValue; -} - -void TextEditor::SetColorizerEnable(bool aValue) { - mColorizerEnabled = aValue; -} - -void TextEditor::SetCursorPosition(const Coordinates& aPosition) { - if (mState.mCursorPosition != aPosition) { - mState.mCursorPosition = aPosition; - mCursorPositionChanged = true; - EnsureCursorVisible(); - } -} - -void TextEditor::SetSelectionStart(const Coordinates& aPosition) { - mState.mSelectionStart = SanitizeCoordinates(aPosition); - if (mState.mSelectionStart > mState.mSelectionEnd) - std::swap(mState.mSelectionStart, mState.mSelectionEnd); -} - -void TextEditor::SetSelectionEnd(const Coordinates& aPosition) { - mState.mSelectionEnd = SanitizeCoordinates(aPosition); - if (mState.mSelectionStart > mState.mSelectionEnd) - std::swap(mState.mSelectionStart, mState.mSelectionEnd); -} - -void TextEditor::SetSelection(const Coordinates& aStart, const Coordinates& aEnd, - SelectionMode aMode) { - auto oldSelStart = mState.mSelectionStart; - auto oldSelEnd = mState.mSelectionEnd; - - mState.mSelectionStart = SanitizeCoordinates(aStart); - mState.mSelectionEnd = SanitizeCoordinates(aEnd); - if (mState.mSelectionStart > mState.mSelectionEnd) - std::swap(mState.mSelectionStart, mState.mSelectionEnd); - - switch (aMode) { - case TextEditor::SelectionMode::Normal: - break; - case TextEditor::SelectionMode::Word: { - mState.mSelectionStart = FindWordStart(mState.mSelectionStart); - if (!IsOnWordBoundary(mState.mSelectionEnd)) - mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd)); - break; - } - case TextEditor::SelectionMode::Line: { - const auto lineNo = mState.mSelectionEnd.mLine; - const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0; - mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0); - mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo)); - break; - } - default: - break; - } - - if (mState.mSelectionStart != oldSelStart || mState.mSelectionEnd != oldSelEnd) - mCursorPositionChanged = true; -} - -void TextEditor::SetTabSize(int aValue) { - mTabSize = std::max(0, std::min(32, aValue)); -} - -void TextEditor::InsertText(const std::string& aValue) { - InsertText(aValue.c_str()); -} - -void TextEditor::InsertText(const char* aValue) { - if (aValue == nullptr) - return; - - auto pos = GetActualCursorCoordinates(); - auto start = std::min(pos, mState.mSelectionStart); - int totalLines = pos.mLine - start.mLine; - - totalLines += InsertTextAt(pos, aValue); - - SetSelection(pos, pos); - SetCursorPosition(pos); - Colorize(start.mLine - 1, totalLines + 2); -} - -void TextEditor::DeleteSelection() { - ASSERT(mState.mSelectionEnd >= mState.mSelectionStart); - - if (mState.mSelectionEnd == mState.mSelectionStart) - return; - - DeleteRange(mState.mSelectionStart, mState.mSelectionEnd); - - SetSelection(mState.mSelectionStart, mState.mSelectionStart); - SetCursorPosition(mState.mSelectionStart); - Colorize(mState.mSelectionStart.mLine, 1); -} - -void TextEditor::MoveUp(int aAmount, bool aSelect) { - auto oldPos = mState.mCursorPosition; - mState.mCursorPosition.mLine = std::max(0, mState.mCursorPosition.mLine - aAmount); - if (oldPos != mState.mCursorPosition) { - if (aSelect) { - if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else { - mInteractiveStart = mState.mCursorPosition; - mInteractiveEnd = oldPos; - } - } else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - - EnsureCursorVisible(); - } -} - -void TextEditor::MoveDown(int aAmount, bool aSelect) { - ASSERT(mState.mCursorPosition.mColumn >= 0); - auto oldPos = mState.mCursorPosition; - mState.mCursorPosition.mLine = - std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount)); - - if (mState.mCursorPosition != oldPos) { - if (aSelect) { - if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else { - mInteractiveStart = oldPos; - mInteractiveEnd = mState.mCursorPosition; - } - } else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - - EnsureCursorVisible(); - } -} - -static bool IsUTFSequence(char c) { - return (c & 0xC0) == 0x80; -} - -void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) { - if (mLines.empty()) - return; - - auto oldPos = mState.mCursorPosition; - mState.mCursorPosition = GetActualCursorCoordinates(); - auto line = mState.mCursorPosition.mLine; - auto cindex = GetCharacterIndex(mState.mCursorPosition); - - while (aAmount-- > 0) { - if (cindex == 0) { - if (line > 0) { - --line; - if ((int)mLines.size() > line) - cindex = (int)mLines[line].size(); - else - cindex = 0; - } - } else { - --cindex; - if (cindex > 0) { - if ((int)mLines.size() > line) { - while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar)) - --cindex; - } - } - } - - mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); - if (aWordMode) { - mState.mCursorPosition = FindWordStart(mState.mCursorPosition); - cindex = GetCharacterIndex(mState.mCursorPosition); - } - } - - mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); - - ASSERT(mState.mCursorPosition.mColumn >= 0); - if (aSelect) { - if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else { - mInteractiveStart = mState.mCursorPosition; - mInteractiveEnd = oldPos; - } - } else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd, - aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); - - EnsureCursorVisible(); -} - -void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) { - auto oldPos = mState.mCursorPosition; - - if (mLines.empty() || oldPos.mLine >= mLines.size()) - return; - - auto cindex = GetCharacterIndex(mState.mCursorPosition); - while (aAmount-- > 0) { - auto lindex = mState.mCursorPosition.mLine; - auto& line = mLines[lindex]; - - if (cindex >= line.size()) { - if (mState.mCursorPosition.mLine < mLines.size() - 1) { - mState.mCursorPosition.mLine = - std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1)); - mState.mCursorPosition.mColumn = 0; - } else - return; - } else { - cindex += UTF8CharLength(line[cindex].mChar); - mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); - if (aWordMode) - mState.mCursorPosition = FindNextWord(mState.mCursorPosition); - } - } - - if (aSelect) { - if (oldPos == mInteractiveEnd) - mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition); - else if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else { - mInteractiveStart = oldPos; - mInteractiveEnd = mState.mCursorPosition; - } - } else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd, - aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); - - EnsureCursorVisible(); -} - -void TextEditor::MoveTop(bool aSelect) { - auto oldPos = mState.mCursorPosition; - SetCursorPosition(Coordinates(0, 0)); - - if (mState.mCursorPosition != oldPos) { - if (aSelect) { - mInteractiveEnd = oldPos; - mInteractiveStart = mState.mCursorPosition; - } else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - } -} - -void TextEditor::TextEditor::MoveBottom(bool aSelect) { - auto oldPos = GetCursorPosition(); - auto newPos = Coordinates((int)mLines.size() - 1, 0); - SetCursorPosition(newPos); - if (aSelect) { - mInteractiveStart = oldPos; - mInteractiveEnd = newPos; - } else - mInteractiveStart = mInteractiveEnd = newPos; - SetSelection(mInteractiveStart, mInteractiveEnd); -} - -void TextEditor::MoveHome(bool aSelect) { - auto oldPos = mState.mCursorPosition; - SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0)); - - if (mState.mCursorPosition != oldPos) { - if (aSelect) { - if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else { - mInteractiveStart = mState.mCursorPosition; - mInteractiveEnd = oldPos; - } - } else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - } -} - -void TextEditor::MoveEnd(bool aSelect) { - auto oldPos = mState.mCursorPosition; - SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine))); - - if (mState.mCursorPosition != oldPos) { - if (aSelect) { - if (oldPos == mInteractiveEnd) - mInteractiveEnd = mState.mCursorPosition; - else if (oldPos == mInteractiveStart) - mInteractiveStart = mState.mCursorPosition; - else { - mInteractiveStart = oldPos; - mInteractiveEnd = mState.mCursorPosition; - } - } else - mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; - SetSelection(mInteractiveStart, mInteractiveEnd); - } -} - -void TextEditor::Delete() { - ASSERT(!mReadOnly); - - if (mLines.empty()) - return; - - UndoRecord u; - u.mBefore = mState; - - if (HasSelection()) { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - - DeleteSelection(); - } else { - auto pos = GetActualCursorCoordinates(); - SetCursorPosition(pos); - auto& line = mLines[pos.mLine]; - - if (pos.mColumn == GetLineMaxColumn(pos.mLine)) { - if (pos.mLine == (int)mLines.size() - 1) - return; - - u.mRemoved = '\n'; - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - Advance(u.mRemovedEnd); - - auto& nextLine = mLines[pos.mLine + 1]; - line.insert(line.end(), nextLine.begin(), nextLine.end()); - RemoveLine(pos.mLine + 1); - } else { - auto cindex = GetCharacterIndex(pos); - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - u.mRemovedEnd.mColumn++; - u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd); - - auto d = UTF8CharLength(line[cindex].mChar); - while (d-- > 0 && cindex < (int)line.size()) - line.erase(line.begin() + cindex); - } - - mTextChanged = true; - - Colorize(pos.mLine, 1); - } - - u.mAfter = mState; - AddUndo(u); -} - -void TextEditor::Backspace() { - ASSERT(!mReadOnly); - - if (mLines.empty()) - return; - - UndoRecord u; - u.mBefore = mState; - - if (HasSelection()) { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - - DeleteSelection(); - } else { - auto pos = GetActualCursorCoordinates(); - SetCursorPosition(pos); - - if (mState.mCursorPosition.mColumn == 0) { - if (mState.mCursorPosition.mLine == 0) - return; - - u.mRemoved = '\n'; - u.mRemovedStart = u.mRemovedEnd = - Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); - Advance(u.mRemovedEnd); - - auto& line = mLines[mState.mCursorPosition.mLine]; - auto& prevLine = mLines[mState.mCursorPosition.mLine - 1]; - auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1); - prevLine.insert(prevLine.end(), line.begin(), line.end()); - - ErrorMarkers etmp; - for (auto& i : mErrorMarkers) - etmp.insert(ErrorMarkers::value_type( - i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second)); - mErrorMarkers = std::move(etmp); - - RemoveLine(mState.mCursorPosition.mLine); - --mState.mCursorPosition.mLine; - mState.mCursorPosition.mColumn = prevSize; - } else { - auto& line = mLines[mState.mCursorPosition.mLine]; - auto cindex = GetCharacterIndex(pos) - 1; - auto cend = cindex + 1; - while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) - --cindex; - - // if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1) - // --cindex; - - u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); - --u.mRemovedStart.mColumn; - --mState.mCursorPosition.mColumn; - - while (cindex < line.size() && cend-- > cindex) { - u.mRemoved += line[cindex].mChar; - line.erase(line.begin() + cindex); - } - } - - mTextChanged = true; - - EnsureCursorVisible(); - Colorize(mState.mCursorPosition.mLine, 1); - } - - u.mAfter = mState; - AddUndo(u); -} - -void TextEditor::SelectWordUnderCursor() { - auto c = GetCursorPosition(); - SetSelection(FindWordStart(c), FindWordEnd(c)); -} - -void TextEditor::SelectAll() { - SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0)); -} - -bool TextEditor::HasSelection() const { - return mState.mSelectionEnd > mState.mSelectionStart; -} - -void TextEditor::Copy() { - if (HasSelection()) { - ImGui::SetClipboardText(GetSelectedText().c_str()); - } else { - if (!mLines.empty()) { - std::string str; - auto& line = mLines[GetActualCursorCoordinates().mLine]; - for (auto& g : line) - str.push_back(g.mChar); - ImGui::SetClipboardText(str.c_str()); - } - } -} - -void TextEditor::Cut() { - if (IsReadOnly()) { - Copy(); - } else { - if (HasSelection()) { - UndoRecord u; - u.mBefore = mState; - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - - Copy(); - DeleteSelection(); - - u.mAfter = mState; - AddUndo(u); - } - } -} - -void TextEditor::Paste() { - if (IsReadOnly()) - return; - - auto clipText = ImGui::GetClipboardText(); - if (clipText != nullptr && strlen(clipText) > 0) { - UndoRecord u; - u.mBefore = mState; - - if (HasSelection()) { - u.mRemoved = GetSelectedText(); - u.mRemovedStart = mState.mSelectionStart; - u.mRemovedEnd = mState.mSelectionEnd; - DeleteSelection(); - } - - u.mAdded = clipText; - u.mAddedStart = GetActualCursorCoordinates(); - - InsertText(clipText); - - u.mAddedEnd = GetActualCursorCoordinates(); - u.mAfter = mState; - AddUndo(u); - } -} - -bool TextEditor::CanUndo() const { - return !mReadOnly && mUndoIndex > 0; -} - -bool TextEditor::CanRedo() const { - return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size(); -} - -void TextEditor::Undo(int aSteps) { - while (CanUndo() && aSteps-- > 0) - mUndoBuffer[--mUndoIndex].Undo(this); -} - -void TextEditor::Redo(int aSteps) { - while (CanRedo() && aSteps-- > 0) - mUndoBuffer[mUndoIndex++].Redo(this); -} - -const TextEditor::Palette& TextEditor::GetDarkPalette() { - const static Palette p = {{ - 0xff7f7f7f, // Default - 0xffd69c56, // Keyword - 0xff00ff00, // Number - 0xff7070e0, // String - 0xff70a0e0, // Char literal - 0xffffffff, // Punctuation - 0xff408080, // Preprocessor - 0xffaaaaaa, // Identifier - 0xff9bc64d, // Known identifier - 0xffc040a0, // Preproc identifier - 0xff206020, // Comment (single line) - 0xff406020, // Comment (multi line) - 0xff101010, // Background - 0xffe0e0e0, // Cursor - 0x80a06020, // Selection - 0x800020ff, // ErrorMarker - 0x40f08000, // Breakpoint - 0xff707000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40a0a0a0, // Current line edge - }}; - return p; -} - -const TextEditor::Palette& TextEditor::GetLightPalette() { - const static Palette p = {{ - 0xff7f7f7f, // None - 0xffff0c06, // Keyword - 0xff008000, // Number - 0xff2020a0, // String - 0xff304070, // Char literal - 0xff000000, // Punctuation - 0xff406060, // Preprocessor - 0xff404040, // Identifier - 0xff606010, // Known identifier - 0xffc040a0, // Preproc identifier - 0xff205020, // Comment (single line) - 0xff405020, // Comment (multi line) - 0xffffffff, // Background - 0xff000000, // Cursor - 0x80600000, // Selection - 0xa00010ff, // ErrorMarker - 0x80f08000, // Breakpoint - 0xff505000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40000000, // Current line edge - }}; - return p; -} - -const TextEditor::Palette& TextEditor::GetRetroBluePalette() { - const static Palette p = {{ - 0xff00ffff, // None - 0xffffff00, // Keyword - 0xff00ff00, // Number - 0xff808000, // String - 0xff808000, // Char literal - 0xffffffff, // Punctuation - 0xff008000, // Preprocessor - 0xff00ffff, // Identifier - 0xffffffff, // Known identifier - 0xffff00ff, // Preproc identifier - 0xff808080, // Comment (single line) - 0xff404040, // Comment (multi line) - 0xff800000, // Background - 0xff0080ff, // Cursor - 0x80ffff00, // Selection - 0xa00000ff, // ErrorMarker - 0x80ff8000, // Breakpoint - 0xff808000, // Line number - 0x40000000, // Current line fill - 0x40808080, // Current line fill (inactive) - 0x40000000, // Current line edge - }}; - return p; -} - -std::string TextEditor::GetText() const { - return GetText(Coordinates(), Coordinates((int)mLines.size(), 0)); -} - -std::vector TextEditor::GetTextLines() const { - std::vector result; - - result.reserve(mLines.size()); - - for (auto& line : mLines) { - std::string text; - - text.resize(line.size()); - - for (size_t i = 0; i < line.size(); ++i) - text[i] = line[i].mChar; - - result.emplace_back(std::move(text)); - } - - return result; -} - -std::string TextEditor::GetSelectedText() const { - return GetText(mState.mSelectionStart, mState.mSelectionEnd); -} - -std::string TextEditor::GetCurrentLineText() const { - auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine); - return GetText(Coordinates(mState.mCursorPosition.mLine, 0), - Coordinates(mState.mCursorPosition.mLine, lineLength)); -} - -void TextEditor::ProcessInputs() {} - -void TextEditor::Colorize(int aFromLine, int aLines) { - int toLine = - aLines == -1 ? (int)mLines.size() : std::min((int)mLines.size(), aFromLine + aLines); - mColorRangeMin = std::min(mColorRangeMin, aFromLine); - mColorRangeMax = std::max(mColorRangeMax, toLine); - mColorRangeMin = std::max(0, mColorRangeMin); - mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax); - mCheckComments = true; -} - -void TextEditor::ColorizeRange(int aFromLine, int aToLine) { - if (mLines.empty() || aFromLine >= aToLine) - return; - - std::string buffer; - std::cmatch results; - std::string id; - - int endLine = std::max(0, std::min((int)mLines.size(), aToLine)); - for (int i = aFromLine; i < endLine; ++i) { - auto& line = mLines[i]; - - if (line.empty()) - continue; - - buffer.resize(line.size()); - for (size_t j = 0; j < line.size(); ++j) { - auto& col = line[j]; - buffer[j] = col.mChar; - col.mColorIndex = PaletteIndex::Default; - } - - const char* bufferBegin = &buffer.front(); - const char* bufferEnd = bufferBegin + buffer.size(); - - auto last = bufferEnd; - - for (auto first = bufferBegin; first != last;) { - const char* token_begin = nullptr; - const char* token_end = nullptr; - PaletteIndex token_color = PaletteIndex::Default; - - bool hasTokenizeResult = false; - - if (mLanguageDefinition.mTokenize != nullptr) { - if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end, token_color)) - hasTokenizeResult = true; - } - - if (hasTokenizeResult == false) { - // todo : remove - // printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), - // first); - - for (auto& p : mRegexList) { - if (std::regex_search(first, last, results, p.first, - std::regex_constants::match_continuous)) { - hasTokenizeResult = true; - - auto& v = *results.begin(); - token_begin = v.first; - token_end = v.second; - token_color = p.second; - break; - } - } - } - - if (hasTokenizeResult == false) { - first++; - } else { - const size_t token_length = token_end - token_begin; - - if (token_color == PaletteIndex::Identifier) { - id.assign(token_begin, token_end); - - // todo : allmost all language definitions use lower case to specify keywords, - // so shouldn't this use ::tolower ? - if (!mLanguageDefinition.mCaseSensitive) - std::transform(id.begin(), id.end(), id.begin(), ::toupper); - - if (!line[first - bufferBegin].mPreprocessor) { - if (mLanguageDefinition.mKeywords.count(id) != 0) - token_color = PaletteIndex::Keyword; - else if (mLanguageDefinition.mIdentifiers.count(id) != 0) - token_color = PaletteIndex::KnownIdentifier; - else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) - token_color = PaletteIndex::PreprocIdentifier; - } else { - if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) - token_color = PaletteIndex::PreprocIdentifier; - } - } - - for (size_t j = 0; j < token_length; ++j) - line[(token_begin - bufferBegin) + j].mColorIndex = token_color; - - first = token_end; - } - } - } -} - -void TextEditor::ColorizeInternal() { - if (mLines.empty() || !mColorizerEnabled) - return; - - if (mCheckComments) { - auto endLine = mLines.size(); - auto endIndex = 0; - auto commentStartLine = endLine; - auto commentStartIndex = endIndex; - auto withinString = false; - auto withinSingleLineComment = false; - auto withinPreproc = false; - auto firstChar = true; // there is no other non-whitespace characters in the line before - auto concatenate = false; // '\' on the very end of the line - auto currentLine = 0; - auto currentIndex = 0; - while (currentLine < endLine || currentIndex < endIndex) { - auto& line = mLines[currentLine]; - - if (currentIndex == 0 && !concatenate) { - withinSingleLineComment = false; - withinPreproc = false; - firstChar = true; - } - - concatenate = false; - - if (!line.empty()) { - auto& g = line[currentIndex]; - auto c = g.mChar; - - if (c != mLanguageDefinition.mPreprocChar && !isspace(c)) - firstChar = false; - - if (currentIndex == (int)line.size() - 1 && line[line.size() - 1].mChar == '\\') - concatenate = true; - - bool inComment = - (commentStartLine < currentLine || - (commentStartLine == currentLine && commentStartIndex <= currentIndex)); - - if (withinString) { - line[currentIndex].mMultiLineComment = inComment; - - if (c == '\"') { - if (currentIndex + 1 < (int)line.size() && - line[currentIndex + 1].mChar == '\"') { - currentIndex += 1; - if (currentIndex < (int)line.size()) - line[currentIndex].mMultiLineComment = inComment; - } else - withinString = false; - } else if (c == '\\') { - currentIndex += 1; - if (currentIndex < (int)line.size()) - line[currentIndex].mMultiLineComment = inComment; - } - } else { - if (firstChar && c == mLanguageDefinition.mPreprocChar) - withinPreproc = true; - - if (c == '\"') { - withinString = true; - line[currentIndex].mMultiLineComment = inComment; - } else { - auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; }; - auto from = line.begin() + currentIndex; - auto& startStr = mLanguageDefinition.mCommentStart; - auto& singleStartStr = mLanguageDefinition.mSingleLineComment; - - if (singleStartStr.size() > 0 && - currentIndex + singleStartStr.size() <= line.size() && - equals(singleStartStr.begin(), singleStartStr.end(), from, - from + singleStartStr.size(), pred)) { - withinSingleLineComment = true; - } else if (!withinSingleLineComment && - currentIndex + startStr.size() <= line.size() && - equals(startStr.begin(), startStr.end(), from, - from + startStr.size(), pred)) { - commentStartLine = currentLine; - commentStartIndex = currentIndex; - } - - inComment = inComment = - (commentStartLine < currentLine || (commentStartLine == currentLine && - commentStartIndex <= currentIndex)); - - line[currentIndex].mMultiLineComment = inComment; - line[currentIndex].mComment = withinSingleLineComment; - - auto& endStr = mLanguageDefinition.mCommentEnd; - if (currentIndex + 1 >= (int)endStr.size() && - equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, - pred)) { - commentStartIndex = endIndex; - commentStartLine = endLine; - } - } - } - line[currentIndex].mPreprocessor = withinPreproc; - currentIndex += UTF8CharLength(c); - if (currentIndex >= (int)line.size()) { - currentIndex = 0; - ++currentLine; - } - } else { - currentIndex = 0; - ++currentLine; - } - } - mCheckComments = false; - } - - if (mColorRangeMin < mColorRangeMax) { - const int increment = (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000; - const int to = std::min(mColorRangeMin + increment, mColorRangeMax); - ColorizeRange(mColorRangeMin, to); - mColorRangeMin = to; - - if (mColorRangeMax == mColorRangeMin) { - mColorRangeMin = std::numeric_limits::max(); - mColorRangeMax = 0; - } - return; - } -} - -float TextEditor::TextDistanceToLineStart(const Coordinates& aFrom) const { - auto& line = mLines[aFrom.mLine]; - float distance = 0.0f; - float spaceSize = - ImGui::GetFont() - ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr) - .x; - int colIndex = GetCharacterIndex(aFrom); - for (size_t it = 0u; it < line.size() && it < colIndex;) { - if (line[it].mChar == '\t') { - distance = (1.0f + std::floor((1.0f + distance) / (float(mTabSize) * spaceSize))) * - (float(mTabSize) * spaceSize); - ++it; - } else { - auto d = UTF8CharLength(line[it].mChar); - char tempCString[7]; - int i = 0; - for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++) - tempCString[i] = line[it].mChar; - - tempCString[i] = '\0'; - distance += ImGui::GetFont() - ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, - nullptr, nullptr) - .x; - } - } - - return distance; -} - -void TextEditor::EnsureCursorVisible() { - if (!mWithinRender) { - mScrollToCursor = true; - return; - } - - float scrollX = ImGui::GetScrollX(); - float scrollY = ImGui::GetScrollY(); - - auto height = ImGui::GetWindowHeight(); - auto width = ImGui::GetWindowWidth(); - - auto top = 1 + (int)ceil(scrollY / mCharAdvance.y); - auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y); - - auto left = (int)ceil(scrollX / mCharAdvance.x); - auto right = (int)ceil((scrollX + width) / mCharAdvance.x); - - auto pos = GetActualCursorCoordinates(); - auto len = TextDistanceToLineStart(pos); - - if (pos.mLine < top) - ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y)); - if (pos.mLine > bottom - 4) - ImGui::SetScrollY(std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height)); - if (len + mTextStart < left + 4) - ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4)); - if (len + mTextStart > right - 4) - ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width)); -} - -int TextEditor::GetPageSize() const { - auto height = ImGui::GetWindowHeight() - 20.0f; - return (int)floor(height / mCharAdvance.y); -} - -TextEditor::UndoRecord::UndoRecord( - const std::string& aAdded, const TextEditor::Coordinates aAddedStart, - const TextEditor::Coordinates aAddedEnd, const std::string& aRemoved, - const TextEditor::Coordinates aRemovedStart, const TextEditor::Coordinates aRemovedEnd, - TextEditor::EditorState& aBefore, TextEditor::EditorState& aAfter) - : mAdded(aAdded), mAddedStart(aAddedStart), mAddedEnd(aAddedEnd), mRemoved(aRemoved), - mRemovedStart(aRemovedStart), mRemovedEnd(aRemovedEnd), mBefore(aBefore), mAfter(aAfter) { - ASSERT(mAddedStart <= mAddedEnd); - ASSERT(mRemovedStart <= mRemovedEnd); -} - -void TextEditor::UndoRecord::Undo(TextEditor* aEditor) { - if (!mAdded.empty()) { - aEditor->DeleteRange(mAddedStart, mAddedEnd); - aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2); - } - - if (!mRemoved.empty()) { - auto start = mRemovedStart; - aEditor->InsertTextAt(start, mRemoved.c_str()); - aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2); - } - - aEditor->mState = mBefore; - aEditor->EnsureCursorVisible(); -} - -void TextEditor::UndoRecord::Redo(TextEditor* aEditor) { - if (!mRemoved.empty()) { - aEditor->DeleteRange(mRemovedStart, mRemovedEnd); - aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1); - } - - if (!mAdded.empty()) { - auto start = mAddedStart; - aEditor->InsertTextAt(start, mAdded.c_str()); - aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1); - } - - aEditor->mState = mAfter; - aEditor->EnsureCursorVisible(); -} - -const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL() { - static bool inited = false; - static LanguageDefinition langDef; - if (!inited) { - static const char* const keywords[] = { - "auto", "break", "case", "char", "const", "continue", - "default", "do", "double", "else", "enum", "extern", - "float", "for", "goto", "if", "inline", "int", - "long", "register", "restrict", "return", "short", "signed", - "sizeof", "static", "struct", "switch", "typedef", "union", - "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", - "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", - "_Static_assert", "_Thread_local"}; - for (auto& k : keywords) - langDef.mKeywords.insert(k); - - static const char* const identifiers[] = { - "abort", "abs", "acos", "asin", "atan", "atexit", "atof", - "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", - "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", - "isalpha", "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", - "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", - "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", - "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper"}; - for (auto& k : identifiers) { - Identifier id; - id.mDeclaration = "Built-in function"; - langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); - } - - langDef.mTokenRegexStrings.push_back(std::make_pair( - "[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); - langDef.mTokenRegexStrings.push_back(std::make_pair( - "L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); - langDef.mTokenRegexStrings.push_back(std::make_pair( - "\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); - langDef.mTokenRegexStrings.push_back(std::make_pair( - "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair( - "[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair( - "0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair( - "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); - langDef.mTokenRegexStrings.push_back(std::make_pair( - "[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); - langDef.mTokenRegexStrings.push_back(std::make_pair( - "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", - PaletteIndex::Punctuation)); - - langDef.mCommentStart = "/*"; - langDef.mCommentEnd = "*/"; - langDef.mSingleLineComment = "//"; - - langDef.mCaseSensitive = true; - langDef.mAutoIndentation = true; - - langDef.mName = "GLSL"; - - inited = true; - } - return langDef; -} - -} // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/text_editor.h b/src/core/devtools/widget/text_editor.h deleted file mode 100644 index 5c3f29f11..000000000 --- a/src/core/devtools/widget/text_editor.h +++ /dev/null @@ -1,408 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2017 BalazsJako -// SPDX-License-Identifier: MIT - -// source: https://github.com/BalazsJako/ImGuiColorTextEdit - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/assert.h" -#include "imgui.h" - -namespace Core::Devtools::Widget { - -class TextEditor { -public: - enum class PaletteIndex { - Default, - Keyword, - Number, - String, - CharLiteral, - Punctuation, - Preprocessor, - Identifier, - KnownIdentifier, - PreprocIdentifier, - Comment, - MultiLineComment, - Background, - Cursor, - Selection, - ErrorMarker, - Breakpoint, - LineNumber, - CurrentLineFill, - CurrentLineFillInactive, - CurrentLineEdge, - Max - }; - - enum class SelectionMode { Normal, Word, Line }; - - struct Breakpoint { - int mLine; - bool mEnabled; - std::string mCondition; - - Breakpoint() : mLine(-1), mEnabled(false) {} - }; - - // Represents a character coordinate from the user's point of view, - // i. e. consider an uniform grid (assuming fixed-width font) on the - // screen as it is rendered, and each cell has its own coordinate, starting from 0. - // Tabs are counted as [1..mTabSize] count empty spaces, depending on - // how many space is necessary to reach the next tab stop. - // For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize - // = 4, because it is rendered as " ABC" on the screen. - struct Coordinates { - int mLine, mColumn; - Coordinates() : mLine(0), mColumn(0) {} - Coordinates(int aLine, int aColumn) : mLine(aLine), mColumn(aColumn) { - ASSERT(aLine >= 0); - ASSERT(aColumn >= 0); - } - static Coordinates Invalid() { - static Coordinates invalid(-1, -1); - return invalid; - } - - bool operator==(const Coordinates& o) const { - return mLine == o.mLine && mColumn == o.mColumn; - } - - bool operator!=(const Coordinates& o) const { - return mLine != o.mLine || mColumn != o.mColumn; - } - - bool operator<(const Coordinates& o) const { - if (mLine != o.mLine) - return mLine < o.mLine; - return mColumn < o.mColumn; - } - - bool operator>(const Coordinates& o) const { - if (mLine != o.mLine) - return mLine > o.mLine; - return mColumn > o.mColumn; - } - - bool operator<=(const Coordinates& o) const { - if (mLine != o.mLine) - return mLine < o.mLine; - return mColumn <= o.mColumn; - } - - bool operator>=(const Coordinates& o) const { - if (mLine != o.mLine) - return mLine > o.mLine; - return mColumn >= o.mColumn; - } - }; - - struct Identifier { - Coordinates mLocation; - std::string mDeclaration; - }; - - typedef std::string String; - typedef std::unordered_map Identifiers; - typedef std::unordered_set Keywords; - typedef std::map ErrorMarkers; - typedef std::unordered_set Breakpoints; - typedef std::array Palette; - typedef uint8_t Char; - - struct Glyph { - Char mChar; - PaletteIndex mColorIndex = PaletteIndex::Default; - bool mComment : 1; - bool mMultiLineComment : 1; - bool mPreprocessor : 1; - - Glyph(Char aChar, PaletteIndex aColorIndex) - : mChar(aChar), mColorIndex(aColorIndex), mComment(false), mMultiLineComment(false), - mPreprocessor(false) {} - }; - - typedef std::vector Line; - typedef std::vector Lines; - - struct LanguageDefinition { - typedef std::pair TokenRegexString; - typedef std::vector TokenRegexStrings; - typedef bool (*TokenizeCallback)(const char* in_begin, const char* in_end, - const char*& out_begin, const char*& out_end, - PaletteIndex& paletteIndex); - - std::string mName; - Keywords mKeywords; - Identifiers mIdentifiers; - Identifiers mPreprocIdentifiers; - std::string mCommentStart, mCommentEnd, mSingleLineComment; - char mPreprocChar; - bool mAutoIndentation; - - TokenizeCallback mTokenize; - - TokenRegexStrings mTokenRegexStrings; - - bool mCaseSensitive; - - LanguageDefinition() - : mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) {} - - static const LanguageDefinition& GLSL(); - }; - - TextEditor(); - ~TextEditor(); - - void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); - const LanguageDefinition& GetLanguageDefinition() const { - return mLanguageDefinition; - } - - const Palette& GetPalette() const { - return mPaletteBase; - } - void SetPalette(const Palette& aValue); - - void SetErrorMarkers(const ErrorMarkers& aMarkers) { - mErrorMarkers = aMarkers; - } - void SetBreakpoints(const Breakpoints& aMarkers) { - mBreakpoints = aMarkers; - } - - void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); - void SetText(const std::string& aText); - std::string GetText() const; - - void SetTextLines(const std::vector& aLines); - std::vector GetTextLines() const; - - std::string GetSelectedText() const; - std::string GetCurrentLineText() const; - - int GetTotalLines() const { - return (int)mLines.size(); - } - bool IsOverwrite() const { - return mOverwrite; - } - - void SetReadOnly(bool aValue); - bool IsReadOnly() const { - return mReadOnly; - } - bool IsTextChanged() const { - return mTextChanged; - } - bool IsCursorPositionChanged() const { - return mCursorPositionChanged; - } - - bool IsColorizerEnabled() const { - return mColorizerEnabled; - } - void SetColorizerEnable(bool aValue); - - Coordinates GetCursorPosition() const { - return GetActualCursorCoordinates(); - } - void SetCursorPosition(const Coordinates& aPosition); - - inline void SetHandleMouseInputs(bool aValue) { - mHandleMouseInputs = aValue; - } - inline bool IsHandleMouseInputsEnabled() const { - return mHandleKeyboardInputs; - } - - inline void SetHandleKeyboardInputs(bool aValue) { - mHandleKeyboardInputs = aValue; - } - inline bool IsHandleKeyboardInputsEnabled() const { - return mHandleKeyboardInputs; - } - - inline void SetImGuiChildIgnored(bool aValue) { - mIgnoreImGuiChild = aValue; - } - inline bool IsImGuiChildIgnored() const { - return mIgnoreImGuiChild; - } - - inline void SetShowWhitespaces(bool aValue) { - mShowWhitespaces = aValue; - } - inline bool IsShowingWhitespaces() const { - return mShowWhitespaces; - } - - void SetTabSize(int aValue); - inline int GetTabSize() const { - return mTabSize; - } - - void InsertText(const std::string& aValue); - void InsertText(const char* aValue); - - void MoveUp(int aAmount = 1, bool aSelect = false); - void MoveDown(int aAmount = 1, bool aSelect = false); - void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false); - void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false); - void MoveTop(bool aSelect = false); - void MoveBottom(bool aSelect = false); - void MoveHome(bool aSelect = false); - void MoveEnd(bool aSelect = false); - - void SetSelectionStart(const Coordinates& aPosition); - void SetSelectionEnd(const Coordinates& aPosition); - void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, - SelectionMode aMode = SelectionMode::Normal); - void SelectWordUnderCursor(); - void SelectAll(); - bool HasSelection() const; - - void Copy(); - void Cut(); - void Paste(); - void Delete(); - - bool CanUndo() const; - bool CanRedo() const; - void Undo(int aSteps = 1); - void Redo(int aSteps = 1); - - static const Palette& GetDarkPalette(); - static const Palette& GetLightPalette(); - static const Palette& GetRetroBluePalette(); - -private: - typedef std::vector> RegexList; - - struct EditorState { - Coordinates mSelectionStart; - Coordinates mSelectionEnd; - Coordinates mCursorPosition; - }; - - class UndoRecord { - public: - UndoRecord() {} - ~UndoRecord() {} - - UndoRecord(const std::string& aAdded, const TextEditor::Coordinates aAddedStart, - const TextEditor::Coordinates aAddedEnd, - - const std::string& aRemoved, const TextEditor::Coordinates aRemovedStart, - const TextEditor::Coordinates aRemovedEnd, - - TextEditor::EditorState& aBefore, TextEditor::EditorState& aAfter); - - void Undo(TextEditor* aEditor); - void Redo(TextEditor* aEditor); - - std::string mAdded; - Coordinates mAddedStart; - Coordinates mAddedEnd; - - std::string mRemoved; - Coordinates mRemovedStart; - Coordinates mRemovedEnd; - - EditorState mBefore; - EditorState mAfter; - }; - - typedef std::vector UndoBuffer; - - void ProcessInputs(); - void Colorize(int aFromLine = 0, int aCount = -1); - void ColorizeRange(int aFromLine = 0, int aToLine = 0); - void ColorizeInternal(); - float TextDistanceToLineStart(const Coordinates& aFrom) const; - void EnsureCursorVisible(); - int GetPageSize() const; - std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const; - Coordinates GetActualCursorCoordinates() const; - Coordinates SanitizeCoordinates(const Coordinates& aValue) const; - void Advance(Coordinates& aCoordinates) const; - void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd); - int InsertTextAt(Coordinates& aWhere, const char* aValue); - void AddUndo(UndoRecord& aValue); - Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const; - Coordinates FindWordStart(const Coordinates& aFrom) const; - Coordinates FindWordEnd(const Coordinates& aFrom) const; - Coordinates FindNextWord(const Coordinates& aFrom) const; - int GetCharacterIndex(const Coordinates& aCoordinates) const; - int GetCharacterColumn(int aLine, int aIndex) const; - int GetLineCharacterCount(int aLine) const; - int GetLineMaxColumn(int aLine) const; - bool IsOnWordBoundary(const Coordinates& aAt) const; - void RemoveLine(int aStart, int aEnd); - void RemoveLine(int aIndex); - Line& InsertLine(int aIndex); - void EnterCharacter(ImWchar aChar, bool aShift); - void Backspace(); - void DeleteSelection(); - std::string GetWordUnderCursor() const; - std::string GetWordAt(const Coordinates& aCoords) const; - ImU32 GetGlyphColor(const Glyph& aGlyph) const; - - void HandleKeyboardInputs(); - void HandleMouseInputs(); - void Render(); - - float mLineSpacing; - Lines mLines; - EditorState mState; - UndoBuffer mUndoBuffer; - int mUndoIndex; - - int mTabSize; - bool mOverwrite; - bool mReadOnly; - bool mWithinRender; - bool mScrollToCursor; - bool mScrollToTop; - bool mTextChanged; - bool mColorizerEnabled; - float mTextStart; // position (in pixels) where a code line starts relative to the left of the - // TextEditor. - int mLeftMargin; - bool mCursorPositionChanged; - int mColorRangeMin, mColorRangeMax; - SelectionMode mSelectionMode; - bool mHandleKeyboardInputs; - bool mHandleMouseInputs; - bool mIgnoreImGuiChild; - bool mShowWhitespaces; - - Palette mPaletteBase; - Palette mPalette; - LanguageDefinition mLanguageDefinition; - RegexList mRegexList; - - bool mCheckComments; - Breakpoints mBreakpoints; - ErrorMarkers mErrorMarkers; - ImVec2 mCharAdvance; - Coordinates mInteractiveStart, mInteractiveEnd; - std::string mLineBuffer; - uint64_t mStartTime; - - float mLastClick; -}; - -} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/libraries/dialogs/error_dialog.cpp b/src/core/libraries/dialogs/error_dialog.cpp index 811f2cb99..b122e2d0a 100644 --- a/src/core/libraries/dialogs/error_dialog.cpp +++ b/src/core/libraries/dialogs/error_dialog.cpp @@ -75,7 +75,7 @@ public: std::min(io.DisplaySize.y, 300.0f), }; - CentralizeNextWindow(); + CentralizeWindow(); SetNextWindowSize(window_size); SetNextWindowCollapsed(false); if (first_render || !io.NavActive) { diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 4d8aa8817..ce30895ca 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -519,12 +519,10 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { // Dumping them using the current ring pointer would result in files containing only the // `IndirectBuffer` command. To access the actual command stream, we need to unwrap the IB. auto acb = acb_span; - auto base_addr = reinterpret_cast(acb_ptr); const auto* indirect_buffer = reinterpret_cast(acb_span.data()); if (indirect_buffer->header.opcode == PM4ItOpcode::IndirectBuffer) { - base_addr = reinterpret_cast(indirect_buffer->Address()); - acb = {reinterpret_cast(base_addr), indirect_buffer->ib_size}; + acb = {indirect_buffer->Address(), indirect_buffer->ib_size}; } using namespace DebugStateType; @@ -534,9 +532,9 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { .submit_num = seq_num, .num2 = gnm_vqid, .data = {acb.begin(), acb.end()}, - .base_addr = base_addr, }); } + liverpool->SubmitAsc(vqid, acb_span); *asc_queue.read_addr += acb_size; @@ -1127,25 +1125,9 @@ int PS4_SYSV_ABI sceGnmInsertSetColorMarker() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceGnmInsertSetMarker(u32* cmdbuf, u32 size, const char* marker) { - LOG_TRACE(Lib_GnmDriver, "called"); - - if (cmdbuf && marker) { - const auto len = std::strlen(marker); - const u32 packet_size = ((len + 8) >> 2) + ((len + 0xc) >> 3) * 2; - if (packet_size + 2 == size) { - auto* nop = reinterpret_cast(cmdbuf); - nop->header = - PM4Type3Header{PM4ItOpcode::Nop, packet_size, PM4ShaderType::ShaderGraphics}; - nop->data_block[0] = PM4CmdNop::PayloadType::DebugSetMarker; - const auto marker_len = len + 1; - std::memcpy(&nop->data_block[1], marker, marker_len); - std::memset(reinterpret_cast(&nop->data_block[1]) + marker_len, 0, - packet_size * 4 - marker_len); - return ORBIS_OK; - } - } - return -1; +int PS4_SYSV_ABI sceGnmInsertSetMarker() { + LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); + return ORBIS_OK; } int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker() { @@ -2181,16 +2163,15 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[ .submit_num = seq_num, .num2 = cbpair, .data = {dcb_span.begin(), dcb_span.end()}, - .base_addr = reinterpret_cast(dcb_gpu_addrs[cbpair]), }); DebugState.PushQueueDump({ .type = QueueType::ccb, .submit_num = seq_num, .num2 = cbpair, .data = {ccb_span.begin(), ccb_span.end()}, - .base_addr = reinterpret_cast(ccb), }); } + liverpool->SubmitGfx(dcb_span, ccb_span); } diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index a95daa90d..33bccf427 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -108,7 +108,7 @@ s32 PS4_SYSV_ABI sceGnmInsertPopMarker(u32* cmdbuf, u32 size); s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color); s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker); int PS4_SYSV_ABI sceGnmInsertSetColorMarker(); -s32 PS4_SYSV_ABI sceGnmInsertSetMarker(u32* cmdbuf, u32 size, const char* marker); +int PS4_SYSV_ABI sceGnmInsertSetMarker(); int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker(); s32 PS4_SYSV_ABI sceGnmInsertWaitFlipDone(u32* cmdbuf, u32 size, s32 vo_handle, u32 buf_idx); int PS4_SYSV_ABI sceGnmIsCoredumpValid(); diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp index 01e56f8b8..c4bf84258 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp @@ -98,7 +98,7 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) { param_sfo.Open(param_sfo_path); auto last_write = param_sfo.GetLastWrite(); -#if defined(_WIN32) && !defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) +#ifdef _WIN32 auto utc_time = std::chrono::file_clock::to_utc(last_write); #else auto utc_time = std::chrono::file_clock::to_sys(last_write); @@ -402,7 +402,7 @@ void SaveDialogUi::Draw() { }; } - CentralizeNextWindow(); + CentralizeWindow(); SetNextWindowSize(window_size); SetNextWindowCollapsed(false); if (first_render || !io.NavActive) { diff --git a/src/core/libraries/system/msgdialog_ui.cpp b/src/core/libraries/system/msgdialog_ui.cpp index 862f5a569..ae1dced12 100644 --- a/src/core/libraries/system/msgdialog_ui.cpp +++ b/src/core/libraries/system/msgdialog_ui.cpp @@ -256,7 +256,7 @@ void MsgDialogUi::Draw() { std::min(io.DisplaySize.y, 300.0f), }; - CentralizeNextWindow(); + CentralizeWindow(); SetNextWindowSize(window_size); SetNextWindowCollapsed(false); if (first_render || !io.NavActive) { diff --git a/src/imgui/imgui_std.h b/src/imgui/imgui_std.h index ce79da705..168204ea8 100644 --- a/src/imgui/imgui_std.h +++ b/src/imgui/imgui_std.h @@ -25,14 +25,9 @@ inline float FastInFastOutCubic(float x) { } // namespace Easing -inline void CentralizeNextWindow() { - const auto display_size = GetIO().DisplaySize; - SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f}); -} - inline void CentralizeWindow() { const auto display_size = GetIO().DisplaySize; - SetWindowPos(display_size / 2.0f); + SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f}); } inline void KeepWindowInside(ImVec2 display_size = GetIO().DisplaySize) { diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index 7f9c69d49..ba4a05d01 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -7,7 +7,6 @@ #include #include "common/assert.h" -#include "common/config.h" #include "common/io_file.h" #include "common/polyfill_thread.h" #include "imgui_impl_vulkan.h" @@ -148,11 +147,6 @@ void WorkerLoop() { g_job_list.pop_front(); g_job_list_mtx.unlock(); - if (Config::vkCrashDiagnosticEnabled()) { - // FIXME: Crash diagnostic hangs when building the command buffer here - continue; - } - if (!path.empty()) { // Decode PNG from file Common::FS::IOFile file(path, Common::FS::FileAccessMode::Read); if (!file.IsOpen()) { diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index d4ebeb883..b3b718836 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -6,7 +6,6 @@ #include "common/debug.h" #include "common/polyfill_thread.h" #include "common/thread.h" -#include "core/debug_state.h" #include "core/libraries/videoout/driver.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/pm4_cmds.h" @@ -188,7 +187,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(dcb.data()); while (!dcb.empty()) { const auto* header = reinterpret_cast(dcb.data()); const u32 type = header->type; @@ -361,9 +359,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanindex_base_hi); regs.num_indices = draw_index->index_count; regs.draw_initiator = draw_index->draw_initiator; - if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); - } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndex2", cmd_address)); @@ -378,9 +373,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanmax_size; regs.num_indices = draw_index_off->index_count; regs.draw_initiator = draw_index_off->draw_initiator; - if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); - } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin( @@ -394,9 +386,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); regs.num_indices = draw_index->index_count; regs.draw_initiator = draw_index->draw_initiator; - if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); - } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexAuto", cmd_address)); @@ -410,9 +399,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandata_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(PM4CmdDrawIndirect::DrawInstancedArgs); - if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); - } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndirect", cmd_address)); @@ -427,9 +413,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandata_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(PM4CmdDrawIndexIndirect::DrawIndexInstancedArgs); - if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); - } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin( @@ -445,9 +428,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandim_y; regs.cs_program.dim_z = dispatch_direct->dim_z; regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; - if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); - } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:Dispatch", cmd_address)); @@ -462,9 +442,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandata_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions); - if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); - } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin( @@ -599,7 +576,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span acb, int vqid) { TracyFiberEnter(acb_task_name); - auto base_addr = reinterpret_cast(acb.data()); while (!acb.empty()) { const auto* header = reinterpret_cast(acb.data()); const u32 type = header->type; @@ -644,9 +620,6 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { regs.cs_program.dim_y = dispatch_direct->dim_y; regs.cs_program.dim_z = dispatch_direct->dim_z; regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; - if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); - } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address)); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index a4cf79334..1c994d0a0 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -772,8 +772,6 @@ struct Liverpool { BitField<27, 1, u32> fmask_compress_1frag_only; BitField<28, 1, u32> dcc_enable; BitField<29, 1, u32> cmask_addr_type; - - u32 u32all; } info; union Color0Attrib { BitField<0, 5, TilingMode> tile_mode_index; @@ -782,8 +780,6 @@ struct Liverpool { BitField<12, 3, u32> num_samples_log2; BitField<15, 2, u32> num_fragments_log2; BitField<17, 1, u32> force_dst_alpha_1; - - u32 u32all; } attrib; INSERT_PADDING_WORDS(1); u32 cmask_base_address; @@ -939,7 +935,7 @@ struct Liverpool { BitField<5, 1, u32> gs_en; BitField<6, 1, u32> vs_en; - bool IsStageEnabled(u32 stage) const { + bool IsStageEnabled(u32 stage) { switch (stage) { case 0: case 1: diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index a7a862ea3..b9fbfcb89 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -213,7 +213,6 @@ struct PM4CmdNop { enum PayloadType : u32 { DebugMarkerPush = 0x68750001u, ///< Begin of GPU event scope DebugMarkerPop = 0x68750002u, ///< End of GPU event scope - DebugSetMarker = 0x68750003u, ///< Set GPU event marker SetVsharpInUdata = 0x68750004u, ///< Indicates that V# will be set in the next packet SetTsharpInUdata = 0x68750005u, ///< Indicates that T# will be set in the next packet SetSsharpInUdata = 0x68750006u, ///< Indicates that S# will be set in the next packet