diff --git a/CMakeLists.txt b/CMakeLists.txt index 781e93e10..9cfc0d8ef 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,9 +47,15 @@ else() message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}") endif() -if (APPLE AND ARCHITECTURE STREQUAL "x86_64") +if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") # Exclude ARM homebrew path to avoid conflicts when cross compiling. list(APPEND CMAKE_IGNORE_PREFIX_PATH "/opt/homebrew") + + # Need to reconfigure pkg-config to use the right architecture library paths. + # It's not ideal to override these but otherwise the build breaks just by having pkg-config installed. + set(ENV{PKG_CONFIG_DIR} "") + set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig:${CMAKE_SYSROOT}/usr/local/lib/pkgconfig:${CMAKE_SYSROOT}/usr/local/share/pkgconfig") + set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT}) endif() # This function should be passed a list of all files in a target. It will automatically generate file groups @@ -103,6 +109,7 @@ find_package(Boost 1.84.0 CONFIG) find_package(FFmpeg 5.1.2 MODULE) find_package(fmt 10.2.0 CONFIG) find_package(glslang 14.2.0 CONFIG) +find_package(half 1.12.0 MODULE) find_package(magic_enum 0.9.6 CONFIG) find_package(RenderDoc 1.6.0 MODULE) find_package(SDL3 3.1.2 CONFIG) @@ -432,6 +439,8 @@ set(COMMON src/common/logging/backend.cpp src/common/version.h src/common/ntapi.h src/common/ntapi.cpp + src/common/number_utils.h + src/common/number_utils.cpp src/common/memory_patcher.h src/common/memory_patcher.cpp src/common/scm_rev.cpp @@ -771,7 +780,7 @@ endif() create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") @@ -794,9 +803,6 @@ if (APPLE) # Replacement for std::chrono::time_zone target_link_libraries(shadps4 PRIVATE date::date-tz) - - # Half float conversions for F16C patches - target_link_libraries(shadps4 PRIVATE half) endif() if (NOT ENABLE_QT_GUI) diff --git a/cmake/Findhalf.cmake b/cmake/Findhalf.cmake new file mode 100644 index 000000000..f95a791df --- /dev/null +++ b/cmake/Findhalf.cmake @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +find_path(half_INCLUDE_DIR NAMES half.hpp PATH_SUFFIXES half) + +if (half_INCLUDE_DIR) + file(STRINGS "${half_INCLUDE_DIR}/half.hpp" _ver_line + REGEX "^// Version [0-9.]+$" + LIMIT_COUNT 1 + ) + string(REGEX MATCH "[0-9.]+" half_VERSION "${_ver_line}") + unset(_ver_line) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(half + REQUIRED_VARS half_INCLUDE_DIR + VERSION_VAR half_VERSION +) + +if (half_FOUND AND NOT TARGET half::half) + add_library(half::half INTERFACE IMPORTED) + set_target_properties(half::half PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${half_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(half_INCLUDE_DIR) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 2f9336c21..80f49f527 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -141,11 +141,14 @@ if (WIN32) target_compile_options(sirit PUBLIC "-Wno-error=unused-command-line-argument") endif() -if (APPLE) - # half +# half +if (NOT TARGET half::half) add_library(half INTERFACE) target_include_directories(half INTERFACE half/include) + add_library(half::half ALIAS half) +endif() +if (APPLE) # date if (NOT TARGET date::date-tz) option(BUILD_TZ_LIB "" ON) diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 72af7b5cd..bc8a2763c 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -81,7 +81,9 @@ #pragma pack(1) template struct BitField { -private: + + using Type = T; + // UnderlyingType is T for non-enum types and the underlying type of T if // T is an enumeration. Note that T is wrapped within an enable_if in the // former case to workaround compile errors which arise when using @@ -92,7 +94,6 @@ private: // We store the value as the unsigned type to avoid undefined behaviour on value shifting using StorageType = std::make_unsigned_t; -public: /// Constants to allow limited introspection of fields if needed static constexpr std::size_t position = Position; static constexpr std::size_t bits = Bits; diff --git a/src/common/number_utils.cpp b/src/common/number_utils.cpp new file mode 100644 index 000000000..af29e5cd3 --- /dev/null +++ b/src/common/number_utils.cpp @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "common/number_utils.h" +#include "video_core/amdgpu/pixel_format.h" +#include "video_core/amdgpu/types.h" + +#define UF11_EXPONENT_SHIFT 6 +#define UF10_EXPONENT_SHIFT 5 + +#define RGB9E5_MANTISSA_BITS 9 +#define RGB9E5_EXP_BIAS 1 + +#define F32_INFINITY 0x7f800000 + +namespace NumberUtils { + +float Uf11ToF32(u16 val) { + union { + float f; + u32 ui; + } f32; + + int exponent = (val & 0x07c0) >> UF11_EXPONENT_SHIFT; + int mantissa = (val & 0x003f); + + f32.f = 0.0; + + if (exponent == 0) { + if (mantissa != 0) { + const float scale = 1.0 / (1 << 20); + f32.f = scale * mantissa; + } + } else if (exponent == 31) { + f32.ui = F32_INFINITY | mantissa; + } else { + float scale, decimal; + exponent -= 15; + if (exponent < 0) { + scale = 1.0f / (1 << -exponent); + } else { + scale = (float)(1 << exponent); + } + decimal = 1.0f + (float)mantissa / 64; + f32.f = scale * decimal; + } + + return f32.f; +} + +float Uf10ToF32(u16 val) { + union { + float f; + u32 ui; + } f32; + + int exponent = (val & 0x03e0) >> UF10_EXPONENT_SHIFT; + int mantissa = (val & 0x001f); + + f32.f = 0.0; + + if (exponent == 0) { + if (mantissa != 0) { + const float scale = 1.0 / (1 << 19); + f32.f = scale * mantissa; + } + } else if (exponent == 31) { + f32.ui = F32_INFINITY | mantissa; + } else { + float scale, decimal; + exponent -= 15; + if (exponent < 0) { + scale = 1.0f / (1 << -exponent); + } else { + scale = (float)(1 << exponent); + } + decimal = 1.0f + (float)mantissa / 32; + f32.f = scale * decimal; + } + + return f32.f; +} + +float Uf16ToF32(u16 val) { + return half_float::half_cast(reinterpret_cast(val)); +} + +float U2ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 3.0f; + return float(val * c); +} + +float S2ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 1.0f; + return float(val * c); +} + +float U4ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 15.0f; + return float(val * c); +} + +float S4ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 7.0f; + return float(val * c); +} + +float U5ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 31.0f; + return float(val * c); +} + +float S5ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 15.0f; + return float(val * c); +} + +float U6ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 63.0f; + return float(val * c); +} + +float S6ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 31.0f; + return float(val * c); +} + +float U8ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 255.0f; + return float(val * c); +} + +float S8ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 127.0f; + return float(val * c); +} + +float U10ToUnorm(u16 val) { + static constexpr auto c = 1.0f / 1023.0f; + return float(val * c); +} + +float S10ToSnorm(s16 val) { + static constexpr auto c = 1.0f / 511.0f; + return float(val * c); +} + +float U16ToUnorm(u16 val) { + static constexpr auto c = 1.0f / 65535.0f; + return float(val * c); +} + +float S16ToSnorm(s16 val) { + static constexpr auto c = 1.0f / 32767.0f; + return float(val * c); +} + +} // namespace NumberUtils \ No newline at end of file diff --git a/src/common/number_utils.h b/src/common/number_utils.h new file mode 100644 index 000000000..05c62a1cb --- /dev/null +++ b/src/common/number_utils.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace NumberUtils { + +float Uf11ToF32(u16 val); +float Uf10ToF32(u16 val); +float Uf16ToF32(u16 val); +float U2ToUnorm(u8 val); +float S2ToSnorm(s8 val); +float U4ToUnorm(u8 val); +float S4ToSnorm(s8 val); +float U5ToUnorm(u8 val); +float S5ToSnorm(s8 val); +float U6ToUnorm(u8 val); +float S6ToSnorm(s8 val); +float U8ToUnorm(u8 val); +float S8ToSnorm(s8 val); +float U10ToUnorm(u16 val); +float S10ToSnorm(s16 val); +float U16ToUnorm(u16 val); +float S16ToSnorm(s16 val); + +} // namespace NumberUtils diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 93b00285d..adcb0cadb 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -102,6 +102,10 @@ void DebugStateImpl::RequestFrameDump(s32 count) { gnm_frame_dump_request_count = count; frame_dump_list.clear(); frame_dump_list.resize(count); + const auto f = gnm_frame_count.load() + 1; + for (size_t i = 0; i < count; ++i) { + frame_dump_list[i].frame_id = f + i; + } waiting_submit_pause = true; } @@ -139,7 +143,7 @@ void DebugStateImpl::PushQueueDump(QueueDump dump) { } void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, - const AmdGpu::Liverpool::Regs& regs) { + const AmdGpu::Liverpool::Regs& regs, bool is_compute) { std::scoped_lock lock{frame_dump_list_mutex}; const auto it = waiting_reg_dumps.find(header_addr); if (it == waiting_reg_dumps.end()) { @@ -150,15 +154,24 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, 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()}, - }; + if (is_compute) { + dump.is_compute = true; + const auto& cs = dump.regs.cs_program; + dump.cs_data = ComputerShaderDump{ + .cs_program = cs, + .code = std::vector{cs.Code().begin(), cs.Code().end()}, + }; + } else { + 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..cd1c6aa93 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -36,9 +36,9 @@ class FrameGraph; namespace DebugStateType { enum class QueueType { - acb, - dcb, - ccb, + dcb = 0, + ccb = 1, + acb = 2, }; struct QueueDump { @@ -54,13 +54,21 @@ struct ShaderDump { std::vector code{}; }; +struct ComputerShaderDump { + Vulkan::Liverpool::ComputeProgram cs_program{}; + std::vector code{}; +}; + struct RegDump { + bool is_compute{false}; static constexpr size_t MaxShaderStages = 5; Vulkan::Liverpool::Regs regs{}; std::array stages{}; + ComputerShaderDump cs_data{}; }; struct FrameDump { + u32 frame_id; std::vector queues; std::unordered_map regs; // address -> reg dump }; @@ -143,7 +151,7 @@ public: void PushQueueDump(QueueDump dump); void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, - const AmdGpu::Liverpool::Regs& regs); + const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); }; } // namespace DebugStateType diff --git a/src/core/devtools/help.txt b/src/core/devtools/help.txt new file mode 100644 index 000000000..9670c5cea --- /dev/null +++ b/src/core/devtools/help.txt @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +R"( +* If you hold shift, you can move the window without docking it. +* You don't need to close every window you open. When a parent window is closed, all its children will be closed too. +* If you want to inspect or compare more than 1 frame dump without undocking, there's a option to keep showing opened popups even when in hide/minimize the frame dump window. +* To use the disassembly viewer, you need to set up a cli to use a external disassembler and use "{src}" as a placeholder for the source code file, e.g. dis.exe --some-opt "{src}" +)" \ No newline at end of file diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 17ef43bbc..38b17c863 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -31,6 +31,12 @@ static float debug_popup_timing = 3.0f; static bool just_opened_options = false; +// clang-format off +static std::string help_text = +#include "help.txt" + ; +// clang-format on + void L::DrawMenuBar() { const auto& ctx = *GImGui; const auto& io = ctx.IO; @@ -38,6 +44,7 @@ void L::DrawMenuBar() { auto isSystemPaused = DebugState.IsGuestThreadsPaused(); bool open_popup_options = false; + bool open_popup_help = false; if (BeginMainMenuBar()) { if (BeginMenu("Options")) { @@ -60,6 +67,7 @@ void L::DrawMenuBar() { ImGui::EndMenu(); } open_popup_options = MenuItem("Options"); + open_popup_help = MenuItem("Help & Tips"); ImGui::EndMenu(); } EndMainMenuBar(); @@ -84,6 +92,9 @@ void L::DrawMenuBar() { OpenPopup("GPU Tools Options"); just_opened_options = true; } + if (open_popup_help) { + OpenPopup("HelpTips"); + } } void L::DrawAdvanced() { @@ -154,25 +165,49 @@ void L::DrawAdvanced() { if (BeginPopupModal("GPU Tools Options", &close_popup_options, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { static char disassembly_cli[512]; + static bool frame_dump_render_on_collapse; if (just_opened_options) { just_opened_options = false; auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1); disassembly_cli[s] = '\0'; + frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse; } InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli)); if (IsItemHovered()) { SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")"); } + Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse); + if (IsItemHovered()) { + SetTooltip("When a frame dump is collapsed, it will keep\n" + "showing all opened popups related to it"); + } if (Button("Save")) { Options.disassembly_cli = disassembly_cli; + Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse; SaveIniSettingsToDisk(io.IniFilename); CloseCurrentPopup(); } EndPopup(); } + + if (BeginPopup("HelpTips", ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove)) { + CentralizeWindow(); + + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{10.0f}); + PushTextWrapPos(600.0f); + + const char* begin = help_text.data(); + TextUnformatted(begin, begin + help_text.size()); + + PopTextWrapPos(); + PopStyleVar(); + + EndPopup(); + } } void L::DrawSimple() { diff --git a/src/core/devtools/options.cpp b/src/core/devtools/options.cpp index 82fa6e87e..1b49da76b 100644 --- a/src/core/devtools/options.cpp +++ b/src/core/devtools/options.cpp @@ -11,14 +11,20 @@ TOptions Options; void LoadOptionsConfig(const char* line) { char str[512]; + int i; if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) { Options.disassembly_cli = str; return; } + if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) { + Options.frame_dump_render_on_collapse = i != 0; + return; + } } void SerializeOptionsConfig(ImGuiTextBuffer* buf) { buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str()); + buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse); } } // namespace Core::Devtools diff --git a/src/core/devtools/options.h b/src/core/devtools/options.h index 9d291d768..c3a8aaf31 100644 --- a/src/core/devtools/options.h +++ b/src/core/devtools/options.h @@ -10,7 +10,8 @@ struct ImGuiTextBuffer; namespace Core::Devtools { struct TOptions { - std::string disassembly_cli; + std::string disassembly_cli{}; + bool frame_dump_render_on_collapse{false}; }; extern TOptions Options; diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index f5d31efef..9a42f8238 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -1173,7 +1173,7 @@ CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump, } } -void CmdListViewer::Draw() { +void CmdListViewer::Draw(bool only_batches_view) { const auto& ctx = *GetCurrentContext(); if (batch_view.open) { @@ -1188,6 +1188,10 @@ void CmdListViewer::Draw() { ++it; } + if (only_batches_view) { + return; + } + if (cmdb_view.Open) { MemoryEditor::Sizes s; cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr); @@ -1228,7 +1232,7 @@ void CmdListViewer::Draw() { Text("size : %04llX", cmdb_size); Separator(); - if (TreeNode("Batches")) { + { int tree_depth = 0; int tree_depth_show = 0; @@ -1283,9 +1287,10 @@ void CmdListViewer::Draw() { auto const* pm4_hdr = reinterpret_cast(cmdb_addr + batch.start_addr); + bool ignore_header = false; char batch_hdr[128]; if (batch.type == static_cast(0xFF)) { - snprintf(batch_hdr, sizeof(batch_hdr), "State batch"); + ignore_header = true; } else if (!batch.marker.empty()) { snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s | %s", cmdb_addr + batch.start_addr, batch.id, @@ -1309,22 +1314,35 @@ void CmdListViewer::Draw() { 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.SetData(data, name, batch_id); pop.open = true; } else { - batch_view.SetData(data, batch_id); - batch_view.open = true; + if (batch_view.open && + this->last_selected_batch == static_cast(batch_id)) { + batch_view.open = false; + } else { + this->last_selected_batch = static_cast(batch_id); + batch_view.SetData(data, name, batch_id); + if (!batch_view.open || !batch_view.moved) { + batch_view.open = true; + const auto pos = GetItemRectMax() + ImVec2{5.0f, 0.0f}; + batch_view.SetPos(pos); + } + } } } }; bool show_batch_content = true; - if (group_batches) { + if (group_batches && !ignore_header) { show_batch_content = CollapsingHeader(batch_hdr, ImGuiTreeNodeFlags_AllowOverlap); SameLine(GetContentRegionAvail().x - 40.0f); - if (Button("->", {40.0f, 0.0f})) { + const char* text = + last_selected_batch == static_cast(batch_id) && batch_view.open ? "X" + : "->"; + if (Button(text, {40.0f, 0.0f})) { open_batch_view(); } } @@ -1332,7 +1350,7 @@ void CmdListViewer::Draw() { if (show_batch_content) { auto processed_size = 0ull; auto bb = ctx.LastItemData.Rect; - if (group_batches) { + if (group_batches && !ignore_header) { Indent(); } auto const batch_sz = batch.end_addr - batch.start_addr; @@ -1354,7 +1372,12 @@ void CmdListViewer::Draw() { if (!group_batches) { if (IsDrawCall(op)) { SameLine(GetContentRegionAvail().x - 40.0f); - if (Button("->", {40.0f, 0.0f})) { + const char* text = + last_selected_batch == static_cast(batch_id) && + batch_view.open + ? "X" + : "->"; + if (Button(text, {40.0f, 0.0f})) { open_batch_view(); } } @@ -1426,7 +1449,7 @@ void CmdListViewer::Draw() { processed_size += processed_len; } - if (group_batches) { + if (group_batches && !ignore_header) { Unindent(); }; bb = {{0.0f, bb.Max.y}, ctx.LastItemData.Rect.Max}; @@ -1450,8 +1473,6 @@ void CmdListViewer::Draw() { PopID(); highlight_batch = current_highlight_batch; - - TreePop(); } } EndChild(); diff --git a/src/core/devtools/widget/cmd_list.h b/src/core/devtools/widget/cmd_list.h index 971c8fffe..ed71d0b76 100644 --- a/src/core/devtools/widget/cmd_list.h +++ b/src/core/devtools/widget/cmd_list.h @@ -53,6 +53,8 @@ class CmdListViewer { u32 highlight_batch{~0u}; RegView batch_view; + int last_selected_batch{-1}; + std::vector extra_batch_view; static void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body); @@ -68,7 +70,7 @@ public: explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector& cmd_list, uintptr_t base_addr = 0, std::string name = ""); - void Draw(); + void Draw(bool only_batches_view = false); }; } // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index 701d16399..e650f5fc7 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -4,15 +4,16 @@ #pragma once #include +#include #include #include +#include "common/bit_field.h" #include "common/types.h" #include "video_core/amdgpu/pm4_opcodes.h" namespace Core::Devtools::Widget { - /* * Generic PM4 header */ @@ -57,16 +58,24 @@ void DrawRow(const char* text, const char* fmt, Args... 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 DrawValueRow(const char* text, T value) { + if constexpr (std::is_enum_v) { + return DrawRow(text, "%X (%s)", value, magic_enum::enum_name(value).data()); + } else if constexpr (std::is_integral_v) { + return DrawRow(text, "%X", value); + } else if constexpr (std::is_base_of_v, T>) { + return DrawValueRow(text, value.Value()); + } else { + static_assert(false, "Unsupported type"); + } } template -void DrawMultipleRow(const char* text, const char* fmt, V arg, Extra&&... extra_args) { - DrawRow(text, fmt, arg); +void DrawValueRowList(const char* text, V arg, Extra&&... extra_args) { + DrawValueRow(text, arg); if constexpr (sizeof...(extra_args) > 0) { - DrawMultipleRow(std::forward(extra_args)...); + DrawValueRowList(std::forward(extra_args)...); } } diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 29b5cb8ee..86ba7b86e 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -7,6 +7,7 @@ #include #include "common/io_file.h" +#include "core/devtools/options.h" #include "frame_dump.h" #include "imgui_internal.h" #include "imgui_memory_editor.h" @@ -45,11 +46,14 @@ FrameDumpViewer::FrameDumpViewer(const FrameDump& _frame_dump) selected_submit_num = 0; selected_queue_num2 = 0; + has_queue_type.fill(false); 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); + if (!cmd.data.empty()) { + has_queue_type[static_cast(cmd.type)] = true; + } + const auto fname = fmt::format("F{} {}_{:02}_{:02}", frame_dump->frame_id, + magic_enum::enum_name(cmd.type), cmd.submit_num, cmd.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); @@ -64,9 +68,28 @@ void FrameDumpViewer::Draw() { return; } + const auto try_select = [&, this] { + const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) { + return cmd.type == selected_queue_type && + (selected_submit_num == -1 || cmd.submit_num == selected_submit_num) && + (selected_queue_num2 == -1 || cmd.num2 == selected_queue_num2); + }); + if (it != frame_dump->queues.end()) { + selected_cmd = static_cast(std::distance(frame_dump->queues.begin(), it)); + selected_submit_num = static_cast(frame_dump->queues[selected_cmd].submit_num); + selected_queue_num2 = static_cast(frame_dump->queues[selected_cmd].num2); + } + }; + + bool is_showing = Options.frame_dump_render_on_collapse; + bool is_collapsed = true; + char name[32]; - snprintf(name, sizeof(name), "Frame #%d dump", id); + snprintf(name, sizeof(name), "Frame #%d dump", frame_dump->frame_id); if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) { + is_showing = true; + is_collapsed = false; + if (IsWindowAppearing()) { auto window = GetCurrentWindow(); static ImGuiID dock_id = ImHashStr("FrameDumpDock"); @@ -79,12 +102,15 @@ void FrameDumpViewer::Draw() { if (BeginCombo("##select_queue_type", magic_enum::enum_name(selected_queue_type).data(), ImGuiComboFlags_WidthFitPreview)) { bool selected = false; -#define COMBO(x) C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected) - COMBO(QueueType::acb) +#define COMBO(x) \ + if (has_queue_type[static_cast(x)]) \ + C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected) COMBO(QueueType::dcb); COMBO(QueueType::ccb); + COMBO(QueueType::acb); if (selected) { selected_submit_num = selected_queue_num2 = -1; + try_select(); } EndCombo(); } @@ -111,9 +137,9 @@ void FrameDumpViewer::Draw() { SameLine(); if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(), ImGuiComboFlags_WidthFitPreview)) { - std::array available_submits{}; + std::array available_submits{false}; for (const auto& cmd : frame_dump->queues) { - if (cmd.type == selected_queue_type) { + if (cmd.type == selected_queue_type && !cmd.data.empty()) { available_submits[cmd.submit_num] = true; } } @@ -128,6 +154,7 @@ void FrameDumpViewer::Draw() { } if (selected) { selected_queue_num2 = -1; + try_select(); } EndCombo(); } @@ -136,9 +163,10 @@ void FrameDumpViewer::Draw() { SameLine(); if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(), ImGuiComboFlags_WidthFitPreview)) { - std::array available_queues{}; + std::array available_queues{false}; for (const auto& cmd : frame_dump->queues) { - if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) { + if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num && + !cmd.data.empty()) { available_queues[cmd.num2] = true; } } @@ -152,21 +180,14 @@ void FrameDumpViewer::Draw() { } } if (selected) { - 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)); - } + try_select(); } EndCombo(); } EndGroup(); - - if (selected_cmd != -1) { - cmd_list_viewer[selected_cmd].Draw(); - } + } + if (is_showing && selected_cmd != -1) { + cmd_list_viewer[selected_cmd].Draw(is_collapsed); } End(); } diff --git a/src/core/devtools/widget/frame_dump.h b/src/core/devtools/widget/frame_dump.h index 2b3ff2411..cc4fe6381 100644 --- a/src/core/devtools/widget/frame_dump.h +++ b/src/core/devtools/widget/frame_dump.h @@ -20,6 +20,7 @@ class FrameDumpViewer { int id; std::vector cmd_list_viewer; + std::array has_queue_type; DebugStateType::QueueType selected_queue_type; s32 selected_submit_num; diff --git a/src/core/devtools/widget/reg_popup.cpp b/src/core/devtools/widget/reg_popup.cpp index d012437c3..0633e76e6 100644 --- a/src/core/devtools/widget/reg_popup.cpp +++ b/src/core/devtools/widget/reg_popup.cpp @@ -9,6 +9,7 @@ #include "cmd_list.h" #include "common.h" +#include "imgui/imgui_std.h" using namespace ImGui; using magic_enum::enum_name; @@ -21,13 +22,13 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) { // 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 + DrawValueRowList( + "BASE_ADDR", buffer.base_address, + "PITCH.TILE_MAX", buffer.pitch.tile_max, + "PITCH.FMASK_TILE_MAX", buffer.pitch.fmask_tile_max, + "SLICE.TILE_MAX", buffer.slice.tile_max, + "VIEW.SLICE_START", buffer.view.slice_start, + "VIEW.SLICE_MAX", buffer.view.slice_max ); TableNextRow(); @@ -49,31 +50,25 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) { } 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 + DrawValueRowList( + "CMASK_BASE_EXT", buffer.cmask_base_address, + "FMASK_BASE_EXT", buffer.fmask_base_address, + "FMASK_SLICE.TILE_MAX", buffer.fmask_slice.tile_max, + "CLEAR_WORD0", buffer.clear_word0, + "CLEAR_WORD1", buffer.clear_word1, + "Pitch()", buffer.Pitch(), + "Height()", buffer.Height(), + "Address()", buffer.Address(), + "CmaskAddress", buffer.CmaskAddress(), + "FmaskAddress", buffer.FmaskAddress(), + "NumSamples()", buffer.NumSamples(), + "NumSlices()", buffer.NumSlices(), + "GetColorSliceSize()", buffer.GetColorSliceSize(), + "GetTilingMode()", buffer.GetTilingMode(), + "IsTiled()", buffer.IsTiled(), + "NumFormat()", buffer.NumFormat() ); - 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(); @@ -89,37 +84,34 @@ void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) { 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() + DrawValueRowList( + "Z_INFO.FORMAT", depth_buffer.z_info.format, + "Z_INFO.NUM_SAMPLES", depth_buffer.z_info.num_samples, + "Z_INFO.TILE_SPLIT", depth_buffer.z_info.tile_split, + "Z_INFO.TILE_MODE_INDEX", depth_buffer.z_info.tile_mode_index, + "Z_INFO.DECOMPRESS_ON_N_ZPLANES", depth_buffer.z_info.decompress_on_n_zplanes, + "Z_INFO.ALLOW_EXPCLEAR", depth_buffer.z_info.allow_expclear, + "Z_INFO.READ_SIZE", depth_buffer.z_info.read_size, + "Z_INFO.TILE_SURFACE_EN", depth_buffer.z_info.tile_surface_en, + "Z_INFO.CLEAR_DISALLOWED", depth_buffer.z_info.clear_disallowed, + "Z_INFO.ZRANGE_PRECISION", depth_buffer.z_info.zrange_precision, + "STENCIL_INFO.FORMAT", depth_buffer.stencil_info.format, + "Z_READ_BASE", depth_buffer.z_read_base, + "STENCIL_READ_BASE", depth_buffer.stencil_read_base, + "Z_WRITE_BASE", depth_buffer.z_write_base, + "STENCIL_WRITE_BASE", depth_buffer.stencil_write_base, + "DEPTH_SIZE.PITCH_TILE_MAX", depth_buffer.depth_size.pitch_tile_max, + "DEPTH_SIZE.HEIGHT_TILE_MAX", depth_buffer.depth_size.height_tile_max, + "DEPTH_SLICE.TILE_MAX", depth_buffer.depth_slice.tile_max, + "Pitch()", depth_buffer.Pitch(), + "Height()", depth_buffer.Height(), + "Address()", depth_buffer.Address(), + "NumSamples()", depth_buffer.NumSamples(), + "NumBits()", depth_buffer.NumBits(), + "GetDepthSliceSize()", depth_buffer.GetDepthSliceSize() ); // clang-format on + EndTable(); } SeparatorText("Depth control"); @@ -127,19 +119,17 @@ void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) { 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 + DrawValueRowList( + "STENCIL_ENABLE", depth_control.stencil_enable, + "DEPTH_ENABLE", depth_control.depth_enable, + "DEPTH_WRITE_ENABLE", depth_control.depth_write_enable, + "DEPTH_BOUNDS_ENABLE", depth_control.depth_bounds_enable, + "DEPTH_FUNC", depth_control.depth_func, + "BACKFACE_ENABLE", depth_control.backface_enable, + "STENCIL_FUNC", depth_control.stencil_ref_func, + "STENCIL_FUNC_BF", depth_control.stencil_bf_func, + "ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", depth_control.enable_color_writes_on_depth_fail, + "DISABLE_COLOR_WRITES_ON_DEPTH_PASS", depth_control.disable_color_writes_on_depth_pass ); // clang-format on @@ -152,24 +142,45 @@ RegPopup::RegPopup() { id = unique_id++; } -void RegPopup::SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id) { +void RegPopup::SetData(const std::string& base_title, AmdGpu::Liverpool::ColorBuffer color_buffer, + u32 cb_id) { this->data = color_buffer; - this->title = fmt::format("Batch #{} CB #{}", batch_id, cb_id); + this->title = fmt::format("{}/CB #{}", base_title, cb_id); } -void RegPopup::SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer, - AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id) { +void RegPopup::SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBuffer depth_buffer, + AmdGpu::Liverpool::DepthControl depth_control) { this->data = std::make_tuple(depth_buffer, depth_control); - this->title = fmt::format("Batch #{} Depth", batch_id); + this->title = fmt::format("{}/Depth", base_title); +} + +void RegPopup::SetPos(ImVec2 pos, bool auto_resize) { + char name[128]; + snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id); + Begin(name, &open, flags); + SetWindowPos(pos); + if (auto_resize) { + if (std::holds_alternative(data)) { + SetWindowSize({365.0f, 520.0f}); + KeepWindowInside(); + } else if (std::holds_alternative(data)) { + SetWindowSize({404.0f, 543.0f}); + KeepWindowInside(); + } + } + last_pos = GetWindowPos(); + moved = false; + End(); } void RegPopup::Draw() { - char name[128]; snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id); + if (Begin(name, &open, flags)) { + if (GetWindowPos() != last_pos) { + moved = true; + } - 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)) { @@ -178,5 +189,4 @@ void RegPopup::Draw() { } End(); } - } // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/reg_popup.h b/src/core/devtools/widget/reg_popup.h index ba4224d73..9ccd60ac0 100644 --- a/src/core/devtools/widget/reg_popup.h +++ b/src/core/devtools/widget/reg_popup.h @@ -5,6 +5,8 @@ #include +#include + #include "common/types.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" @@ -12,25 +14,31 @@ namespace Core::Devtools::Widget { class RegPopup { int id; + ImGuiWindowFlags flags{ImGuiWindowFlags_NoSavedSettings}; using DepthBuffer = std::tuple; + ImVec2 last_pos; std::variant data; std::string title{}; - void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer); + static void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer); - void DrawDepthBuffer(const DepthBuffer& depth_data); + static void DrawDepthBuffer(const DepthBuffer& depth_data); public: bool open = false; + bool moved = false; RegPopup(); - void SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id); + void SetData(const std::string& base_title, AmdGpu::Liverpool::ColorBuffer color_buffer, + u32 cb_id); - void SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer, - AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id); + void SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBuffer depth_buffer, + AmdGpu::Liverpool::DepthControl depth_control); + + void SetPos(ImVec2 pos, bool auto_resize = false); void Draw(); }; diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp index 2e8bb8f54..10cc88085 100644 --- a/src/core/devtools/widget/reg_view.cpp +++ b/src/core/devtools/widget/reg_view.cpp @@ -11,6 +11,7 @@ #include "common.h" #include "common/io_file.h" #include "core/devtools/options.h" +#include "imgui/imgui_std.h" #include "imgui_internal.h" #include "reg_view.h" @@ -22,6 +23,8 @@ using namespace ImGui; using magic_enum::enum_name; +constexpr auto depth_id = 0xF3; + static std::optional exec_cli(const char* cli) { std::array buffer{}; std::string output; @@ -40,7 +43,16 @@ static std::optional exec_cli(const char* cli) { namespace Core::Devtools::Widget { void RegView::ProcessShader(int shader_id) { - auto shader = data.stages[shader_id]; + std::vector shader_code; + Vulkan::Liverpool::UserData user_data; + if (data.is_compute) { + shader_code = data.cs_data.code; + user_data = data.cs_data.cs_program.user_data; + } else { + const auto& s = data.stages[shader_id]; + shader_code = s.code; + user_data = s.user_data.user_data; + } std::string shader_dis; @@ -57,7 +69,7 @@ void RegView::ProcessShader(int shader_id) { } 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.Write(shader_code); file.Close(); auto result = exec_cli(cli.c_str()); @@ -73,8 +85,9 @@ void RegView::ProcessShader(int shader_id) { MemoryEditor hex_view; hex_view.Open = true; hex_view.ReadOnly = true; - hex_view.Cols = 16; + hex_view.Cols = 8; hex_view.OptShowAscii = false; + hex_view.OptShowOptions = false; TextEditor dis_view; dis_view.SetPalette(TextEditor::GetDarkPalette()); @@ -84,7 +97,7 @@ void RegView::ProcessShader(int shader_id) { ShaderCache cache{ .hex_view = hex_view, .dis_view = dis_view, - .user_data = shader.user_data.user_data, + .user_data = user_data, }; shader_decomp.emplace(shader_id, std::move(cache)); } @@ -95,34 +108,64 @@ void RegView::SelectShader(int id) { } } -void RegView::DrawRegs() { +void RegView::DrawComputeRegs() { + const auto& cs = data.cs_data.cs_program; + + if (BeginTable("CREGS", 2, ImGuiTableFlags_Borders)) { + TableNextRow(); + + // clang-format off + DrawValueRowList( + "DISPATCH_INITIATOR", cs.dispatch_initiator, + "DIM_X", cs.dim_x, + "DIM_Y", cs.dim_y, + "DIM_Z", cs.dim_z, + "START_X", cs.start_x, + "START_Y", cs.start_y, + "START_Z", cs.start_z, + "NUM_THREAD_X.FULL", cs.num_thread_x.full, + "NUM_THREAD_X.PARTIAL", cs.num_thread_x.partial, + "NUM_THREAD_Y.FULL", cs.num_thread_y.full, + "NUM_THREAD_Y.PARTIAL", cs.num_thread_y.partial, + "NUM_THREAD_Z.FULL", cs.num_thread_z.full, + "NUM_THREAD_Z.PARTIAL", cs.num_thread_z.partial, + "MAX_WAVE_ID", cs.max_wave_id, + "SETTINGS.NUM_VGPRS", cs.settings.num_vgprs, + "SETTINGS.NUM_SGPRS", cs.settings.num_sgprs, + "SETTINGS.NUM_USER_REGS", cs.settings.num_user_regs, + "SETTINGS.TGID_ENABLE", cs.settings.tgid_enable, + "SETTINGS.LDS_DWORDS", cs.settings.lds_dwords, + "RESOURCE_LIMITS", cs.resource_limits + ); + // clang-format on + + EndTable(); + } +} + +void RegView::DrawGraphicsRegs() { const auto& regs = data.regs; if (BeginTable("REGS", 2, ImGuiTableFlags_Borders)) { + TableNextRow(); - 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()); + DrawValueRow("Primitive type", regs.primitive_type); const auto open_new_popup = [&](int cb, auto... args) { + const auto pos = GetItemRectMax() + ImVec2(5.0f, 0.0f); if (GetIO().KeyShift) { auto& pop = extra_reg_popup.emplace_back(); - pop.SetData(args...); + pop.SetData(title, args...); pop.open = true; + pop.SetPos(pos, 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.SetData(title, args...); + if (!default_reg_popup.open || !default_reg_popup.moved) { 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(); + default_reg_popup.SetPos(pos, true); } } }; @@ -142,7 +185,7 @@ void RegView::DrawRegs() { } else { const char* text = last_selected_cb == cb && default_reg_popup.open ? "x" : "->"; if (SmallButton(text)) { - open_new_popup(cb, buffer, batch_id, cb); + open_new_popup(cb, buffer, cb); } } @@ -156,13 +199,30 @@ void RegView::DrawRegs() { 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); + open_new_popup(depth_id, regs.depth_buffer, regs.depth_control); } } + 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); + + DrawValueRow("Color control", regs.color_control.mode); + + DrawRow("Primitive restart", "%X (IDX: %X)", regs.enable_primitive_restart & 1, + regs.primitive_restart_index); + // clang-format off + DrawValueRowList( + "Polygon mode", regs.polygon_control.PolyMode(), + "Cull mode", regs.polygon_control.CullingMode(), + "Clip Space", regs.clipper_control.clip_space, + "Front face", regs.polygon_control.front_face, + "Num Samples", regs.aa_config.NumSamples() + ); + // clang-format on + EndTable(); } } @@ -172,9 +232,9 @@ RegView::RegView() { id = unique_id++; char name[128]; - snprintf(name, sizeof(name), "BatchView###reg_dump_%d", id); + snprintf(name, sizeof(name), "###reg_dump_%d", id); SetNextWindowPos({400.0f, 200.0f}); - SetNextWindowSize({450.0f, 500.0f}); + SetNextWindowSize({290.0f, 435.0f}); ImGuiID root_dock_id; Begin(name); { @@ -188,7 +248,7 @@ RegView::RegView() { ImGuiID up1, down1; DockBuilderRemoveNodeChildNodes(root_dock_id); - DockBuilderSplitNode(root_dock_id, ImGuiDir_Up, 0.2f, &up1, &down1); + DockBuilderSplitNode(root_dock_id, ImGuiDir_Up, 0.19f, &up1, &down1); snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id); DockBuilderDockWindow(name, up1); @@ -202,35 +262,68 @@ RegView::RegView() { DockBuilderFinish(root_dock_id); } -void RegView::SetData(DebugStateType::RegDump data, u32 batch_id) { - this->data = std::move(data); +void RegView::SetData(DebugStateType::RegDump _data, const std::string& base_title, u32 batch_id) { + this->data = std::move(_data); this->batch_id = batch_id; + this->title = fmt::format("{}/Batch {}", base_title, batch_id); // clear cache - selected_shader = -1; shader_decomp.clear(); - default_reg_popup.open = false; + if (data.is_compute) { + selected_shader = -2; + last_selected_cb = -1; + default_reg_popup.open = false; + ProcessShader(-2); + } else { + const auto& regs = data.regs; + if (selected_shader >= 0 && !regs.stage_enable.IsStageEnabled(selected_shader)) { + selected_shader = -1; + } + if (default_reg_popup.open) { + default_reg_popup.open = false; + if (last_selected_cb == depth_id) { + const auto& has_depth = + regs.depth_buffer.Address() != 0 && regs.depth_control.depth_enable; + if (has_depth) { + default_reg_popup.SetData(title, regs.depth_buffer, regs.depth_control); + default_reg_popup.open = true; + } + } else if (last_selected_cb >= 0 && + last_selected_cb < AmdGpu::Liverpool::NumColorBuffers) { + const auto& buffer = regs.color_buffers[last_selected_cb]; + const bool has_cb = buffer && regs.color_target_mask.GetMask(last_selected_cb); + if (has_cb) { + default_reg_popup.SetData(title, buffer, last_selected_cb); + default_reg_popup.open = true; + } + } + } + } extra_reg_popup.clear(); } -void RegView::Draw() { - +void RegView::SetPos(ImVec2 pos) { char name[128]; - snprintf(name, sizeof(name), "BatchView %u###reg_dump_%d", batch_id, id); + snprintf(name, sizeof(name), "%s###reg_dump_%d", title.c_str(), id); + Begin(name, &open, ImGuiWindowFlags_MenuBar); + SetWindowPos(pos); + KeepWindowInside(); + last_pos = GetWindowPos(); + moved = false; + End(); +} + +void RegView::Draw() { + char name[128]; + snprintf(name, sizeof(name), "%s###reg_dump_%d", title.c_str(), id); + if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) { + if (GetWindowPos() != last_pos) { + moved = true; + } + 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); @@ -240,11 +333,31 @@ void RegView::Draw() { 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); + if (!data.is_compute && + BeginChild("STAGES", {}, + ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY)) { + for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; i++) { + if (data.regs.stage_enable.IsStageEnabled(i)) { + const bool selected = selected_shader == i; + if (selected) { + PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); + } + if (Button(names[i], {40.0f, 40.0f})) { + SelectShader(i); + } + if (selected) { + PopStyleColor(); + } + } + SameLine(); + } + EndChild(); + } } + 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* { @@ -257,10 +370,11 @@ void RegView::Draw() { if (show_user_data) { snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id); - if (Begin(name, &show_user_data)) { + + if (Begin(name, &show_user_data, ImGuiWindowFlags_NoScrollbar)) { auto shader = get_shader(); if (!shader) { - Text("Select a stage"); + Text("Stage not selected"); } else { shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size()); } @@ -273,7 +387,7 @@ void RegView::Draw() { if (Begin(name, &show_disassembly)) { auto shader = get_shader(); if (!shader) { - Text("Select a stage"); + Text("Stage not selected"); } else { shader->dis_view.Render("Disassembly", GetContentRegionAvail()); } @@ -284,7 +398,11 @@ void RegView::Draw() { if (show_registers) { snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id); if (Begin(name, &show_registers)) { - DrawRegs(); + if (data.is_compute) { + DrawComputeRegs(); + } else { + DrawGraphicsRegs(); + } } End(); } diff --git a/src/core/devtools/widget/reg_view.h b/src/core/devtools/widget/reg_view.h index 67ab1e04f..3ac8ec077 100644 --- a/src/core/devtools/widget/reg_view.h +++ b/src/core/devtools/widget/reg_view.h @@ -18,8 +18,10 @@ struct ShaderCache { class RegView { int id; + std::string title; DebugStateType::RegDump data; u32 batch_id{~0u}; + ImVec2 last_pos; std::unordered_map shader_decomp; int selected_shader{-1}; @@ -35,14 +37,19 @@ class RegView { void SelectShader(int shader_id); - void DrawRegs(); + void DrawComputeRegs(); + + void DrawGraphicsRegs(); public: bool open = false; + bool moved = false; RegView(); - void SetData(DebugStateType::RegDump data, u32 batch_id); + void SetData(DebugStateType::RegDump data, const std::string& base_title, u32 batch_id); + + void SetPos(ImVec2 pos); void Draw(); }; diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index 7d36b019a..0ae9f57eb 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -3,6 +3,7 @@ #include #include "common/io_file.h" +#include "common/logging/formatter.h" #include "core/file_format/pkg.h" #include "core/file_format/pkg_type.h" @@ -349,7 +350,8 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: auto parent_path = extract_path.parent_path(); auto title_id = GetTitleID(); - if (parent_path.filename() != title_id) { + if (parent_path.filename() != title_id && + !fmt::UTF(extract_path.u8string()).data.ends_with("-UPDATE")) { extractPaths[ndinode_counter] = parent_path / title_id; } else { // DLCs path has different structure diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 8e6d74622..b5f1711db 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -28,16 +28,11 @@ void MntPoints::UnmountAll() { m_mnt_pairs.clear(); } -std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, bool* is_read_only) { +std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) { // Evil games like Turok2 pass double slashes e.g /app0//game.kpf - std::string corrected_path(guest_directory); - size_t pos = corrected_path.find("//"); - while (pos != std::string::npos) { - corrected_path.replace(pos, 2, "/"); - pos = corrected_path.find("//", pos + 1); - } + const auto normalized_path = std::filesystem::path(path).lexically_normal().string(); - const MntPair* mount = GetMount(corrected_path); + const MntPair* mount = GetMount(normalized_path); if (!mount) { return ""; } @@ -47,80 +42,88 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, b } // Nothing to do if getting the mount itself. - if (corrected_path == mount->mount) { + if (normalized_path == mount->mount) { return mount->host_path; } // Remove device (e.g /app0) from path to retrieve relative path. - pos = mount->mount.size() + 1; - const auto rel_path = std::string_view(corrected_path).substr(pos); + const auto rel_path = std::string_view{normalized_path}.substr(mount->mount.size() + 1); std::filesystem::path host_path = mount->host_path / rel_path; - std::filesystem::path patch_path = mount->host_path; patch_path += "-UPDATE"; - if (corrected_path.starts_with("/app0/") && std::filesystem::exists(patch_path / rel_path)) { - host_path = patch_path / rel_path; + patch_path /= rel_path; + + if ((normalized_path.starts_with("/app0") || normalized_path.starts_with("/hostapp")) && + std::filesystem::exists(patch_path)) { + return patch_path; } if (!NeedsCaseInsensitiveSearch) { return host_path; } - // If the path does not exist attempt to verify this. - // Retrieve parent path until we find one that exists. - std::scoped_lock lk{m_mutex}; - path_parts.clear(); - auto current_path = host_path; - while (!std::filesystem::exists(current_path)) { - // We have probably cached this if it's a folder. - if (auto it = path_cache.find(current_path); it != path_cache.end()) { - current_path = it->second; - break; + const auto search = [&](const auto host_path) { + // If the path does not exist attempt to verify this. + // Retrieve parent path until we find one that exists. + std::scoped_lock lk{m_mutex}; + path_parts.clear(); + auto current_path = host_path; + while (!std::filesystem::exists(current_path)) { + // We have probably cached this if it's a folder. + if (auto it = path_cache.find(current_path); it != path_cache.end()) { + current_path = it->second; + break; + } + path_parts.emplace_back(current_path.filename()); + current_path = current_path.parent_path(); } - path_parts.emplace_back(current_path.filename()); - current_path = current_path.parent_path(); - } - - // We have found an anchor. Traverse parts we recoded and see if they - // exist in filesystem but in different case. - auto guest_path = current_path; - while (!path_parts.empty()) { - const auto part = path_parts.back(); - const auto add_match = [&](const auto& host_part) { - current_path /= host_part; - guest_path /= part; - path_cache[guest_path] = current_path; - path_parts.pop_back(); - }; - - // Can happen when the mismatch is in upper folder. - if (std::filesystem::exists(current_path / part)) { - add_match(part); - continue; - } - const auto part_low = Common::ToLower(part.string()); - bool found_match = false; - for (const auto& path : std::filesystem::directory_iterator(current_path)) { - const auto candidate = path.path().filename(); - const auto filename = Common::ToLower(candidate.string()); - // Check if a filename matches in case insensitive manner. - if (filename != part_low) { + // We have found an anchor. Traverse parts we recoded and see if they + // exist in filesystem but in different case. + auto guest_path = current_path; + while (!path_parts.empty()) { + const auto part = path_parts.back(); + const auto add_match = [&](const auto& host_part) { + current_path /= host_part; + guest_path /= part; + path_cache[guest_path] = current_path; + path_parts.pop_back(); + }; + // Can happen when the mismatch is in upper folder. + if (std::filesystem::exists(current_path / part)) { + add_match(part); continue; } - // We found a match, record the actual path in the cache. - add_match(candidate); - found_match = true; - break; - } - if (!found_match) { - // Opening the guest path will surely fail but at least gives - // a better error message than the empty path. - return host_path; + const auto part_low = Common::ToLower(part.string()); + bool found_match = false; + for (const auto& path : std::filesystem::directory_iterator(current_path)) { + const auto candidate = path.path().filename(); + const auto filename = Common::ToLower(candidate.string()); + // Check if a filename matches in case insensitive manner. + if (filename != part_low) { + continue; + } + // We found a match, record the actual path in the cache. + add_match(candidate); + found_match = true; + break; + } + if (!found_match) { + return std::optional({}); + } } + return std::optional(current_path); + }; + + if (const auto path = search(patch_path)) { + return *path; + } + if (const auto path = search(host_path)) { + return *path; } - // The path was found. - return current_path; + // Opening the guest path will surely fail but at least gives + // a better error message than the empty path. + return host_path; } int HandleTable::CreateHandle() { diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index d193e765f..1c414c961 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -112,7 +112,7 @@ AvPlayer::AvPlayer(const SceAvPlayerInitData& data) m_state(std::make_unique(m_init_data)) {} s32 AvPlayer::PostInit(const SceAvPlayerPostInitData& data) { - m_post_init_data = data; + m_state->PostInit(data); return ORBIS_OK; } diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index b6ad940c9..d7f28094e 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -56,7 +56,6 @@ private: SceAvPlayerInitData m_init_data{}; SceAvPlayerInitData m_init_data_original{}; - SceAvPlayerPostInitData m_post_init_data{}; std::mutex m_file_io_mutex{}; std::atomic_bool m_has_source{}; diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index fcae180e7..19925ba0c 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -37,7 +37,8 @@ namespace Libraries::AvPlayer { using namespace Kernel; -AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state) : m_state(state) {} +AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2) + : m_state(state), m_use_vdec2(use_vdec2) {} AvPlayerSource::~AvPlayerSource() { Stop(); @@ -129,18 +130,25 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index); } switch (info.type) { - case SCE_AVPLAYER_VIDEO: + case SCE_AVPLAYER_VIDEO: { LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); info.details.video.aspect_ratio = f32(p_stream->codecpar->width) / p_stream->codecpar->height; - info.details.video.width = Common::AlignUp(u32(p_stream->codecpar->width), 16); - info.details.video.height = Common::AlignUp(u32(p_stream->codecpar->height), 16); + auto width = u32(p_stream->codecpar->width); + auto height = u32(p_stream->codecpar->height); + if (!m_use_vdec2) { + width = Common::AlignUp(width, 16); + height = Common::AlignUp(height, 16); + } + info.details.video.width = width; + info.details.video.height = height; if (p_lang_node != nullptr) { std::memcpy(info.details.video.language_code, p_lang_node->value, std::min(strlen(p_lang_node->value), size_t(3))); } break; - case SCE_AVPLAYER_AUDIO: + } + case SCE_AVPLAYER_AUDIO: { LOG_INFO(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index); info.details.audio.channel_count = p_stream->codecpar->ch_layout.nb_channels; info.details.audio.sample_rate = p_stream->codecpar->sample_rate; @@ -150,7 +158,8 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info std::min(strlen(p_lang_node->value), size_t(3))); } break; - case SCE_AVPLAYER_TIMEDTEXT: + } + case SCE_AVPLAYER_TIMEDTEXT: { LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index); info.details.subs.font_size = 12; info.details.subs.text_size = 12; @@ -159,10 +168,12 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info std::min(strlen(p_lang_node->value), size_t(3))); } break; - default: + } + default: { LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type); return false; } + } return true; } @@ -189,8 +200,12 @@ bool AvPlayerSource::EnableStream(u32 stream_index) { LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for video stream {}.", stream_index); return false; } - const auto width = Common::AlignUp(u32(m_video_codec_context->width), 16); - const auto height = Common::AlignUp(u32(m_video_codec_context->height), 16); + auto width = u32(m_video_codec_context->width); + auto height = u32(m_video_codec_context->height); + if (!m_use_vdec2) { + width = Common::AlignUp(width, 16); + height = Common::AlignUp(height, 16); + } const auto size = (width * height * 3) / 2; for (u64 index = 0; index < m_num_output_video_framebuffers; ++index) { m_video_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size)); @@ -316,7 +331,7 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { auto frame = m_video_frames.Pop(); if (!frame.has_value()) { - LOG_WARNING(Lib_AvPlayer, "Could get video frame. EOF reached."); + LOG_TRACE(Lib_AvPlayer, "Could get video frame. EOF reached."); return false; } @@ -351,7 +366,7 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { auto frame = m_audio_frames.Pop(); if (!frame.has_value()) { - LOG_WARNING(Lib_AvPlayer, "Could get audio frame. EOF reached."); + LOG_TRACE(Lib_AvPlayer, "Could get audio frame. EOF reached."); return false; } @@ -537,9 +552,13 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& fram return nv12_frame; } -static void CopyNV12Data(u8* dst, const AVFrame& src) { - const auto width = Common::AlignUp(u32(src.width), 16); - const auto height = Common::AlignUp(u32(src.height), 16); +static void CopyNV12Data(u8* dst, const AVFrame& src, bool use_vdec2) { + auto width = u32(src.width); + auto height = u32(src.height); + if (!use_vdec2) { + width = Common::AlignUp(width, 16); + height = Common::AlignUp(height, 16); + } if (src.width == width) { std::memcpy(dst, src.data[0], src.width * src.height); @@ -561,7 +580,7 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame ASSERT(frame.format == AV_PIX_FMT_NV12); auto p_buffer = buffer.GetBuffer(); - CopyNV12Data(p_buffer, frame); + CopyNV12Data(p_buffer, frame, m_use_vdec2); const auto pkt_dts = u64(frame.pkt_dts) * 1000; const auto stream = m_avformat_context->streams[m_video_stream_index.value()]; @@ -570,8 +589,12 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame const auto num = time_base.num; const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts; - const auto width = Common::AlignUp(u32(frame.width), 16); - const auto height = Common::AlignUp(u32(frame.height), 16); + auto width = u32(frame.width); + auto height = u32(frame.height); + if (!m_use_vdec2) { + width = Common::AlignUp(width, 16); + height = Common::AlignUp(height, 16); + } return Frame{ .buffer = std::move(buffer), @@ -583,8 +606,8 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame { .video = { - .width = u32(width), - .height = u32(height), + .width = width, + .height = height, .aspect_ratio = AVRationalToF32(frame.sample_aspect_ratio), .crop_left_offset = u32(frame.crop_left), .crop_right_offset = u32(frame.crop_right + (width - frame.width)), diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 906122142..505d74465 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -120,7 +120,7 @@ private: class AvPlayerSource { public: - AvPlayerSource(AvPlayerStateCallback& state); + AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2); ~AvPlayerSource(); bool Init(const SceAvPlayerInitData& init_data, std::string_view path); @@ -168,6 +168,7 @@ private: Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame); AvPlayerStateCallback& m_state; + bool m_use_vdec2 = false; SceAvPlayerMemAllocator m_memory_replacement{}; u32 m_num_output_video_framebuffers{}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index 5cabdba2c..e66100679 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -130,6 +130,10 @@ AvPlayerState::~AvPlayerState() { m_event_queue.Clear(); } +void AvPlayerState::PostInit(const SceAvPlayerPostInitData& post_init_data) { + m_post_init_data = post_init_data; +} + // Called inside GAME thread bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { if (path.empty()) { @@ -144,7 +148,9 @@ bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType sourc return false; } - m_up_source = std::make_unique(*this); + m_up_source = std::make_unique( + *this, m_post_init_data.video_decoder_init.decoderType.video_type == + SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE2); if (!m_up_source->Init(m_init_data, path)) { SetState(AvState::Error); m_up_source.reset(); diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index 151eea52c..d106127e4 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -24,6 +24,7 @@ public: AvPlayerState(const SceAvPlayerInitData& init_data); ~AvPlayerState(); + void PostInit(const SceAvPlayerPostInitData& post_init_data); bool AddSource(std::string_view filename, SceAvPlayerSourceType source_type); s32 GetStreamCount(); bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); @@ -68,6 +69,7 @@ private: std::unique_ptr m_up_source; SceAvPlayerInitData m_init_data{}; + SceAvPlayerPostInitData m_post_init_data{}; SceAvPlayerEventReplacement m_event_replacement{}; bool m_auto_start{}; u8 m_default_language[4]{}; diff --git a/src/core/libraries/dialogs/ime_dialog_ui.cpp b/src/core/libraries/dialogs/ime_dialog_ui.cpp index 48f5d75dc..9d50d2fbb 100644 --- a/src/core/libraries/dialogs/ime_dialog_ui.cpp +++ b/src/core/libraries/dialogs/ime_dialog_ui.cpp @@ -245,7 +245,7 @@ void ImeDialogUi::Draw() { window_size = {500.0f, 150.0f}; } - CentralizeWindow(); + CentralizeNextWindow(); SetNextWindowSize(window_size); SetNextWindowCollapsed(false); diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index 041870ed7..dae285664 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -438,6 +438,7 @@ constexpr int ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT = 0x8096000A; // SystemService library constexpr int ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER = 0x80A10003; +constexpr int ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT = 0x80A10004; // NpTrophy library constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600; diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 907244b22..4fff59003 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -987,8 +987,12 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() { } int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId) { - LOG_DEBUG(Lib_NpManager, "called returned sign out"); - return ORBIS_NP_ERROR_SIGNED_OUT; + LOG_DEBUG(Lib_NpManager, "userId {}", userId); + std::string name = Config::getUserName(); + // Fill the unused stuffs to 0 + memset(onlineId, 0, sizeof(*onlineId)); + strcpy(onlineId->data, name.c_str()); + return ORBIS_OK; } int PS4_SYSV_ABI sceNpGetParentalControlInfo() { diff --git a/src/core/libraries/system/systemservice.cpp b/src/core/libraries/system/systemservice.cpp index 8002e2bfc..9c6fe8f2e 100644 --- a/src/core/libraries/system/systemservice.cpp +++ b/src/core/libraries/system/systemservice.cpp @@ -1942,9 +1942,12 @@ int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess() { return ORBIS_OK; } -int PS4_SYSV_ABI sceSystemServiceReceiveEvent() { - LOG_ERROR(Lib_SystemService, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event) { + LOG_ERROR(Lib_SystemService, "(STUBBED) called, event type = {:#x}", (int)event->eventType); + if (event == nullptr) { + return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; + } + return ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT; } int PS4_SYSV_ABI sceSystemServiceReenableMusicPlayer() { diff --git a/src/core/libraries/system/systemservice.h b/src/core/libraries/system/systemservice.h index 1976606a8..56583c97e 100644 --- a/src/core/libraries/system/systemservice.h +++ b/src/core/libraries/system/systemservice.h @@ -87,6 +87,32 @@ enum OrbisSystemParamLanguage { ORBIS_SYSTEM_PARAM_LANG_INDONESIAN = 29 }; +enum OrbisSystemServiceEventType { + ORBIS_SYSTEM_SERVICE_EVENT_INVALID = -1, + ORBIS_SYSTEM_SERVICE_EVENT_ON_RESUME = 0x10000000, + ORBIS_SYSTEM_SERVICE_EVENT_GAME_LIVE_STREAMING_STATUS_UPDATE = 0x10000001, + ORBIS_SYSTEM_SERVICE_EVENT_SESSION_INVITATION = 0x10000002, + ORBIS_SYSTEM_SERVICE_EVENT_ENTITLEMENT_UPDATE = 0x10000003, + ORBIS_SYSTEM_SERVICE_EVENT_GAME_CUSTOM_DATA = 0x10000004, + ORBIS_SYSTEM_SERVICE_EVENT_DISPLAY_SAFE_AREA_UPDATE = 0x10000005, + ORBIS_SYSTEM_SERVICE_EVENT_URL_OPEN = 0x10000006, + ORBIS_SYSTEM_SERVICE_EVENT_LAUNCH_APP = 0x10000007, + ORBIS_SYSTEM_SERVICE_EVENT_APP_LAUNCH_LINK = 0x10000008, + ORBIS_SYSTEM_SERVICE_EVENT_ADDCONTENT_INSTALL = 0x10000009, + ORBIS_SYSTEM_SERVICE_EVENT_RESET_VR_POSITION = 0x1000000a, + ORBIS_SYSTEM_SERVICE_EVENT_JOIN_EVENT = 0x1000000b, + ORBIS_SYSTEM_SERVICE_EVENT_PLAYGO_LOCUS_UPDATE = 0x1000000c, + ORBIS_SYSTEM_SERVICE_EVENT_PLAY_TOGETHER_HOST = 0x1000000d, + ORBIS_SYSTEM_SERVICE_EVENT_SERVICE_ENTITLEMENT_UPDATE = 0x1000000e, + ORBIS_SYSTEM_SERVICE_EVENT_EYE_TO_EYE_DISTANCE_UPDATE = 0x1000000f, + ORBIS_SYSTEM_SERVICE_EVENT_JOIN_MATCH_EVENT = 0x10000010, + ORBIS_SYSTEM_SERVICE_EVENT_PLAY_TOGETHER_HOST_A = 0x10000011, + ORBIS_SYSTEM_SERVICE_EVENT_WEBBROWSER_CLOSED = 0x10000012, + ORBIS_SYSTEM_SERVICE_EVENT_CONTROLLER_SETTINGS_CLOSED = 0x10000013, + ORBIS_SYSTEM_SERVICE_EVENT_JOIN_TEAM_ON_TEAM_MATCH_EVENT = 0x10000014, + ORBIS_SYSTEM_SERVICE_EVENT_OPEN_SHARE_MENU = 0x30000000 +}; + struct OrbisSystemServiceStatus { s32 eventNum; bool isSystemUiOverlaid; @@ -102,6 +128,41 @@ struct OrbisSystemServiceDisplaySafeAreaInfo { uint8_t reserved[128]; }; +struct OrbisSystemServiceEvent { + OrbisSystemServiceEventType eventType; + union { + char param[8192]; + struct { + char source[1024]; + char url[4096]; + } urlOpen; + struct { + u32 size; + u8 arg[8188]; + } launchApp; + struct { + u32 size; + u8 arg[2020]; + } appLaunchLink; + struct { + s32 userId; + char eventId[37]; + char bootArgument[7169]; + } joinEvent; + struct { + s32 userId; + u32 npServiceLabel; + u8 reserved[8184]; + } serviceEntitlementUpdate; + struct { + s32 userId; + u32 npServiceLabel; + u8 reserved[8184]; + } unifiedEntitlementUpdate; + u8 reserved[8192]; + }; +}; + bool IsSplashVisible(); int PS4_SYSV_ABI sceAppMessagingClearEventFlag(); @@ -480,7 +541,7 @@ s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(int param_id, int* value); int PS4_SYSV_ABI sceSystemServiceParamGetString(); int PS4_SYSV_ABI sceSystemServicePowerTick(); int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess(); -int PS4_SYSV_ABI sceSystemServiceReceiveEvent(); +s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event); int PS4_SYSV_ABI sceSystemServiceReenableMusicPlayer(); int PS4_SYSV_ABI sceSystemServiceRegisterDaemon(); int PS4_SYSV_ABI sceSystemServiceReleaseFb0(); diff --git a/src/emulator.cpp b/src/emulator.cpp index 46bcfea37..a3019c9ca 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -100,6 +100,12 @@ Emulator::~Emulator() { } void Emulator::Run(const std::filesystem::path& file) { + + // Use the eboot from the separated updates folder if it's there + std::filesystem::path game_patch_folder = file.parent_path().concat("-UPDATE"); + bool use_game_patch = std::filesystem::exists(game_patch_folder / "sce_sys"); + std::filesystem::path eboot_path = use_game_patch ? game_patch_folder / file.filename() : file; + // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); mnt->Mount(file.parent_path(), "/app0"); @@ -114,10 +120,7 @@ void Emulator::Run(const std::filesystem::path& file) { std::string app_version; u32 fw_version; - std::filesystem::path game_patch_folder = file.parent_path().concat("-UPDATE"); - bool use_game_patch = std::filesystem::exists(game_patch_folder / "sce_sys"); - std::filesystem::path sce_sys_folder = - use_game_patch ? game_patch_folder / "sce_sys" : file.parent_path() / "sce_sys"; + std::filesystem::path sce_sys_folder = eboot_path.parent_path() / "sce_sys"; if (std::filesystem::is_directory(sce_sys_folder)) { for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) { if (entry.path().filename() == "param.sfo") { @@ -132,7 +135,7 @@ void Emulator::Run(const std::filesystem::path& file) { Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles"; if (!std::filesystem::exists(trophyDir)) { TRP trp; - if (!trp.Extract(file.parent_path(), id)) { + if (!trp.Extract(eboot_path.parent_path(), id)) { LOG_ERROR(Loader, "Couldn't extract trophies"); } } @@ -221,17 +224,23 @@ void Emulator::Run(const std::filesystem::path& file) { Libraries::InitHLELibs(&linker->GetHLESymbols()); // Load the module with the linker - linker->LoadModule(file); + linker->LoadModule(eboot_path); // check if we have system modules to load - LoadSystemModules(file); + LoadSystemModules(eboot_path); // Load all prx from game's sce_module folder std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; if (std::filesystem::is_directory(sce_module_folder)) { for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { - LOG_INFO(Loader, "Loading {}", fmt::UTF(entry.path().u8string())); - linker->LoadModule(entry.path()); + std::filesystem::path module_path = entry.path(); + std::filesystem::path update_module_path = + eboot_path.parent_path() / "sce_module" / entry.path().filename(); + if (std::filesystem::exists(update_module_path) && use_game_patch) { + module_path = update_module_path; + } + LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string())); + linker->LoadModule(module_path); } } diff --git a/src/imgui/imgui_std.h b/src/imgui/imgui_std.h index ce79da705..cd7208064 100644 --- a/src/imgui/imgui_std.h +++ b/src/imgui/imgui_std.h @@ -31,7 +31,7 @@ inline void CentralizeNextWindow() { } inline void CentralizeWindow() { - const auto display_size = GetIO().DisplaySize; + const auto display_size = GetIO().DisplaySize - GetCurrentWindowRead()->SizeFull; SetWindowPos(display_size / 2.0f); } @@ -41,7 +41,7 @@ inline void KeepWindowInside(ImVec2 display_size = GetIO().DisplaySize) { SetWindowPos(ImMax(cur_pos, ImVec2(0.0f, 0.0f))); return; } - const auto cur_size = GetWindowSize(); + const auto cur_size = GetCurrentWindowRead()->SizeFull; const auto bottom_right = cur_pos + cur_size; if (bottom_right.x > display_size.x || bottom_right.y > display_size.y) { const auto max_pos = display_size - cur_size; diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h index 640b25e49..99805cd52 100644 --- a/src/qt_gui/game_info.h +++ b/src/qt_gui/game_info.h @@ -26,8 +26,8 @@ public: GameInfo game; game.path = filePath; std::filesystem::path sce_folder_path = filePath / "sce_sys" / "param.sfo"; - std::filesystem::path game_update_path = - std::filesystem::path(filePath.string() + "-UPDATE"); + std::filesystem::path game_update_path = filePath; + game_update_path += "-UPDATE"; if (std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo")) { sce_folder_path = game_update_path / "sce_sys" / "param.sfo"; } diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index fba8616af..8d7018522 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -290,19 +290,21 @@ public: if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC) { bool error = false; - QString folder_path, game_update_path; + QString folder_path, game_update_path, dlc_path; Common::FS::PathToQString(folder_path, m_games[itemID].path); Common::FS::PathToQString(game_update_path, m_games[itemID].path.concat("-UPDATE")); + Common::FS::PathToQString( + dlc_path, Config::getAddonInstallDir() / + Common::FS::PathFromQString(folder_path).parent_path().filename()); QString message_type = tr("Game"); + if (selected == deleteUpdate) { if (!Config::getSeparateUpdateEnabled()) { - QMessageBox::critical( - nullptr, tr("Error"), - QString(tr("This feature requires the 'Enable Separate Update Folder' " - "config option " - "to work. If you want to use this feature, please enable it."))); + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("requiresEnableSeparateUpdateFolder_MSG"))); error = true; - } else if (!std::filesystem::exists(m_games[itemID].path.concat("-UPDATE"))) { + } else if (!std::filesystem::exists( + Common::FS::PathFromQString(game_update_path))) { QMessageBox::critical(nullptr, tr("Error"), QString(tr("This game has no update to delete!"))); error = true; @@ -311,15 +313,12 @@ public: message_type = tr("Update"); } } else if (selected == deleteDLC) { - std::filesystem::path addon_path = - Config::getAddonInstallDir() / - Common::FS::PathFromQString(folder_path).parent_path().filename(); - if (!std::filesystem::exists(addon_path)) { + if (!std::filesystem::exists(Common::FS::PathFromQString(dlc_path))) { QMessageBox::critical(nullptr, tr("Error"), QString(tr("This game has no DLC to delete!"))); error = true; } else { - folder_path = QString::fromStdString(addon_path.string()); + folder_path = dlc_path; message_type = tr("DLC"); } } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index d80102ff4..e52820102 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -678,13 +678,10 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int auto game_install_dir = ids.getSelectedDirectory(); auto game_folder_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); - bool use_game_update = pkgType.contains("Patch") && Config::getSeparateUpdateEnabled(); + bool use_game_update = pkgType.contains("PATCH") && Config::getSeparateUpdateEnabled(); auto game_update_path = use_game_update ? game_install_dir / (std::string(pkg.GetTitleID()) + "-UPDATE") : game_folder_path; - if (!std::filesystem::exists(game_update_path)) { - std::filesystem::create_directory(game_update_path); - } QString gameDirPath; Common::FS::PathToQString(gameDirPath, game_folder_path); QDir game_dir(gameDirPath); @@ -792,8 +789,6 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int } } } else { - QString gameDirPath; - Common::FS::PathToQString(gameDirPath, game_folder_path); msgBox.setText(QString(tr("Game already installed") + "\n" + gameDirPath + "\n" + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 77701f221..309b40b46 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -127,15 +127,15 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge connect(ui->fullscreenCheckBox, &QCheckBox::stateChanged, this, [](int val) { Config::setFullscreenMode(val); }); + connect(ui->separateUpdatesCheckBox, &QCheckBox::stateChanged, this, + [](int val) { Config::setSeparateUpdateEnabled(val); }); + connect(ui->showSplashCheckBox, &QCheckBox::stateChanged, this, [](int val) { Config::setShowSplash(val); }); connect(ui->ps4proCheckBox, &QCheckBox::stateChanged, this, [](int val) { Config::setNeoMode(val); }); - connect(ui->separateUpdatesCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setSeparateUpdateEnabled(val); }); - connect(ui->logTypeComboBox, &QComboBox::currentTextChanged, this, [](const QString& text) { Config::setLogType(text.toStdString()); }); @@ -270,10 +270,10 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->consoleLanguageGroupBox->installEventFilter(this); ui->emulatorLanguageGroupBox->installEventFilter(this); ui->fullscreenCheckBox->installEventFilter(this); + ui->separateUpdatesCheckBox->installEventFilter(this); ui->showSplashCheckBox->installEventFilter(this); ui->ps4proCheckBox->installEventFilter(this); ui->discordRPCCheckbox->installEventFilter(this); - ui->separateUpdatesCheckBox->installEventFilter(this); ui->userName->installEventFilter(this); ui->logTypeGroupBox->installEventFilter(this); ui->logFilter->installEventFilter(this); @@ -327,12 +327,12 @@ void SettingsDialog::LoadValuesFromConfig() { ui->BGMVolumeSlider->setValue((Config::getBGMvolume())); ui->discordRPCCheckbox->setChecked(Config::getEnableDiscordRPC()); ui->fullscreenCheckBox->setChecked(Config::isFullscreenMode()); + ui->separateUpdatesCheckBox->setChecked(Config::getSeparateUpdateEnabled()); ui->showSplashCheckBox->setChecked(Config::showSplash()); ui->ps4proCheckBox->setChecked(Config::isNeoMode()); ui->logTypeComboBox->setCurrentText(QString::fromStdString(Config::getLogType())); ui->logFilterLineEdit->setText(QString::fromStdString(Config::getLogFilter())); ui->userNameLineEdit->setText(QString::fromStdString(Config::getUserName())); - ui->separateUpdatesCheckBox->setChecked(Config::getSeparateUpdateEnabled()); ui->debugDump->setChecked(Config::debugDump()); ui->vkValidationCheckBox->setChecked(Config::vkValidationEnabled()); @@ -436,14 +436,14 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("emulatorLanguageGroupBox"); } else if (elementName == "fullscreenCheckBox") { text = tr("fullscreenCheckBox"); + } else if (elementName == "separateUpdatesCheckBox") { + text = tr("separateUpdatesCheckBox"); } else if (elementName == "showSplashCheckBox") { text = tr("showSplashCheckBox"); } else if (elementName == "ps4proCheckBox") { text = tr("ps4proCheckBox"); } else if (elementName == "discordRPCCheckbox") { text = tr("discordRPCCheckbox"); - } else if (elementName == "separateUpdatesCheckBox") { - text = tr("separateUpdatesCheckBox"); } else if (elementName == "userName") { text = tr("userName"); } else if (elementName == "logTypeGroupBox") { diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 5d1225a1e..cce728f65 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 854 - 630 + 660 diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 463fd9716..f8f7f5336 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -140,6 +140,26 @@ Copy All نسخ الكل + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKG تثبيت + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen تمكين ملء الشاشة + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox تمكين وضع ملء الشاشة:\nيجعل نافذة اللعبة تنتقل تلقائيًا إلى وضع ملء الشاشة.\nيمكن التبديل بالضغط على المفتاح F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index b3a6b9001..4bdc75828 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Aktiver fuld skærm:\nSætter automatisk spilvinduet i fuld skærm.\nDette kan skiftes ved at trykke på F11-tasten. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 75a14b348..2a436ec2c 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -140,6 +140,26 @@ Copy All Alles kopieren + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKG installieren + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Vollbild aktivieren + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Vollbildmodus aktivieren:\nSchaltet das Spielfenster automatisch in den Vollbildmodus.\nKann durch Drücken der F11-Taste umgeschaltet werden. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 871709297..b7d51bfb3 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Ενεργοποίηση Πλήρους Οθόνης:\nΑυτόματα μετατρέπει το παράθυρο του παιχνιδιού σε λειτουργία πλήρους οθόνης.\nΜπορεί να ενεργοποιηθεί/απενεργοποιηθεί πατώντας το πλήκτρο F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 7c44fbdf4..5b58d865f 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 1a5a8c887..032579f91 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -140,6 +140,26 @@ Copy All Copiar todo + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Instalar PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Habilitar pantalla completa + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Habilitar Pantalla Completa:\nColoca automáticamente la ventana del juego en modo de pantalla completa.\nEsto se puede alternar presionando la tecla F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 0a6b21916..8976d185c 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -140,6 +140,26 @@ Copy All کپی کردن تمامی مقادیر + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG نصب PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen تمام صفحه + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 98efe9c8f..c2cb6c5e8 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Ota Täysikokoisuus käyttöön:\nSiirtää pelin ikkunan automaattisesti täysikokoiseen tilaan.\nTätä voidaan vaihtaa painamalla F11-näppäintä. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 8e371908b..f891b7475 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -140,6 +140,26 @@ Copy All Copier tout + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Installer un PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Plein écran + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Activer le mode plein écran:\nMet automatiquement la fenêtre du jeu en mode plein écran.\nCela peut être activé en appuyant sur la touche F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 8104a3d98..500629682 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -140,6 +140,26 @@ Copy All Összes Másolása + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKG telepítése + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Teljesképernyő Engedélyezése + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Teljes képernyő engedélyezése:\nAutomatikusan teljes képernyőre állítja a játék ablakát.\nEz a F11 billentyű megnyomásával kapcsolható ki/be. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 288149346..8721e3a8a 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Aktifkan Mode Layar Penuh:\nSecara otomatis menempatkan jendela permainan dalam mode layar penuh.\nIni dapat dinonaktifkan dengan menekan tombol F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 47000d57f..747148b6e 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -140,6 +140,26 @@ Copy All Copia Tutto + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Installa PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Abilita Schermo Intero + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Abilita Schermo Intero:\nMetti automaticamente la finestra di gioco in modalità schermo intero.\nQuesto può essere disattivato premendo il tasto F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 952ceb5ab..a020b1687 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -140,6 +140,26 @@ Copy All すべてコピー + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKGをインストール + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen フルスクリーンを有効にする + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox 全画面モードを有効にする:\nゲームウィンドウを自動的に全画面モードにします。\nF11キーを押すことで切り替えることができます。 + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index d7d200672..03ab49ad6 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index f82a20a9d..aecf5aec9 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Įjungti visą ekraną:\nAutomatiškai perjungia žaidimo langą į viso ekrano režimą.\nTai galima išjungti paspaudus F11 klavišą. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 6fdf5ff8e..303b5f831 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Aktiver fullskjerm:\nSetter automatisk spillvinduet i fullskjermmodus.\nDette kan slås av ved å trykke på F11-tasten. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index f99ad54da..c4f49b93e 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Volledig scherm inschakelen:\nZet het gamevenster automatisch in de volledig scherm modus.\nDit kan worden omgeschakeld door op de F11-toets te drukken. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index a1fd3dc6a..0f6a928b4 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -140,6 +140,26 @@ Copy All Kopiuj wszystko + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Zainstaluj PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Włącz pełny ekran + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Włącz tryb pełnoekranowy:\nAutomatycznie przełącza okno gry w tryb pełnoekranowy.\nMożna to wyłączyć naciskając klawisz F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 50c575623..c1c2c438b 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -140,6 +140,26 @@ Copy All Copiar Tudo + + + Delete... + Deletar... + + + + Delete Game + Deletar Jogo + + + + Delete Update + Deletar Atualização + + + + Delete DLC + Deletar DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Instalar PKG + + + Game + Jogo + + + + requiresEnableSeparateUpdateFolder_MSG + Este recurso requer a opção de configuração 'Habilitar Pasta de Atualização Separada' para funcionar. Se você quiser usar este recurso, habilite-o. + + + + This game has no update to delete! + Este jogo não tem atualização para excluir! + + + + Update + Atualização + + + + This game has no DLC to delete! + Este jogo não tem DLC para excluir! + + + + DLC + DLC + + + + Delete %1 + Deletar %1 + + + + Are you sure you want to delete %1's %2 directory? + Tem certeza de que deseja excluir o diretório %2 de %1 ? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Ativar Tela Cheia + + + Enable Separate Update Folder + Habilitar pasta de atualização separada + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Ativar Tela Cheia:\nMove automaticamente a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. + + + separateUpdatesCheckBox + Habilitar pasta de atualização separada:\nPermite instalar atualizações de jogos em uma pasta separada para fácil gerenciamento. + showSplashCheckBox diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index ba2952976..ffb6b6547 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Activează modul pe ecran complet:\nPune automat fereastra jocului în modul pe ecran complet.\nAceasta poate fi dezactivată apăsând tasta F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 5b29bf7c7..73263587d 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -62,7 +62,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Выберите папку, в которую вы хотите установить. @@ -140,6 +140,26 @@ Copy All Копировать все + + + Delete... + Удаление... + + + + Delete Game + Удалить игру + + + + Delete Update + Удалить обновление + + + + Delete DLC + Удалить DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Установить PKG + + + Game + Игры + + + + requiresEnableSeparateUpdateFolder_MSG + Эта функция требует включения настройки 'Отдельная папка обновлений'. Если вы хотите использовать эту функцию, пожалуйста включите её. + + + + This game has no update to delete! + У этой игры нет обновлений для удаления! + + + + Update + Обновления + + + + This game has no DLC to delete! + У этой игры нет DLC для удаления! + + + + DLC + DLC + + + + Delete %1 + Удалить %1 + + + + Are you sure you want to delete %1's %2 directory? + Вы уверены, что хотите удалить папку %2 %1? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Полноэкранный режим + + + Enable Separate Update Folder + Отдельная папка обновлений + Show Splash @@ -460,17 +525,17 @@ Cursor - Курсор + Курсор мыши Hide Cursor - Скрыть курсор + Скрывать курсор Hide Cursor Idle Timeout - Тайм-аут скрытия курсора при неактивности + Тайм-аут скрытия курсора при бездействии @@ -575,7 +640,7 @@ Check for Updates at Startup - Проверка обновлений при запуске + Проверка при запуске @@ -590,12 +655,12 @@ GUI Settings - Настройки интерфейса + Интерфейс Play title music - Воспроизведение заглавной музыки + Играть заглавную музыку @@ -1056,6 +1121,11 @@ fullscreenCheckBox Полноэкранный режим:\nАвтоматически переводит игровое окно в полноэкранный режим.\nВы можете отключить это, нажав клавишу F11. + + + separateUpdatesCheckBox + Отдельная папка обновлений:\nПозволяет устанавливать обновления игры в отдельную папку для удобства. + showSplashCheckBox @@ -1094,22 +1164,22 @@ GUIgroupBox - Воспроизведение заглавной музыки:\nЕсли игра это поддерживает, включает воспроизведение специальной музыки при выборе игры в интерфейсе. + Играть заглавную музыку:\nВключает воспроизведение специальной музыки при выборе игры в списке, если она это поддерживает. hideCursorGroupBox - Скрыть курсор:\nВыберите, когда курсор исчезнет:\nНикогда: Вы всегда будете видеть мышь.\nНеактивный: Yстановите время, через которое она исчезнет после бездействия.\nВсегда: вы никогда не увидите мышь. + Скрывать курсор:\nВыберите, когда курсор исчезнет:\nНикогда: Вы всегда будете видеть мышь.\nПри бездействии: Установите время, через которое курсор исчезнет при бездействии.\nВсегда: Вы никогда не будете видеть мышь. idleTimeoutGroupBox - Установите время, через которое курсор исчезнет после бездействия. + Установите время, через которое курсор исчезнет при бездействии. backButtonBehaviorGroupBox - Поведение кнопки «Назад»:\nУстанавливает кнопку «Назад» на контроллере для имитации нажатия в указанной позиции на сенсорной панели PS4. + Поведение кнопки «Назад»:\nНастраивает кнопку «Назад» контроллера на эмуляцию нажатия на указанную область на сенсорной панели контроллера PS4. @@ -1119,7 +1189,7 @@ Idle - Неактивный + При бездействии @@ -1129,17 +1199,17 @@ Touchpad Left - Тачпад Слева + Тачпад слева Touchpad Right - Тачпад Справа + Тачпад справа Touchpad Center - Центр Тачпада + Центр тачпада @@ -1252,7 +1322,7 @@ Play Time - Время Игры + Времени в игре @@ -1330,7 +1400,7 @@ Check for Updates at Startup - Проверка обновлений при запуске + Проверка при запуске diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index fabc79cca..fd835cb81 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -140,6 +140,26 @@ Copy All Kopjo të Gjitha + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Instalo PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Aktivizo Ekranin e plotë + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Aktivizo ekranin e plotë:\nVendos automatikisht dritaren e lojës në mënyrën e ekranit të plotë.\nKjo mund të aktivizohet duke shtypur tastin F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox @@ -1383,4 +1453,4 @@ Krijimi i skedarit skript të përditësimit dështoi - + \ No newline at end of file diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 91ede7319..86930764f 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -140,6 +140,26 @@ Copy All Tümünü Kopyala + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKG Yükle + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Tam Ekranı Etkinleştir + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Tam Ekranı Etkinleştir:\nOyun penceresini otomatik olarak tam ekran moduna alır.\nBu, F11 tuşuna basarak geçiş yapılabilir. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index ebdee76e5..8bc241ece 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Bật chế độ toàn màn hình:\nTự động đặt cửa sổ trò chơi ở chế độ toàn màn hình.\nĐiều này có thể bị vô hiệu hóa bằng cách nhấn phím F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 8533c5456..bc943c860 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -140,6 +140,26 @@ Copy All 复制全部 + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG 安装 PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen 启用全屏 + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox 启用全屏模式:\n自动将游戏窗口设置为全屏模式。\n您可以按 F11 键禁用此选项。 + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index f3db48971..c7b923f83 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox 啟用全螢幕:\n自動將遊戲視窗設置為全螢幕模式。\n可以按F11鍵進行切換。 + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index d4ebeb883..7b4727088 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -446,7 +446,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandim_z; regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs, + true); } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); @@ -463,7 +464,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs, + true); } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); @@ -645,7 +647,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { 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); + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs, true); } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); diff --git a/src/video_core/amdgpu/pixel_format.cpp b/src/video_core/amdgpu/pixel_format.cpp index 6744891a6..b13fc2d11 100644 --- a/src/video_core/amdgpu/pixel_format.cpp +++ b/src/video_core/amdgpu/pixel_format.cpp @@ -144,10 +144,10 @@ static constexpr std::array component_bits = { std::array{8, 8, 0, 0}, // 3 Format8_8 std::array{32, 0, 0, 0}, // 4 Format32 std::array{16, 16, 0, 0}, // 5 Format16_16 - std::array{10, 11, 11, 0}, // 6 Format10_11_11 - std::array{11, 11, 10, 0}, // 7 Format11_11_10 - std::array{10, 10, 10, 2}, // 8 Format10_10_10_2 - std::array{2, 10, 10, 10}, // 9 Format2_10_10_10 + std::array{11, 11, 10, 0}, // 6 Format10_11_11 + std::array{10, 11, 11, 0}, // 7 Format11_11_10 + std::array{2, 10, 10, 10}, // 8 Format10_10_10_2 + std::array{10, 10, 10, 2}, // 9 Format2_10_10_10 std::array{8, 8, 8, 8}, // 10 Format8_8_8_8 std::array{32, 32, 0, 0}, // 11 Format32_32 std::array{16, 16, 16, 16}, // 12 Format16_16_16_16 @@ -155,12 +155,12 @@ static constexpr std::array component_bits = { std::array{32, 32, 32, 32}, // 14 Format32_32_32_32 std::array{0, 0, 0, 0}, // 15 std::array{5, 6, 5, 0}, // 16 Format5_6_5 - std::array{1, 5, 5, 5}, // 17 Format1_5_5_5 - std::array{5, 5, 5, 1}, // 18 Format5_5_5_1 + std::array{5, 5, 5, 1}, // 17 Format1_5_5_5 + std::array{1, 5, 5, 5}, // 18 Format5_5_5_1 std::array{4, 4, 4, 4}, // 19 Format4_4_4_4 - std::array{8, 24, 0, 0}, // 20 Format8_24 - std::array{24, 8, 0, 0}, // 21 Format24_8 - std::array{24, 8, 0, 0}, // 22 FormatX24_8_32 + std::array{24, 8, 0, 0}, // 20 Format8_24 + std::array{8, 24, 0, 0}, // 21 Format24_8 + std::array{8, 24, 0, 0}, // 22 FormatX24_8_32 std::array{0, 0, 0, 0}, // 23 std::array{0, 0, 0, 0}, // 24 std::array{0, 0, 0, 0}, // 25 @@ -197,10 +197,10 @@ static constexpr std::array component_offset = { std::array{0, 8, -1, -1}, // 3 Format8_8 std::array{0, -1, -1, -1}, // 4 Format32 std::array{0, 16, -1, -1}, // 5 Format16_16 - std::array{0, 10, 21, -1}, // 6 Format10_11_11 - std::array{0, 11, 22, -1}, // 7 Format11_11_10 - std::array{0, 10, 20, 30}, // 8 Format10_10_10_2 - std::array{0, 2, 12, 22}, // 9 Format2_10_10_10 + std::array{0, 11, 22, -1}, // 6 Format10_11_11 + std::array{0, 10, 21, -1}, // 7 Format11_11_10 + std::array{0, 2, 12, 22}, // 8 Format10_10_10_2 + std::array{0, 10, 20, 30}, // 9 Format2_10_10_10 std::array{0, 8, 16, 24}, // 10 Format8_8_8_8 std::array{0, 32, -1, -1}, // 11 Format32_32 std::array{0, 16, 32, 48}, // 12 Format16_16_16_16 @@ -208,12 +208,12 @@ static constexpr std::array component_offset = { std::array{0, 32, 64, 96}, // 14 Format32_32_32_32 std::array{-1, -1, -1, -1}, // 15 std::array{0, 5, 11, -1}, // 16 Format5_6_5 - std::array{0, 1, 6, 11}, // 17 Format1_5_5_5 - std::array{0, 5, 10, 15}, // 18 Format5_5_5_1 + std::array{0, 5, 10, 15}, // 17 Format1_5_5_5 + std::array{0, 1, 6, 11}, // 18 Format5_5_5_1 std::array{0, 4, 8, 12}, // 19 Format4_4_4_4 - std::array{0, 8, -1, -1}, // 20 Format8_24 - std::array{0, 24, -1, -1}, // 21 Format24_8 - std::array{0, 24, -1, -1}, // 22 FormatX24_8_32 + std::array{0, 24, -1, -1}, // 20 Format8_24 + std::array{0, 8, -1, -1}, // 21 Format24_8 + std::array{0, 8, -1, -1}, // 22 FormatX24_8_32 std::array{-1, -1, -1, -1}, // 23 std::array{-1, -1, -1, -1}, // 24 std::array{-1, -1, -1, -1}, // 25 diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index a68ec1e74..3e43c50e5 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -2,11 +2,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" +#include "common/number_utils.h" #include "video_core/amdgpu/pixel_format.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include +#define INVALID_NUMBER_FORMAT_COMBO \ + LOG_ERROR(Render_Vulkan, "Unsupported number type {} for format {}", number_type, format); + namespace Vulkan::LiverpoolToVK { using DepthBuffer = Liverpool::DepthBuffer; @@ -725,55 +729,362 @@ void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) { } } -static constexpr float U8ToUnorm(u8 v) { - static constexpr auto c = 1.0f / 255.0f; - return float(v * c); -} - vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color_buffer) { const auto comp_swap = color_buffer.info.comp_swap.Value(); - ASSERT_MSG(comp_swap == Liverpool::ColorBuffer::SwapMode::Standard || - comp_swap == Liverpool::ColorBuffer::SwapMode::Alternate, - "Unsupported component swap mode {}", static_cast(comp_swap)); - - const bool comp_swap_alt = comp_swap == Liverpool::ColorBuffer::SwapMode::Alternate; + const auto format = color_buffer.info.format.Value(); + const auto number_type = color_buffer.info.number_type.Value(); const auto& c0 = color_buffer.clear_word0; const auto& c1 = color_buffer.clear_word1; const auto num_bits = AmdGpu::NumBits(color_buffer.info.format); + const auto num_components = AmdGpu::NumComponents(format); + + const bool comp_swap_alt = + comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::Alternate || + comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::AlternateReverse; + const bool comp_swap_reverse = + comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::StandardReverse || + comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::AlternateReverse; vk::ClearColorValue color{}; - switch (color_buffer.info.number_type) { - case AmdGpu::NumberFormat::Snorm: - [[fallthrough]]; - case AmdGpu::NumberFormat::SnormNz: - [[fallthrough]]; - case AmdGpu::NumberFormat::Unorm: - [[fallthrough]]; - case AmdGpu::NumberFormat::Srgb: { - switch (num_bits) { - case 32: { - color.float32 = std::array{ - U8ToUnorm((c0 >> (comp_swap_alt ? 16 : 0)) & 0xff), - U8ToUnorm((c0 >> 8) & 0xff), - U8ToUnorm((c0 >> (comp_swap_alt ? 0 : 16)) & 0xff), - U8ToUnorm((c0 >> 24) & 0xff), - }; - break; - } - default: { - LOG_ERROR(Render_Vulkan, "Missing clear color conversion for bits {}", num_bits); - break; - } - } + + switch (number_type) { + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[3] = 1; + break; + default: + color.float32[3] = 1.0f; break; } - default: { - LOG_ERROR(Render_Vulkan, "Missing clear color conversion for type {}", - color_buffer.info.number_type.Value()); + + switch (format) { + case AmdGpu::DataFormat::Format8: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + case AmdGpu::NumberFormat::Srgb: // Should we handle gamma correction here? + color.float32[0] = NumberUtils::U8ToUnorm(c0 & 0xff); + break; + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S8ToSnorm(c0 & 0xff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format16: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U16ToUnorm(c0 & 0xffff); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S16ToSnorm(c0 & 0xffff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = NumberUtils::Uf16ToF32(c0 & 0xffff); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format8_8: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + case AmdGpu::NumberFormat::Srgb: // Should we handle gamma correction here? + color.float32[0] = NumberUtils::U8ToUnorm(c0 & 0xff); + color.float32[1] = NumberUtils::U8ToUnorm((c0 >> 8) & 0xff); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S8ToSnorm(c0 & 0xff); + color.float32[1] = NumberUtils::S8ToSnorm((c0 >> 8) & 0xff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0xff; + color.uint32[1] = (c0 >> 8) & 0xff; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format32: + switch (number_type) { + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = *(reinterpret_cast(&c0)); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format16_16: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U16ToUnorm(c0 & 0xffff); + color.float32[1] = NumberUtils::U16ToUnorm((c0 >> 16) & 0xffff); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S16ToSnorm(c0 & 0xffff); + color.float32[1] = NumberUtils::S16ToSnorm((c0 >> 16) & 0xffff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0xffff; + color.uint32[1] = (c0 >> 16) & 0xffff; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = NumberUtils::Uf16ToF32(c0 & 0xffff); + color.float32[1] = NumberUtils::Uf16ToF32((c0 >> 16) & 0xffff); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format10_11_11: + color.float32[0] = NumberUtils::Uf11ToF32(c0 & 0x7ff); + color.float32[1] = NumberUtils::Uf11ToF32((c0 >> 11) & 0x7ff); + color.float32[2] = NumberUtils::Uf10ToF32((c0 >> 22) & 0x3ff); + break; + case AmdGpu::DataFormat::Format11_11_10: + color.float32[0] = NumberUtils::Uf10ToF32(c0 & 0x3ff); + color.float32[1] = NumberUtils::Uf11ToF32((c0 >> 10) & 0x7ff); + color.float32[2] = NumberUtils::Uf11ToF32((c0 >> 21) & 0x7ff); + break; + case AmdGpu::DataFormat::Format5_9_9_9: { + int exponent; + union { + float f; + u32 u; + } scale; + + exponent = (c0 >> 27) - 10; + scale.u = (exponent + 127) << 23; + + color.float32[0] = (c0 & 0x1ff) * scale.f; + color.float32[1] = ((c0 >> 9) & 0x1ff) * scale.f; + color.float32[2] = ((c0 >> 18) & 0x1ff) * scale.f; break; } + case AmdGpu::DataFormat::Format10_10_10_2: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U2ToUnorm(c0 & 0x3); + color.float32[1] = NumberUtils::U10ToUnorm((c0 >> 2) & 0x3ff); + color.float32[2] = NumberUtils::U10ToUnorm((c0 >> 12) & 0x3ff); + color.float32[3] = NumberUtils::U10ToUnorm(c0 >> 22); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S2ToSnorm(c0 & 0x3); + color.float32[1] = NumberUtils::S10ToSnorm((c0 >> 2) & 0x3ff); + color.float32[2] = NumberUtils::S10ToSnorm((c0 >> 12) & 0x3ff); + color.float32[3] = NumberUtils::S2ToSnorm(c0 >> 22); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0x3; + color.uint32[1] = (c0 >> 2) & 0x3ff; + color.uint32[2] = (c0 >> 12) & 0x3ff; + color.uint32[3] = c0 >> 22; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format2_10_10_10: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U10ToUnorm(c0 & 0x3ff); + color.float32[1] = NumberUtils::U10ToUnorm((c0 >> 10) & 0x3ff); + color.float32[2] = NumberUtils::U10ToUnorm((c0 >> 20) & 0x3ff); + color.float32[3] = NumberUtils::U2ToUnorm(c0 >> 30); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S10ToSnorm(c0 & 0x3ff); + color.float32[1] = NumberUtils::S10ToSnorm((c0 >> 10) & 0x3ff); + color.float32[2] = NumberUtils::S10ToSnorm((c0 >> 20) & 0x3ff); + color.float32[3] = NumberUtils::S2ToSnorm(c0 >> 30); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0x3ff; + color.uint32[1] = (c0 >> 10) & 0x3ff; + color.uint32[2] = (c0 >> 20) & 0x3ff; + color.uint32[3] = c0 >> 30; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format8_8_8_8: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + case AmdGpu::NumberFormat::Srgb: // Should we handle gamma correction here? + color.float32[0] = NumberUtils::U8ToUnorm(c0 & 0xff); + color.float32[1] = NumberUtils::U8ToUnorm((c0 >> 8) & 0xff); + color.float32[2] = NumberUtils::U8ToUnorm((c0 >> 16) & 0xff); + color.float32[3] = NumberUtils::U8ToUnorm(c0 >> 24); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S8ToSnorm(c0 & 0xff); + color.float32[1] = NumberUtils::S8ToSnorm((c0 >> 8) & 0xff); + color.float32[2] = NumberUtils::S8ToSnorm((c0 >> 16) & 0xff); + color.float32[3] = NumberUtils::S8ToSnorm(c0 >> 24); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0xff; + color.uint32[1] = (c0 >> 8) & 0xff; + color.uint32[2] = (c0 >> 16) & 0xff; + color.uint32[3] = c0 >> 24; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format32_32: + switch (number_type) { + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + color.uint32[1] = c1; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = *(reinterpret_cast(&c0)); + color.float32[1] = *(reinterpret_cast(&c1)); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format16_16_16_16: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U16ToUnorm(c0 & 0xffff); + color.float32[1] = NumberUtils::U16ToUnorm((c0 >> 16) & 0xffff); + color.float32[2] = NumberUtils::U16ToUnorm(c1 & 0xffff); + color.float32[3] = NumberUtils::U16ToUnorm((c1 >> 16) & 0xffff); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S16ToSnorm(c0 & 0xffff); + color.float32[1] = NumberUtils::S16ToSnorm((c0 >> 16) & 0xffff); + color.float32[2] = NumberUtils::S16ToSnorm(c1 & 0xffff); + color.float32[3] = NumberUtils::S16ToSnorm((c1 >> 16) & 0xffff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0xffff; + color.uint32[1] = (c0 >> 16) & 0xffff; + color.uint32[2] = c1 & 0xffff; + color.uint32[3] = (c1 >> 16) & 0xffff; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = NumberUtils::Uf16ToF32(c0 & 0xffff); + color.float32[1] = NumberUtils::Uf16ToF32((c0 >> 16) & 0xffff); + color.float32[2] = NumberUtils::Uf16ToF32(c1 & 0xffff); + color.float32[3] = NumberUtils::Uf16ToF32((c1 >> 16) & 0xffff); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format32_32_32_32: + switch (number_type) { + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + color.uint32[1] = c0; + color.uint32[2] = c0; + color.uint32[3] = c1; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = *(reinterpret_cast(&c0)); + color.float32[1] = *(reinterpret_cast(&c0)); + color.float32[2] = *(reinterpret_cast(&c0)); + color.float32[3] = *(reinterpret_cast(&c1)); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format5_6_5: + color.float32[0] = NumberUtils::U5ToUnorm(c0 & 0x1f); + color.float32[1] = NumberUtils::U6ToUnorm((c0 >> 5) & 0x3f); + color.float32[2] = NumberUtils::U5ToUnorm(c0 >> 11); + break; + case AmdGpu::DataFormat::Format1_5_5_5: + color.float32[0] = NumberUtils::U5ToUnorm(c0 & 0x1f); + color.float32[1] = NumberUtils::U5ToUnorm((c0 >> 5) & 0x1f); + color.float32[2] = NumberUtils::U5ToUnorm((c0 >> 10) & 0x1f); + color.float32[3] = (c0 >> 15) ? 1.0f : 0.0f; + break; + case AmdGpu::DataFormat::Format5_5_5_1: + color.float32[0] = (c0 & 0x1) ? 1.0f : 0.0f; + color.float32[1] = NumberUtils::U5ToUnorm((c0 >> 1) & 0x1f); + color.float32[2] = NumberUtils::U5ToUnorm((c0 >> 6) & 0x1f); + color.float32[3] = NumberUtils::U5ToUnorm((c0 >> 11) & 0x1f); + break; + case AmdGpu::DataFormat::Format4_4_4_4: + color.float32[0] = NumberUtils::U4ToUnorm(c0 & 0xf); + color.float32[1] = NumberUtils::U4ToUnorm((c0 >> 4) & 0xf); + color.float32[2] = NumberUtils::U4ToUnorm((c0 >> 8) & 0xf); + color.float32[3] = NumberUtils::U4ToUnorm(c0 >> 12); + break; + default: + LOG_ERROR(Render_Vulkan, "Unsupported color buffer format: {}", format); + break; } + + if (num_components == 1) { + if (comp_swap != Liverpool::ColorBuffer::SwapMode::Standard) { + color.float32[static_cast(comp_swap)] = color.float32[0]; + color.float32[0] = 0.0f; + } + } else { + if (comp_swap_alt && num_components == 4) { + std::swap(color.float32[0], color.float32[2]); + } + + if (comp_swap_reverse) { + std::reverse(std::begin(color.float32), std::begin(color.float32) + num_components); + } + + if (comp_swap_alt && num_components != 4) { + color.float32[3] = color.float32[num_components - 1]; + color.float32[num_components - 1] = 0.0f; + } + } + return {.color = color}; } diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 521e4118f..efb74ffae 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -329,6 +329,11 @@ bool ImageInfo::IsMipOf(const ImageInfo& info) const { return false; } + // Ensure that address matches too. + if ((info.guest_address + info.mips_layout[mip].offset) != guest_address) { + return false; + } + return true; } diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 4813a3c57..00e6bea82 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -237,6 +237,16 @@ ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { } } + if (image_id) { + Image& image_resoved = slot_images[image_id]; + + if (image_resoved.info.resources < info.resources) { + // The image was clearly picked up wrong. + FreeImage(image_id); + image_id = {}; + LOG_WARNING(Render_Vulkan, "Image overlap resolve failed"); + } + } // Create and register a new image if (!image_id) { image_id = slot_images.insert(instance, scheduler, info);