mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-28 13:28:01 +03:00
Merge branch 'master' into RSX
This commit is contained in:
commit
1e1bbf7e40
81 changed files with 1042 additions and 18556 deletions
|
@ -145,6 +145,7 @@ export MACOSX_DEPLOYMENT_TARGET=14.0
|
|||
-DCMAKE_SYSTEM_PROCESSOR=arm64 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinARM64.cmake \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-G Ninja
|
||||
|
||||
"$BREW_PATH/bin/ninja"; build_status=$?;
|
||||
|
|
|
@ -108,6 +108,7 @@ export MACOSX_DEPLOYMENT_TARGET=14.0
|
|||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||
-DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-G Ninja
|
||||
|
||||
"$BREW_PATH/bin/ninja"; build_status=$?;
|
||||
|
|
|
@ -22,7 +22,7 @@ QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}"
|
|||
LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z"
|
||||
GLSLANG_URL='https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_mt.7z'
|
||||
VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1"
|
||||
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.10.2/ccache-4.10.2-windows-x86_64.zip"
|
||||
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip"
|
||||
|
||||
DEP_URLS=" \
|
||||
$QT_BASE_URL \
|
||||
|
|
8
.github/workflows/rpcs3.yml
vendored
8
.github/workflows/rpcs3.yml
vendored
|
@ -25,17 +25,17 @@ jobs:
|
|||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.2"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.4"
|
||||
build_sh: "/rpcs3/.ci/build-linux.sh"
|
||||
compiler: clang
|
||||
UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
|
||||
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux"
|
||||
- os: ubuntu-24.04
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.2"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.4"
|
||||
build_sh: "/rpcs3/.ci/build-linux.sh"
|
||||
compiler: gcc
|
||||
- os: ubuntu-24.04-arm
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.2"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.4"
|
||||
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
|
||||
compiler: clang
|
||||
UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
|
||||
|
@ -111,7 +111,7 @@ jobs:
|
|||
LLVM_VER: '19.1.7'
|
||||
VULKAN_VER: '1.3.268.0'
|
||||
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
CCACHE_SHA: '6252f081876a9a9f700fae13a5aec5d0d486b28261d7f1f72ac11c7ad9df4da9'
|
||||
CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c'
|
||||
CCACHE_BIN_DIR: 'C:\ccache_bin'
|
||||
CCACHE_DIR: 'C:\ccache'
|
||||
CCACHE_INODECACHE: 'true'
|
||||
|
|
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -104,3 +104,7 @@
|
|||
path = 3rdparty/discord-rpc/discord-rpc
|
||||
url = ../../Vestrel/discord-rpc
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/GPUOpen/VulkanMemoryAllocator"]
|
||||
path = 3rdparty/GPUOpen/VulkanMemoryAllocator
|
||||
url = ../../Megamouse/VulkanMemoryAllocator.git
|
||||
ignore = dirty
|
||||
|
|
1
3rdparty/GPUOpen/VulkanMemoryAllocator
vendored
Submodule
1
3rdparty/GPUOpen/VulkanMemoryAllocator
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 37064843398c69cc0ca7f8cf5b33128c03a2bd74
|
18120
3rdparty/GPUOpen/include/vk_mem_alloc.h
vendored
18120
3rdparty/GPUOpen/include/vk_mem_alloc.h
vendored
File diff suppressed because it is too large
Load diff
1
3rdparty/MoltenVK/CMakeLists.txt
vendored
1
3rdparty/MoltenVK/CMakeLists.txt
vendored
|
@ -1,4 +1,3 @@
|
|||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(moltenvk NONE)
|
||||
include(ExternalProject)
|
||||
|
||||
|
|
2
3rdparty/OpenAL/openal-soft
vendored
2
3rdparty/OpenAL/openal-soft
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 90191edd20bb877c5cbddfdac7ec0fe49ad93727
|
||||
Subproject commit dc7d7054a5b4f3bec1dc23a42fd616a0847af948
|
2
3rdparty/discord-rpc/CMakeLists.txt
vendored
2
3rdparty/discord-rpc/CMakeLists.txt
vendored
|
@ -9,5 +9,7 @@ if (USE_DISCORD_RPC AND (WIN32 OR CMAKE_SYSTEM MATCHES "Linux" OR APPLE))
|
|||
set(WARNINGS_AS_ERRORS FALSE CACHE BOOL "When enabled, compiles with `-Werror` (on *nix platforms).")
|
||||
|
||||
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
|
||||
target_include_directories(3rdparty_discordRPC INTERFACE discord-rpc/include)
|
||||
target_compile_definitions(3rdparty_discordRPC INTERFACE -DWITH_DISCORD_RPC)
|
||||
target_link_libraries(3rdparty_discordRPC INTERFACE discord-rpc)
|
||||
endif()
|
||||
|
|
2
3rdparty/discord-rpc/discord-rpc
vendored
2
3rdparty/discord-rpc/discord-rpc
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 171b2142ac8acdf016c231e36dc7a8d48daff19c
|
||||
Subproject commit 3dc2c326cb4dc5815c6069970c13154898f58d48
|
2
3rdparty/libusb/CMakeLists.txt
vendored
2
3rdparty/libusb/CMakeLists.txt
vendored
|
@ -1,5 +1,3 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
project(libusb)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.16.9)
|
||||
cmake_minimum_required(VERSION 3.28)
|
||||
|
||||
project(rpcs3 LANGUAGES C CXX)
|
||||
|
||||
|
|
|
@ -413,7 +413,7 @@ public:
|
|||
{
|
||||
std::string name = m_path;
|
||||
|
||||
name.append(_module->getName().data());
|
||||
name.append(_module->getName());
|
||||
//fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
|
||||
name.append(".gz");
|
||||
|
||||
|
@ -425,9 +425,9 @@ public:
|
|||
|
||||
ensure(m_compiler);
|
||||
|
||||
fs::file module_file(name, fs::rewrite);
|
||||
fs::pending_file module_file;
|
||||
|
||||
if (!module_file)
|
||||
if (!module_file.open((name)))
|
||||
{
|
||||
jit_log.error("LLVM: Failed to create module file: %s (%s)", name, fs::g_tls_error);
|
||||
return;
|
||||
|
@ -442,18 +442,17 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
if (!zip(obj.getBufferStart(), obj.getBufferSize(), module_file))
|
||||
if (!zip(obj.getBufferStart(), obj.getBufferSize(), module_file.file))
|
||||
{
|
||||
jit_log.error("LLVM: Failed to compress module: %s", _module->getName().data());
|
||||
module_file.close();
|
||||
fs::remove_file(name);
|
||||
jit_log.error("LLVM: Failed to compress module: %s", std::string(_module->getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
jit_log.trace("LLVM: Created module: %s", _module->getName().data());
|
||||
jit_log.trace("LLVM: Created module: %s", std::string(_module->getName()));
|
||||
|
||||
// Restore space that was overestimated
|
||||
ensure(m_compiler->add_sub_disk_space(max_size - module_file.size()));
|
||||
ensure(m_compiler->add_sub_disk_space(max_size - module_file.file.size()));
|
||||
module_file.commit();
|
||||
}
|
||||
|
||||
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
|
||||
|
|
|
@ -40,13 +40,13 @@ jobs:
|
|||
# displayName: ccache
|
||||
|
||||
# - bash: |
|
||||
# docker pull --quiet rpcs3/rpcs3-ci-jammy:1.2
|
||||
# docker pull --quiet rpcs3/rpcs3-ci-jammy:1.4
|
||||
# docker run \
|
||||
# -v $(pwd):/rpcs3 \
|
||||
# --env-file .ci/docker.env \
|
||||
# -v $CCACHE_DIR:/root/.ccache \
|
||||
# -v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \
|
||||
# rpcs3/rpcs3-ci-jammy:1.2 \
|
||||
# rpcs3/rpcs3-ci-jammy:1.4 \
|
||||
# /rpcs3/.ci/build-linux.sh
|
||||
# displayName: Docker setup and build
|
||||
|
||||
|
|
|
@ -546,6 +546,7 @@ target_sources(rpcs3_emu PRIVATE
|
|||
RSX/Overlays/overlay_trophy_notification.cpp
|
||||
RSX/Overlays/overlay_user_list_dialog.cpp
|
||||
RSX/Overlays/overlay_utils.cpp
|
||||
RSX/Overlays/overlay_video.cpp
|
||||
RSX/Overlays/Shaders/shader_loading_dialog.cpp
|
||||
RSX/Overlays/Shaders/shader_loading_dialog_native.cpp
|
||||
RSX/Program/CgBinaryFragmentProgram.cpp
|
||||
|
|
|
@ -195,7 +195,7 @@ struct audio_port
|
|||
// Handle copy ctor of atomic var
|
||||
audio_port(const audio_port& r)
|
||||
{
|
||||
std::memcpy(this, &r, sizeof(r));
|
||||
std::memcpy(static_cast<void*>(this), &r, sizeof(r));
|
||||
}
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
|
|
@ -244,7 +244,7 @@ static std::vector<SaveDataEntry> get_save_entries(const std::string& base_dir,
|
|||
continue;
|
||||
}
|
||||
|
||||
SaveDataEntry save_entry;
|
||||
SaveDataEntry save_entry {};
|
||||
save_entry.dirName = psf::get_string(psf, "SAVEDATA_DIRECTORY");
|
||||
save_entry.listParam = psf::get_string(psf, "SAVEDATA_LIST_PARAM");
|
||||
save_entry.title = psf::get_string(psf, "TITLE");
|
||||
|
@ -307,7 +307,7 @@ static error_code select_and_delete(ppu_thread& ppu)
|
|||
// Display a blocking Save Data List asynchronously in the GUI thread.
|
||||
if (auto save_dialog = Emu.GetCallbacks().get_save_dialog())
|
||||
{
|
||||
selected = save_dialog->ShowSaveDataList(save_entries, focused, SAVEDATA_OP_LIST_DELETE, vm::null, g_fxo->get<savedata_manager>().enable_overlay);
|
||||
selected = save_dialog->ShowSaveDataList(base_dir, save_entries, focused, SAVEDATA_OP_LIST_DELETE, vm::null, g_fxo->get<savedata_manager>().enable_overlay);
|
||||
}
|
||||
|
||||
// Reschedule after a blocking dialog returns
|
||||
|
@ -326,7 +326,7 @@ static error_code select_and_delete(ppu_thread& ppu)
|
|||
focused = save_entries.empty() ? -1 : selected;
|
||||
|
||||
// Get information from the selected entry
|
||||
SaveDataEntry entry = save_entries[selected];
|
||||
const SaveDataEntry& entry = ::at32(save_entries, selected);
|
||||
const std::string info = entry.title + "\n" + entry.subtitle + "\n" + entry.details;
|
||||
|
||||
// Reusable display message string
|
||||
|
@ -760,7 +760,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
|||
|
||||
result->userdata = userdata; // probably should be assigned only once (allows the callback to change it)
|
||||
|
||||
SaveDataEntry save_entry;
|
||||
SaveDataEntry save_entry {};
|
||||
|
||||
if (setList)
|
||||
{
|
||||
|
@ -820,7 +820,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
|||
break;
|
||||
}
|
||||
|
||||
SaveDataEntry save_entry2;
|
||||
SaveDataEntry save_entry2 {};
|
||||
save_entry2.dirName = psf::get_string(psf, "SAVEDATA_DIRECTORY");
|
||||
save_entry2.listParam = psf::get_string(psf, "SAVEDATA_LIST_PARAM");
|
||||
save_entry2.title = psf::get_string(psf, "TITLE");
|
||||
|
@ -1183,7 +1183,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
|||
// Display a blocking Save Data List asynchronously in the GUI thread.
|
||||
if (auto save_dialog = Emu.GetCallbacks().get_save_dialog())
|
||||
{
|
||||
selected = save_dialog->ShowSaveDataList(save_entries, focused, operation, listSet, g_fxo->get<savedata_manager>().enable_overlay);
|
||||
selected = save_dialog->ShowSaveDataList(base_dir, save_entries, focused, operation, listSet, g_fxo->get<savedata_manager>().enable_overlay);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1214,8 +1214,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
|||
else
|
||||
{
|
||||
// Get information from the selected entry
|
||||
SaveDataEntry entry = save_entries[selected];
|
||||
message = get_confirmation_message(operation, entry);
|
||||
message = get_confirmation_message(operation, ::at32(save_entries, selected));
|
||||
}
|
||||
|
||||
// Yield before a blocking dialog is being spawned
|
||||
|
@ -1345,14 +1344,14 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
|||
else
|
||||
{
|
||||
// Get information from the selected entry
|
||||
SaveDataEntry entry = save_entries[selected];
|
||||
message = get_confirmation_message(operation, entry);
|
||||
message = get_confirmation_message(operation, ::at32(save_entries, selected));
|
||||
}
|
||||
|
||||
// Yield before a blocking dialog is being spawned
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
// Get user confirmation by opening a blocking dialog
|
||||
// TODO: show fixedSet->newIcon
|
||||
s32 return_code = CELL_MSGDIALOG_BUTTON_NONE;
|
||||
error_code res = open_msg_dialog(true, CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO, vm::make_str(message), msg_dialog_source::_cellSaveData, vm::null, vm::null, vm::null, &return_code);
|
||||
|
||||
|
|
|
@ -361,5 +361,5 @@ class SaveDialogBase
|
|||
public:
|
||||
virtual ~SaveDialogBase();
|
||||
|
||||
virtual s32 ShowSaveDataList(std::vector<SaveDataEntry>& save_entries, s32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay) = 0;
|
||||
virtual s32 ShowSaveDataList(const std::string& base_dir, std::vector<SaveDataEntry>& save_entries, s32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay) = 0;
|
||||
};
|
||||
|
|
|
@ -4956,9 +4956,9 @@ bool ppu_initialize(const ppu_module<lv2_obj>& info, bool check_only, u64 file_s
|
|||
return +code_ptr;
|
||||
}
|
||||
|
||||
constexpr auto abs_diff = [](u64 a, u64 b) { return a <= b ? b - a : a - b; };
|
||||
[[maybe_unused]] constexpr auto abs_diff = [](u64 a, u64 b) { return a <= b ? b - a : a - b; };
|
||||
|
||||
auto write_le = [](u8*& code, auto value)
|
||||
[[maybe_unused]] auto write_le = [](u8*& code, auto value)
|
||||
{
|
||||
write_to_ptr<le_t<std::remove_cvref_t<decltype(value)>>>(code, value);
|
||||
code += sizeof(value);
|
||||
|
@ -5823,7 +5823,7 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module<lv2_obj>& module
|
|||
min_addr = std::min<u32>(min_addr, mod_func.addr);
|
||||
|
||||
// Translate
|
||||
if (const auto func = translator.Translate(mod_func))
|
||||
if ([[maybe_unused]] const auto func = translator.Translate(mod_func))
|
||||
{
|
||||
#ifdef ARCH_X64 // TODO
|
||||
// Run optimization passes
|
||||
|
@ -5841,7 +5841,7 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module<lv2_obj>& module
|
|||
// Run this only in one module for all functions compiled
|
||||
if (module_part.jit_bounds)
|
||||
{
|
||||
if (const auto func = translator.GetSymbolResolver(module_part))
|
||||
if ([[maybe_unused]] const auto func = translator.GetSymbolResolver(module_part))
|
||||
{
|
||||
#ifdef ARCH_X64 // TODO
|
||||
// Run optimization passes
|
||||
|
|
|
@ -1237,7 +1237,7 @@ void spu_thread::dump_regs(std::string& ret, std::any& /*custom_data*/) const
|
|||
|
||||
if (const_value != r)
|
||||
{
|
||||
// Expectation of pretictable code path has not been met (such as a branch directly to the instruction)
|
||||
// Expectation of predictable code path has not been met (such as a branch directly to the instruction)
|
||||
is_const = false;
|
||||
}
|
||||
|
||||
|
@ -1447,7 +1447,7 @@ std::vector<std::pair<u32, u32>> spu_thread::dump_callstack_list() const
|
|||
if (v != v128::from32r(addr))
|
||||
{
|
||||
// 1) Non-zero lower words are invalid (because BRSL-like instructions generate only zeroes)
|
||||
// 2) Bits normally masked out by indirect braches are considered invalid
|
||||
// 2) Bits normally masked out by indirect branches are considered invalid
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -752,6 +752,22 @@ void PadHandlerBase::process()
|
|||
|
||||
pad->move_data.orientation_enabled = b_has_orientation && device->config && device->config->orientation_enabled.get();
|
||||
|
||||
// Disable pad vibration if no new data was sent for 3 seconds
|
||||
if (pad->m_last_rumble_time_us > 0)
|
||||
{
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
if ((get_system_time() - pad->m_last_rumble_time_us) > 3'000'000)
|
||||
{
|
||||
for (VibrateMotor& motor : pad->m_vibrateMotors)
|
||||
{
|
||||
motor.m_value = 0;
|
||||
}
|
||||
|
||||
pad->m_last_rumble_time_us = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const connection status = update_connection(device);
|
||||
|
||||
switch (status)
|
||||
|
|
|
@ -522,6 +522,8 @@ struct Pad
|
|||
s32 m_orientation_reset_button_index{-1}; // Special button index. -1 if not set.
|
||||
bool get_orientation_reset_button_active();
|
||||
|
||||
u64 m_last_rumble_time_us{0};
|
||||
|
||||
// Cable State: 0 - 1 plugged in ?
|
||||
u8 m_cable_state{0};
|
||||
|
||||
|
|
|
@ -218,12 +218,12 @@ namespace gl
|
|||
m_input_filter = gl::filter::linear;
|
||||
}
|
||||
|
||||
gl::texture_view* ui_overlay_renderer::load_simple_image(rsx::overlays::image_info* desc, bool temp_resource, u32 owner_uid)
|
||||
gl::texture_view* ui_overlay_renderer::load_simple_image(rsx::overlays::image_info_base* desc, bool temp_resource, u32 owner_uid)
|
||||
{
|
||||
auto tex = std::make_unique<gl::texture>(GL_TEXTURE_2D, desc->w, desc->h, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR);
|
||||
tex->copy_from(desc->get_data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
|
||||
|
||||
GLenum remap[] = { GL_RED, GL_ALPHA, GL_BLUE, GL_GREEN };
|
||||
const GLenum remap[] = { GL_RED, GL_ALPHA, GL_BLUE, GL_GREEN };
|
||||
auto view = std::make_unique<gl::texture_view>(tex.get(), remap);
|
||||
|
||||
auto result = view.get();
|
||||
|
@ -234,7 +234,7 @@ namespace gl
|
|||
}
|
||||
else
|
||||
{
|
||||
u64 key = reinterpret_cast<u64>(desc);
|
||||
const u64 key = reinterpret_cast<u64>(desc);
|
||||
temp_image_cache[key] = std::make_pair(owner_uid, std::move(tex));
|
||||
temp_view_cache[key] = std::move(view);
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ namespace gl
|
|||
rsx::overlays::resource_config configuration;
|
||||
configuration.load_files();
|
||||
|
||||
for (const auto &res : configuration.texture_raw_data)
|
||||
for (const auto& res : configuration.texture_raw_data)
|
||||
{
|
||||
load_simple_image(res.get(), false, -1);
|
||||
}
|
||||
|
@ -318,13 +318,22 @@ namespace gl
|
|||
return result;
|
||||
}
|
||||
|
||||
gl::texture_view* ui_overlay_renderer::find_temp_image(rsx::overlays::image_info* desc, u32 owner_uid)
|
||||
gl::texture_view* ui_overlay_renderer::find_temp_image(rsx::overlays::image_info_base* desc, u32 owner_uid)
|
||||
{
|
||||
auto key = reinterpret_cast<u64>(desc);
|
||||
const bool dirty = std::exchange(desc->dirty, false);
|
||||
const u64 key = reinterpret_cast<u64>(desc);
|
||||
|
||||
auto cached = temp_view_cache.find(key);
|
||||
if (cached != temp_view_cache.end())
|
||||
{
|
||||
return cached->second.get();
|
||||
gl::texture_view* view = cached->second.get();
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
view->image()->copy_from(desc->get_data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
return load_simple_image(desc, true, owner_uid);
|
||||
|
@ -420,7 +429,7 @@ namespace gl
|
|||
}
|
||||
case rsx::overlays::image_resource_id::raw_image:
|
||||
{
|
||||
cmd_->bind_texture(31, GL_TEXTURE_2D, find_temp_image(static_cast<rsx::overlays::image_info*>(cmd.config.external_data_ref), ui.uid)->id());
|
||||
cmd_->bind_texture(31, GL_TEXTURE_2D, find_temp_image(static_cast<rsx::overlays::image_info_base*>(cmd.config.external_data_ref), ui.uid)->id());
|
||||
break;
|
||||
}
|
||||
case rsx::overlays::image_resource_id::font_file:
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace gl
|
|||
|
||||
ui_overlay_renderer();
|
||||
|
||||
gl::texture_view* load_simple_image(rsx::overlays::image_info* desc, bool temp_resource, u32 owner_uid);
|
||||
gl::texture_view* load_simple_image(rsx::overlays::image_info_base* desc, bool temp_resource, u32 owner_uid);
|
||||
|
||||
void create();
|
||||
void destroy();
|
||||
|
@ -84,7 +84,7 @@ namespace gl
|
|||
|
||||
gl::texture_view* find_font(rsx::overlays::font* font);
|
||||
|
||||
gl::texture_view* find_temp_image(rsx::overlays::image_info* desc, u32 owner_uid);
|
||||
gl::texture_view* find_temp_image(rsx::overlays::image_info_base* desc, u32 owner_uid);
|
||||
|
||||
void set_primitive_type(rsx::overlays::primitive_type type);
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace rsx
|
|||
{
|
||||
update_value();
|
||||
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
const f32 col = m_last_value ? 1.0f : 0.3f;
|
||||
const f32 bkg = m_last_value ? 0.3f : 1.0f;
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace rsx
|
|||
if (const T new_value = m_setting->get(); new_value != m_last_value || initializing)
|
||||
{
|
||||
m_last_value = new_value;
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ namespace rsx
|
|||
{
|
||||
this->update_value();
|
||||
|
||||
if (!this->is_compiled)
|
||||
if (!this->is_compiled())
|
||||
{
|
||||
const std::string value_text = Emu.GetCallbacks().get_localized_setting(home_menu_setting<T, cfg::_enum<T>>::m_setting, static_cast<u32>(this->m_last_value));
|
||||
m_dropdown.set_text(value_text);
|
||||
|
@ -145,7 +145,7 @@ namespace rsx
|
|||
{
|
||||
this->update_value();
|
||||
|
||||
if (!this->is_compiled)
|
||||
if (!this->is_compiled())
|
||||
{
|
||||
const f64 percentage = std::clamp((this->m_last_value - static_cast<T>(m_minimum)) / std::fabs(m_maximum - m_minimum), 0.0, 1.0);
|
||||
m_slider.set_pos(m_slider.x, this->y + (this->h - m_slider.h) / 2);
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace rsx
|
|||
|
||||
compiled_resource& home_menu_message_box::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
compiled_resource& compiled = overlay_element::get_compiled();
|
||||
compiled.add(m_label.get_compiled());
|
||||
|
|
|
@ -258,9 +258,9 @@ namespace rsx
|
|||
|
||||
compiled_resource& home_menu_page::get_compiled()
|
||||
{
|
||||
if (!is_compiled || (m_message_box && !m_message_box->is_compiled))
|
||||
if (!is_compiled() || (m_message_box && !m_message_box->is_compiled()))
|
||||
{
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
|
||||
if (home_menu_page* page = get_current_page(false))
|
||||
{
|
||||
|
@ -281,7 +281,7 @@ namespace rsx
|
|||
}
|
||||
}
|
||||
|
||||
is_compiled = true;
|
||||
m_is_compiled = true;
|
||||
}
|
||||
|
||||
return compiled_resources;
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace rsx
|
|||
|
||||
compiled_resource& animated_icon::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
compiled_resources = image_view::get_compiled();
|
||||
}
|
||||
|
|
|
@ -302,7 +302,7 @@ namespace rsx
|
|||
void overlay_element::refresh()
|
||||
{
|
||||
// Just invalidate for draw when get_compiled() is called
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::translate(s16 _x, s16 _y)
|
||||
|
@ -310,7 +310,7 @@ namespace rsx
|
|||
x += _x;
|
||||
y += _y;
|
||||
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::scale(f32 _x, f32 _y, bool origin_scaling)
|
||||
|
@ -324,7 +324,7 @@ namespace rsx
|
|||
w = static_cast<u16>(_x * w);
|
||||
h = static_cast<u16>(_y * h);
|
||||
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::set_pos(s16 _x, s16 _y)
|
||||
|
@ -332,7 +332,7 @@ namespace rsx
|
|||
x = _x;
|
||||
y = _y;
|
||||
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::set_size(u16 _w, u16 _h)
|
||||
|
@ -340,7 +340,7 @@ namespace rsx
|
|||
w = _w;
|
||||
h = _h;
|
||||
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::set_padding(u16 left, u16 right, u16 top, u16 bottom)
|
||||
|
@ -350,13 +350,13 @@ namespace rsx
|
|||
padding_top = top;
|
||||
padding_bottom = bottom;
|
||||
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::set_padding(u16 padding)
|
||||
{
|
||||
padding_left = padding_right = padding_top = padding_bottom = padding;
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
// NOTE: Functions as a simple position offset. Top left corner is the anchor.
|
||||
|
@ -365,25 +365,36 @@ namespace rsx
|
|||
margin_left = left;
|
||||
margin_top = top;
|
||||
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::set_margin(u16 margin)
|
||||
{
|
||||
margin_left = margin_top = margin;
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::set_text(const std::string& text)
|
||||
{
|
||||
this->text = utf8_to_u32string(text);
|
||||
is_compiled = false;
|
||||
std::u32string new_text = utf8_to_u32string(text);
|
||||
const bool is_dirty = this->text != new_text;
|
||||
this->text = std::move(new_text);
|
||||
|
||||
if (is_dirty)
|
||||
{
|
||||
m_is_compiled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void overlay_element::set_unicode_text(const std::u32string& text)
|
||||
{
|
||||
const bool is_dirty = this->text != text;
|
||||
this->text = text;
|
||||
is_compiled = false;
|
||||
|
||||
if (is_dirty)
|
||||
{
|
||||
m_is_compiled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void overlay_element::set_text(localized_string_id id)
|
||||
|
@ -394,19 +405,19 @@ namespace rsx
|
|||
void overlay_element::set_font(const char* font_name, u16 font_size)
|
||||
{
|
||||
font_ref = fontmgr::get(font_name, font_size);
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::align_text(text_align align)
|
||||
{
|
||||
alignment = align;
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void overlay_element::set_wrap_text(bool state)
|
||||
{
|
||||
wrap_text = state;
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
font* overlay_element::get_font() const
|
||||
|
@ -563,7 +574,7 @@ namespace rsx
|
|||
|
||||
compiled_resource& overlay_element::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
compiled_resources.clear();
|
||||
|
||||
|
@ -599,7 +610,7 @@ namespace rsx
|
|||
compiled_resources.add(std::move(compiled_resources_temp), margin_left - horizontal_scroll_offset, margin_top - vertical_scroll_offset);
|
||||
}
|
||||
|
||||
is_compiled = true;
|
||||
m_is_compiled = true;
|
||||
}
|
||||
|
||||
return compiled_resources;
|
||||
|
@ -664,7 +675,7 @@ namespace rsx
|
|||
{
|
||||
overlay_element::translate(_x, _y);
|
||||
|
||||
for (auto &itm : m_items)
|
||||
for (auto& itm : m_items)
|
||||
itm->translate(_x, _y);
|
||||
}
|
||||
|
||||
|
@ -675,13 +686,23 @@ namespace rsx
|
|||
translate(dx, dy);
|
||||
}
|
||||
|
||||
bool layout_container::is_compiled()
|
||||
{
|
||||
if (m_is_compiled && std::any_of(m_items.cbegin(), m_items.cend(), [](const auto& item){ return item && !item->is_compiled(); }))
|
||||
{
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
return m_is_compiled;
|
||||
}
|
||||
|
||||
compiled_resource& layout_container::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
compiled_resource result = overlay_element::get_compiled();
|
||||
|
||||
for (auto &itm : m_items)
|
||||
for (auto& itm : m_items)
|
||||
result.add(itm->get_compiled());
|
||||
|
||||
compiled_resources = result;
|
||||
|
@ -716,7 +737,7 @@ namespace rsx
|
|||
return m_items.back().get();
|
||||
}
|
||||
|
||||
auto result = item.get();
|
||||
overlay_element* result = item.get();
|
||||
m_items.insert(m_items.begin() + offset, std::move(item));
|
||||
return result;
|
||||
}
|
||||
|
@ -726,12 +747,12 @@ namespace rsx
|
|||
if (scroll_offset_value == 0 && auto_resize)
|
||||
return layout_container::get_compiled();
|
||||
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
compiled_resource result = overlay_element::get_compiled();
|
||||
const f32 global_y_offset = static_cast<f32>(-scroll_offset_value);
|
||||
|
||||
for (auto &item : m_items)
|
||||
for (auto& item : m_items)
|
||||
{
|
||||
if (!item)
|
||||
{
|
||||
|
@ -808,7 +829,7 @@ namespace rsx
|
|||
if (scroll_offset_value == 0 && auto_resize)
|
||||
return layout_container::get_compiled();
|
||||
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
compiled_resource result = overlay_element::get_compiled();
|
||||
const f32 global_x_offset = static_cast<f32>(-scroll_offset_value);
|
||||
|
@ -862,7 +883,7 @@ namespace rsx
|
|||
|
||||
compiled_resource& image_view::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
auto& result = overlay_element::get_compiled();
|
||||
auto& cmd_img = result.draw_commands.front();
|
||||
|
@ -880,7 +901,7 @@ namespace rsx
|
|||
verts[2] += vertex(padding_left, -padding_top, 0, 0);
|
||||
verts[3] += vertex(-padding_right, -padding_top, 0, 0);
|
||||
|
||||
is_compiled = true;
|
||||
m_is_compiled = true;
|
||||
}
|
||||
|
||||
return compiled_resources;
|
||||
|
@ -892,7 +913,7 @@ namespace rsx
|
|||
external_ref = nullptr;
|
||||
}
|
||||
|
||||
void image_view::set_raw_image(image_info* raw_image)
|
||||
void image_view::set_raw_image(image_info_base* raw_image)
|
||||
{
|
||||
image_resource_ref = image_resource_id::raw_image;
|
||||
external_ref = raw_image;
|
||||
|
@ -935,7 +956,7 @@ namespace rsx
|
|||
|
||||
compiled_resource& image_button::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
auto& compiled = image_view::get_compiled();
|
||||
for (auto& cmd : compiled.draw_commands)
|
||||
|
@ -987,7 +1008,7 @@ namespace rsx
|
|||
|
||||
compiled_resource& rounded_rect::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
compiled_resources.clear();
|
||||
|
||||
|
@ -1068,7 +1089,7 @@ namespace rsx
|
|||
compiled_resources.add(std::move(compiled_resources_temp), margin_left, margin_top);
|
||||
}
|
||||
|
||||
is_compiled = true;
|
||||
m_is_compiled = true;
|
||||
}
|
||||
|
||||
return compiled_resources;
|
||||
|
|
|
@ -30,23 +30,32 @@ namespace rsx
|
|||
triangle_fan = 4
|
||||
};
|
||||
|
||||
struct image_info
|
||||
struct image_info_base
|
||||
{
|
||||
int w = 0, h = 0, channels = 0;
|
||||
int bpp = 0;
|
||||
bool dirty = false;
|
||||
|
||||
image_info_base() {}
|
||||
virtual ~image_info_base() {}
|
||||
virtual const u8* get_data() const = 0;
|
||||
};
|
||||
|
||||
struct image_info : public image_info_base
|
||||
{
|
||||
private:
|
||||
u8* data = nullptr;
|
||||
std::vector<u8> data_grey;
|
||||
|
||||
public:
|
||||
int w = 0, h = 0, channels = 0;
|
||||
int bpp = 0;
|
||||
|
||||
using image_info_base::image_info_base;
|
||||
image_info(image_info&) = delete;
|
||||
image_info(const std::string& filename, bool grayscaled = false);
|
||||
image_info(const std::vector<u8>& bytes, bool grayscaled = false);
|
||||
~image_info();
|
||||
virtual ~image_info();
|
||||
|
||||
void load_data(const std::vector<u8>& bytes, bool grayscaled = false);
|
||||
const u8* get_data() const { return channels == 4 ? data : data_grey.empty() ? nullptr : data_grey.data(); }
|
||||
const u8* get_data() const override { return channels == 4 ? data : data_grey.empty() ? nullptr : data_grey.data(); }
|
||||
};
|
||||
|
||||
struct resource_config
|
||||
|
@ -165,7 +174,6 @@ namespace rsx
|
|||
void set_sinus_offset(f32 sinus_modifier);
|
||||
|
||||
compiled_resource compiled_resources;
|
||||
bool is_compiled = false;
|
||||
|
||||
bool visible = true;
|
||||
|
||||
|
@ -185,6 +193,7 @@ namespace rsx
|
|||
virtual ~overlay_element() = default;
|
||||
|
||||
virtual void refresh();
|
||||
virtual bool is_compiled() { return m_is_compiled; }
|
||||
virtual void translate(s16 _x, s16 _y);
|
||||
virtual void scale(f32 _x, f32 _y, bool origin_scaling);
|
||||
virtual void set_pos(s16 _x, s16 _y);
|
||||
|
@ -204,6 +213,10 @@ namespace rsx
|
|||
virtual std::vector<vertex> render_text(const char32_t* string, f32 x, f32 y);
|
||||
virtual compiled_resource& get_compiled();
|
||||
void measure_text(u16& width, u16& height, bool ignore_word_wrap = false) const;
|
||||
virtual void set_selected(bool selected) { static_cast<void>(selected); }
|
||||
|
||||
protected:
|
||||
bool m_is_compiled = false; // Only use m_is_compiled as a getter in is_compiled() if possible
|
||||
};
|
||||
|
||||
struct layout_container : public overlay_element
|
||||
|
@ -221,6 +234,8 @@ namespace rsx
|
|||
void translate(s16 _x, s16 _y) override;
|
||||
void set_pos(s16 _x, s16 _y) override;
|
||||
|
||||
bool is_compiled() override;
|
||||
|
||||
compiled_resource& get_compiled() override;
|
||||
|
||||
virtual u16 get_scroll_offset_px() = 0;
|
||||
|
@ -248,6 +263,7 @@ namespace rsx
|
|||
compiled_resource& get_compiled() override
|
||||
{
|
||||
// No draw
|
||||
m_is_compiled = true;
|
||||
return compiled_resources;
|
||||
}
|
||||
};
|
||||
|
@ -263,7 +279,7 @@ namespace rsx
|
|||
|
||||
struct image_view : public overlay_element
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
u8 image_resource_ref = image_resource_id::none;
|
||||
void* external_ref = nullptr;
|
||||
|
||||
|
@ -276,7 +292,7 @@ namespace rsx
|
|||
compiled_resource& get_compiled() override;
|
||||
|
||||
void set_image_resource(u8 resource_id);
|
||||
void set_raw_image(image_info* raw_image);
|
||||
void set_raw_image(image_info_base* raw_image);
|
||||
void clear_image();
|
||||
void set_blur_strength(u8 strength);
|
||||
};
|
||||
|
|
|
@ -196,7 +196,7 @@ namespace rsx
|
|||
|
||||
compiled_resource& edit_text::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
auto renderer = get_font();
|
||||
const auto [caret_x, caret_y] = renderer->get_char_offset(text.c_str(), caret_position, clip_text ? w : -1, wrap_text);
|
||||
|
@ -252,7 +252,7 @@ namespace rsx
|
|||
cmd.config.clip_rect = {static_cast<f32>(x), static_cast<f32>(y), static_cast<f32>(x + w), static_cast<f32>(y + h)};
|
||||
}
|
||||
|
||||
is_compiled = true;
|
||||
m_is_compiled = true;
|
||||
}
|
||||
|
||||
return compiled_resources;
|
||||
|
|
|
@ -91,6 +91,15 @@ namespace rsx
|
|||
void list_view::update_selection()
|
||||
{
|
||||
const overlay_element* current_element = get_selected_entry();
|
||||
|
||||
for (auto& item : m_items)
|
||||
{
|
||||
if (item)
|
||||
{
|
||||
item->set_selected(item.get() == current_element);
|
||||
}
|
||||
}
|
||||
|
||||
if (!current_element)
|
||||
{
|
||||
return; // Ideally unreachable but it should still be possible to recover by user interaction.
|
||||
|
@ -195,7 +204,7 @@ namespace rsx
|
|||
m_cancel_btn->set_pos(x + 180, y + h + 20);
|
||||
|
||||
m_cancel_only = cancel_only;
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
bool list_view::get_cancel_only() const
|
||||
|
@ -219,7 +228,7 @@ namespace rsx
|
|||
|
||||
compiled_resource& list_view::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
auto& compiled = vertical_layout::get_compiled();
|
||||
compiled.add(m_highlight_box->get_compiled());
|
||||
|
|
|
@ -100,7 +100,7 @@ namespace rsx
|
|||
}
|
||||
|
||||
// Disable caching
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
|
||||
compiled_resources = rounded_rect::get_compiled();
|
||||
compiled_resources.add(m_text.get_compiled());
|
||||
|
|
|
@ -814,7 +814,7 @@ namespace rsx
|
|||
|
||||
compiled_resource& graph::get_compiled()
|
||||
{
|
||||
if (is_compiled)
|
||||
if (is_compiled())
|
||||
{
|
||||
return compiled_resources;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace rsx
|
|||
void progress_bar::set_limit(f32 limit)
|
||||
{
|
||||
m_limit = limit;
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void progress_bar::set_value(f32 value)
|
||||
|
@ -32,7 +32,7 @@ namespace rsx
|
|||
|
||||
f32 indicator_width = (w * m_value) / m_limit;
|
||||
indicator.set_size(static_cast<u16>(indicator_width), h);
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
void progress_bar::set_pos(s16 _x, s16 _y)
|
||||
|
@ -68,12 +68,12 @@ namespace rsx
|
|||
text_view.set_size(w, text_h);
|
||||
|
||||
set_pos(text_view.x, text_view.y);
|
||||
is_compiled = false;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
|
||||
compiled_resource& progress_bar::get_compiled()
|
||||
{
|
||||
if (!is_compiled)
|
||||
if (!is_compiled())
|
||||
{
|
||||
auto& compiled = overlay_element::get_compiled();
|
||||
compiled.add(text_view.get_compiled());
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "stdafx.h"
|
||||
#include "overlay_save_dialog.h"
|
||||
#include "overlay_video.h"
|
||||
#include "Utilities/date_time.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
|
@ -7,26 +8,18 @@ namespace rsx
|
|||
{
|
||||
namespace overlays
|
||||
{
|
||||
save_dialog::save_dialog_entry::save_dialog_entry(const std::string& text1, const std::string& text2, const std::string& text3, u8 resource_id, const std::vector<u8>& icon_buf)
|
||||
save_dialog::save_dialog_entry::save_dialog_entry(const std::string& text1, const std::string& text2, const std::string& text3, u8 resource_id, const std::vector<u8>& icon_buf, const std::string& video_path)
|
||||
{
|
||||
std::unique_ptr<overlay_element> image = std::make_unique<image_view>();
|
||||
std::unique_ptr<overlay_element> image = resource_id != image_resource_id::raw_image
|
||||
? std::make_unique<video_view>(video_path, resource_id)
|
||||
: !icon_buf.empty() ? std::make_unique<video_view>(video_path, icon_buf)
|
||||
: std::make_unique<video_view>(video_path, resource_config::standard_image_resource::save); // Fallback
|
||||
image->set_size(160, 110);
|
||||
image->set_padding(36, 36, 11, 11); // Square image, 88x88
|
||||
|
||||
if (resource_id != image_resource_id::raw_image)
|
||||
{
|
||||
static_cast<image_view*>(image.get())->set_image_resource(resource_id);
|
||||
}
|
||||
else if (!icon_buf.empty())
|
||||
if (resource_id == image_resource_id::raw_image && !icon_buf.empty())
|
||||
{
|
||||
image->set_padding(0, 0, 11, 11); // Half sized icon, 320x176->160x88
|
||||
icon_data = std::make_unique<image_info>(icon_buf);
|
||||
static_cast<image_view*>(image.get())->set_raw_image(icon_data.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback
|
||||
static_cast<image_view*>(image.get())->set_image_resource(resource_config::standard_image_resource::save);
|
||||
}
|
||||
|
||||
std::unique_ptr<overlay_element> text_stack = std::make_unique<vertical_layout>();
|
||||
|
@ -74,10 +67,18 @@ namespace rsx
|
|||
|
||||
// Pack
|
||||
this->pack_padding = 15;
|
||||
add_element(image);
|
||||
m_image = add_element(image);
|
||||
add_element(text_stack);
|
||||
}
|
||||
|
||||
void save_dialog::save_dialog_entry::set_selected(bool selected)
|
||||
{
|
||||
if (m_image)
|
||||
{
|
||||
static_cast<video_view*>(m_image)->set_active(selected);
|
||||
}
|
||||
}
|
||||
|
||||
save_dialog::save_dialog()
|
||||
{
|
||||
m_dim_background = std::make_unique<overlay_element>();
|
||||
|
@ -197,7 +198,7 @@ namespace rsx
|
|||
return result;
|
||||
}
|
||||
|
||||
s32 save_dialog::show(std::vector<SaveDataEntry>& save_entries, u32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay)
|
||||
s32 save_dialog::show(const std::string& base_dir, std::vector<SaveDataEntry>& save_entries, u32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay)
|
||||
{
|
||||
rsx_log.notice("Showing native UI save_dialog (save_entries=%d, focused=%d, op=0x%x, listSet=*0x%x, enable_overlay=%d)", save_entries.size(), focused, op, listSet, enable_overlay);
|
||||
|
||||
|
@ -218,7 +219,7 @@ namespace rsx
|
|||
{
|
||||
const std::string date_and_size = fmt::format("%s %s", entry.date(), entry.data_size());
|
||||
std::unique_ptr<overlay_element> e;
|
||||
e = std::make_unique<save_dialog_entry>(entry.subtitle, date_and_size, entry.details, image_resource_id::raw_image, entry.iconBuf);
|
||||
e = std::make_unique<save_dialog_entry>(entry.subtitle, date_and_size, entry.details, image_resource_id::raw_image, entry.iconBuf, base_dir + entry.dirName + "/ICON1.PAM");
|
||||
entries.emplace_back(std::move(e));
|
||||
}
|
||||
|
||||
|
@ -270,7 +271,7 @@ namespace rsx
|
|||
id = image_resource_id::raw_image;
|
||||
}
|
||||
|
||||
std::unique_ptr<overlay_element> new_stub = std::make_unique<save_dialog_entry>(title, get_localized_string(localized_string_id::CELL_SAVEDATA_NEW_SAVED_DATA_SUB_TITLE), "", id, icon);
|
||||
std::unique_ptr<overlay_element> new_stub = std::make_unique<save_dialog_entry>(title, get_localized_string(localized_string_id::CELL_SAVEDATA_NEW_SAVED_DATA_SUB_TITLE), "", id, icon, "");
|
||||
|
||||
m_list->add_entry(new_stub);
|
||||
}
|
||||
|
|
|
@ -13,11 +13,12 @@ namespace rsx
|
|||
private:
|
||||
struct save_dialog_entry : horizontal_layout
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<image_info> icon_data;
|
||||
|
||||
public:
|
||||
save_dialog_entry(const std::string& text1, const std::string& text2, const std::string& text3, u8 resource_id, const std::vector<u8>& icon_buf);
|
||||
save_dialog_entry(const std::string& text1, const std::string& text2, const std::string& text3, u8 resource_id, const std::vector<u8>& icon_buf, const std::string& video_path);
|
||||
void set_selected(bool selected) override;
|
||||
|
||||
private:
|
||||
overlay_element* m_image = nullptr;
|
||||
};
|
||||
|
||||
std::unique_ptr<overlay_element> m_dim_background;
|
||||
|
@ -38,7 +39,7 @@ namespace rsx
|
|||
|
||||
compiled_resource get_compiled() override;
|
||||
|
||||
s32 show(std::vector<SaveDataEntry>& save_entries, u32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay);
|
||||
s32 show(const std::string& base_dir, std::vector<SaveDataEntry>& save_entries, u32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
116
rpcs3/Emu/RSX/Overlays/overlay_video.cpp
Normal file
116
rpcs3/Emu/RSX/Overlays/overlay_video.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
#include "stdafx.h"
|
||||
#include "overlay_video.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace overlays
|
||||
{
|
||||
video_view::video_view(const std::string& video_path, const std::string& thumbnail_path)
|
||||
{
|
||||
init_video(video_path);
|
||||
|
||||
if (!thumbnail_path.empty())
|
||||
{
|
||||
m_thumbnail_info = std::make_unique<image_info>(thumbnail_path);
|
||||
set_raw_image(m_thumbnail_info.get());
|
||||
}
|
||||
}
|
||||
|
||||
video_view::video_view(const std::string& video_path, const std::vector<u8>& thumbnail_buf)
|
||||
{
|
||||
init_video(video_path);
|
||||
|
||||
if (!thumbnail_buf.empty())
|
||||
{
|
||||
m_thumbnail_info = std::make_unique<image_info>(thumbnail_buf);
|
||||
set_raw_image(m_thumbnail_info.get());
|
||||
}
|
||||
}
|
||||
|
||||
video_view::video_view(const std::string& video_path, u8 thumbnail_id)
|
||||
: m_thumbnail_id(thumbnail_id)
|
||||
{
|
||||
init_video(video_path);
|
||||
set_image_resource(thumbnail_id);
|
||||
}
|
||||
|
||||
video_view::~video_view()
|
||||
{
|
||||
}
|
||||
|
||||
void video_view::init_video(const std::string& video_path)
|
||||
{
|
||||
if (video_path.empty()) return;
|
||||
|
||||
m_video_source = Emu.GetCallbacks().make_video_source();
|
||||
ensure(!!m_video_source);
|
||||
|
||||
m_video_source->set_update_callback([this]()
|
||||
{
|
||||
if (m_video_active)
|
||||
{
|
||||
m_is_compiled = false;
|
||||
}
|
||||
});
|
||||
m_video_source->set_video_path(video_path);
|
||||
}
|
||||
|
||||
void video_view::set_active(bool active)
|
||||
{
|
||||
if (m_video_source)
|
||||
{
|
||||
m_video_source->set_active(active);
|
||||
m_video_active = active;
|
||||
m_is_compiled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void video_view::update()
|
||||
{
|
||||
if (m_video_active && m_video_source && m_video_source->get_active())
|
||||
{
|
||||
if (!m_video_source->has_new())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_buffer_index = (m_buffer_index + 1) % m_video_info.size();
|
||||
|
||||
auto& info = m_video_info.at(m_buffer_index);
|
||||
if (!info)
|
||||
{
|
||||
info = std::make_unique<video_info>();
|
||||
}
|
||||
|
||||
m_video_source->get_image(info->data, info->w, info->h, info->channels, info->bpp);
|
||||
info->dirty = true;
|
||||
|
||||
set_raw_image(info.get());
|
||||
m_is_compiled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_thumbnail_info && m_thumbnail_info.get() != external_ref)
|
||||
{
|
||||
set_raw_image(m_thumbnail_info.get());
|
||||
m_is_compiled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_thumbnail_id != image_resource_id::none && m_thumbnail_id != image_resource_ref)
|
||||
{
|
||||
set_image_resource(m_thumbnail_id);
|
||||
m_is_compiled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
compiled_resource& video_view::get_compiled()
|
||||
{
|
||||
update();
|
||||
|
||||
return external_ref ? image_view::get_compiled() : overlay_element::get_compiled();
|
||||
}
|
||||
}
|
||||
}
|
43
rpcs3/Emu/RSX/Overlays/overlay_video.h
Normal file
43
rpcs3/Emu/RSX/Overlays/overlay_video.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "overlay_controls.h"
|
||||
#include "util/video_source.h"
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace overlays
|
||||
{
|
||||
struct video_info : public image_info_base
|
||||
{
|
||||
using image_info_base::image_info_base;
|
||||
virtual ~video_info() {}
|
||||
const u8* get_data() const override { return data.empty() ? nullptr : data.data(); }
|
||||
|
||||
std::vector<u8> data;
|
||||
};
|
||||
|
||||
class video_view final : public image_view
|
||||
{
|
||||
public:
|
||||
video_view(const std::string& video_path, const std::string& thumbnail_path);
|
||||
video_view(const std::string& video_path, const std::vector<u8>& thumbnail_buf);
|
||||
video_view(const std::string& video_path, u8 thumbnail_id);
|
||||
virtual ~video_view();
|
||||
|
||||
void set_active(bool active);
|
||||
|
||||
void update();
|
||||
compiled_resource& get_compiled() override;
|
||||
|
||||
private:
|
||||
void init_video(const std::string& video_path);
|
||||
|
||||
usz m_buffer_index = 0;
|
||||
std::array<std::unique_ptr<video_info>, 2> m_video_info; // double buffer
|
||||
std::unique_ptr<video_source> m_video_source;
|
||||
std::unique_ptr<image_info> m_thumbnail_info;
|
||||
u8 m_thumbnail_id = image_resource_id::none;
|
||||
bool m_video_active = false; // This is the expected state. The actual state is found in the video source.
|
||||
};
|
||||
}
|
||||
}
|
|
@ -23,10 +23,6 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_ARM64
|
||||
#define AVX512_ICL_FUNC
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define AVX512_ICL_FUNC
|
||||
#else
|
||||
|
|
|
@ -53,7 +53,7 @@ private:
|
|||
#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
|
||||
#endif
|
||||
#endif
|
||||
#include "3rdparty/GPUOpen/include/vk_mem_alloc.h"
|
||||
#include "3rdparty/GPUOpen/VulkanMemoryAllocator/src/vk_mem_alloc.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
|
|
|
@ -386,22 +386,14 @@ namespace vk
|
|||
VK_BLEND_OP_ADD, VK_BLEND_OP_ADD);
|
||||
}
|
||||
|
||||
vk::image_view* ui_overlay_renderer::upload_simple_texture(vk::render_device& dev, vk::command_buffer& cmd,
|
||||
vk::data_heap& upload_heap, u64 key, u32 w, u32 h, u32 layers, bool font, bool temp, const void* pixel_src, u32 owner_uid)
|
||||
void ui_overlay_renderer::upload_simple_texture(vk::image* tex, vk::command_buffer& cmd,
|
||||
vk::data_heap& upload_heap, u32 w, u32 h, u32 layers, bool font, const void* pixel_src)
|
||||
{
|
||||
const VkFormat format = (font) ? VK_FORMAT_R8_UNORM : VK_FORMAT_B8G8R8A8_UNORM;
|
||||
const u32 pitch = (font) ? w : w * 4;
|
||||
const u32 data_size = pitch * h * layers;
|
||||
const auto offset = upload_heap.alloc<512>(data_size);
|
||||
const auto addr = upload_heap.map(offset, data_size);
|
||||
|
||||
const VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layers };
|
||||
|
||||
auto tex = std::make_unique<vk::image>(dev, dev.get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
VK_IMAGE_TYPE_2D, format, std::max(w, 1u), std::max(h, 1u), 1, 1, layers, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
0, VMM_ALLOCATION_POOL_UNDEFINED);
|
||||
|
||||
if (pixel_src && data_size)
|
||||
std::memcpy(addr, pixel_src, data_size);
|
||||
else if (data_size)
|
||||
|
@ -409,17 +401,32 @@ namespace vk
|
|||
|
||||
upload_heap.unmap();
|
||||
|
||||
VkBufferImageCopy region;
|
||||
region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, layers };
|
||||
region.bufferOffset = offset;
|
||||
region.bufferRowLength = w;
|
||||
region.bufferImageHeight = h;
|
||||
region.imageOffset = {};
|
||||
region.imageExtent = { static_cast<u32>(w), static_cast<u32>(h), 1u };
|
||||
|
||||
change_image_layout(cmd, tex.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, range);
|
||||
const VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layers };
|
||||
VkBufferImageCopy region
|
||||
{
|
||||
.bufferOffset = offset,
|
||||
.bufferRowLength = w,
|
||||
.bufferImageHeight = h,
|
||||
.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, layers },
|
||||
.imageOffset = {},
|
||||
.imageExtent = { static_cast<u32>(w), static_cast<u32>(h), 1u }
|
||||
};
|
||||
change_image_layout(cmd, tex, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, range);
|
||||
vkCmdCopyBufferToImage(cmd, upload_heap.heap->value, tex->value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
change_image_layout(cmd, tex.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range);
|
||||
change_image_layout(cmd, tex, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range);
|
||||
}
|
||||
|
||||
vk::image_view* ui_overlay_renderer::upload_simple_texture(vk::render_device& dev, vk::command_buffer& cmd,
|
||||
vk::data_heap& upload_heap, u64 key, u32 w, u32 h, u32 layers, bool font, bool temp, const void* pixel_src, u32 owner_uid)
|
||||
{
|
||||
const VkFormat format = (font) ? VK_FORMAT_R8_UNORM : VK_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
auto tex = std::make_unique<vk::image>(dev, dev.get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
VK_IMAGE_TYPE_2D, format, std::max(w, 1u), std::max(h, 1u), 1, 1, layers, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
0, VMM_ALLOCATION_POOL_UNDEFINED);
|
||||
|
||||
upload_simple_texture(tex.get(), cmd, upload_heap, w, h, layers, font, pixel_src);
|
||||
|
||||
auto view = std::make_unique<vk::image_view>(dev, tex.get());
|
||||
|
||||
|
@ -521,12 +528,23 @@ namespace vk
|
|||
true, false, bytes.data(), -1);
|
||||
}
|
||||
|
||||
vk::image_view* ui_overlay_renderer::find_temp_image(rsx::overlays::image_info* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid)
|
||||
vk::image_view* ui_overlay_renderer::find_temp_image(rsx::overlays::image_info_base* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid)
|
||||
{
|
||||
u64 key = reinterpret_cast<u64>(desc);
|
||||
auto found = temp_view_cache.find(key);
|
||||
if (found != temp_view_cache.end())
|
||||
return found->second.get();
|
||||
const bool dirty = std::exchange(desc->dirty, false);
|
||||
const u64 key = reinterpret_cast<u64>(desc);
|
||||
|
||||
auto cached = temp_view_cache.find(key);
|
||||
if (cached != temp_view_cache.end())
|
||||
{
|
||||
vk::image_view* view = cached->second.get();
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
upload_simple_texture(view->image(), cmd, upload_heap, desc->w, desc->h, 1, false, desc->get_data());
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
return upload_simple_texture(cmd.get_command_pool().get_owner(), cmd, upload_heap, key, desc->w, desc->h, 1,
|
||||
false, true, desc->get_data(), owner_uid);
|
||||
|
@ -693,7 +711,7 @@ namespace vk
|
|||
: rsx::overlays::texture_sampling_mode::font3D;
|
||||
break;
|
||||
case rsx::overlays::image_resource_id::raw_image:
|
||||
src = find_temp_image(static_cast<rsx::overlays::image_info*>(command.config.external_data_ref), cmd, upload_heap, ui.uid);
|
||||
src = find_temp_image(static_cast<rsx::overlays::image_info_base*>(command.config.external_data_ref), cmd, upload_heap, ui.uid);
|
||||
break;
|
||||
default:
|
||||
src = view_cache[command.config.texture_ref].get();
|
||||
|
|
|
@ -154,6 +154,9 @@ namespace vk
|
|||
|
||||
ui_overlay_renderer();
|
||||
|
||||
void upload_simple_texture(vk::image* tex, vk::command_buffer& cmd,
|
||||
vk::data_heap& upload_heap, u32 w, u32 h, u32 layers, bool font, const void* pixel_src);
|
||||
|
||||
vk::image_view* upload_simple_texture(vk::render_device& dev, vk::command_buffer& cmd,
|
||||
vk::data_heap& upload_heap, u64 key, u32 w, u32 h, u32 layers, bool font, bool temp, const void* pixel_src, u32 owner_uid);
|
||||
|
||||
|
@ -164,7 +167,7 @@ namespace vk
|
|||
void remove_temp_resources(u32 key);
|
||||
|
||||
vk::image_view* find_font(rsx::overlays::font* font, vk::command_buffer& cmd, vk::data_heap& upload_heap);
|
||||
vk::image_view* find_temp_image(rsx::overlays::image_info* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid);
|
||||
vk::image_view* find_temp_image(rsx::overlays::image_info_base* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid);
|
||||
|
||||
std::vector<VkPushConstantRange> get_push_constants() override;
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../VulkanAPI.h"
|
||||
#include "../../rsx_utils.h"
|
||||
#include "shared.h"
|
||||
|
||||
#include "3rdparty/GPUOpen/include/vk_mem_alloc.h"
|
||||
|
||||
namespace vk
|
||||
{
|
||||
// Memory Allocator - base class
|
||||
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
#include "../../rsx_utils.h"
|
||||
#include "shared.h"
|
||||
|
||||
#include "3rdparty/GPUOpen/include/vk_mem_alloc.h"
|
||||
#include "3rdparty/GPUOpen/VulkanMemoryAllocator/src/vk_mem_alloc.h"
|
||||
|
||||
namespace vk
|
||||
{
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace vk
|
|||
}
|
||||
|
||||
border_color_t::border_color_t(const color4f& color, VkFormat fmt, VkImageAspectFlags aspect)
|
||||
: format(fmt), aspect(aspect), color_value(color)
|
||||
: storage_key(0), format(fmt), aspect(aspect), color_value(color)
|
||||
{
|
||||
const auto encoded_color = rsx::encode_color_to_storage_key(color);
|
||||
value = vk::get_border_color(encoded_color);
|
||||
|
|
|
@ -111,6 +111,7 @@ struct EmuCallbacks
|
|||
std::function<bool()> display_sleep_control_supported;
|
||||
std::function<void(bool)> enable_display_sleep;
|
||||
std::function<void()> check_microphone_permissions;
|
||||
std::function<std::unique_ptr<class video_source>()> make_video_source;
|
||||
};
|
||||
|
||||
namespace utils
|
||||
|
|
|
@ -236,6 +236,7 @@ void pad_thread::SetRumble(const u32 pad, u8 large_motor, bool small_motor)
|
|||
if (pad >= m_pads.size())
|
||||
return;
|
||||
|
||||
m_pads[pad]->m_last_rumble_time_us = get_system_time();
|
||||
m_pads[pad]->m_vibrateMotors[0].m_value = large_motor;
|
||||
m_pads[pad]->m_vibrateMotors[1].m_value = small_motor ? 255 : 0;
|
||||
}
|
||||
|
|
|
@ -147,6 +147,7 @@
|
|||
<ClCompile Include="Emu\RSX\Overlays\overlay_compile_notification.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_user_list_dialog.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_utils.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_video.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\Trophies\overlay_trophy_list_dialog.cpp" />
|
||||
|
@ -682,6 +683,7 @@
|
|||
<ClInclude Include="Emu\RSX\Overlays\overlay_manager.h" />
|
||||
<ClInclude Include="Emu\RSX\Overlays\overlay_media_list_dialog.h" />
|
||||
<ClInclude Include="Emu\RSX\Overlays\overlay_progress_bar.hpp" />
|
||||
<ClInclude Include="Emu\RSX\Overlays\overlay_video.h" />
|
||||
<ClInclude Include="Emu\RSX\Overlays\Trophies\overlay_trophy_list_dialog.h" />
|
||||
<ClInclude Include="Emu\RSX\Program\FragmentProgramRegister.h" />
|
||||
<ClInclude Include="Emu\RSX\Program\GLSLTypes.h" />
|
||||
|
@ -760,6 +762,7 @@
|
|||
<ClInclude Include="..\Utilities\Timer.h" />
|
||||
<ClInclude Include="util\types.hpp" />
|
||||
<ClInclude Include="..\Utilities\version.h" />
|
||||
<ClInclude Include="util\video_source.h" />
|
||||
<ClInclude Include="util\vm.hpp" />
|
||||
<ClInclude Include="util\asm.hpp" />
|
||||
<ClInclude Include="Crypto\aes.h" />
|
||||
|
|
|
@ -1354,6 +1354,9 @@
|
|||
<ClCompile Include="Emu\RSX\Common\texture_cache_types.cpp">
|
||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_video.cpp">
|
||||
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
|
@ -2716,6 +2719,12 @@
|
|||
<ClInclude Include="Emu\RSX\Program\FragmentProgramRegister.h">
|
||||
<Filter>Emu\GPU\RSX\Program</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util\video_source.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\Overlays\overlay_video.h">
|
||||
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "Emu/Cell/Modules/sceNpTrophy.h"
|
||||
#include "Emu/Io/Null/null_camera_handler.h"
|
||||
#include "Emu/Io/Null/null_music_handler.h"
|
||||
#include "util/video_source.h"
|
||||
|
||||
#include <clocale>
|
||||
|
||||
|
@ -173,6 +174,8 @@ void headless_application::InitializeCallbacks()
|
|||
|
||||
callbacks.check_microphone_permissions = [](){};
|
||||
|
||||
callbacks.make_video_source = [](){ return nullptr; };
|
||||
|
||||
Emu.SetCallbacks(std::move(callbacks));
|
||||
}
|
||||
|
||||
|
|
|
@ -807,6 +807,7 @@
|
|||
<ClCompile Include="rpcs3qt\progress_indicator.cpp" />
|
||||
<ClCompile Include="rpcs3qt\qt_camera_handler.cpp" />
|
||||
<ClCompile Include="rpcs3qt\qt_music_handler.cpp" />
|
||||
<ClCompile Include="rpcs3qt\qt_video_source.cpp" />
|
||||
<ClCompile Include="rpcs3qt\raw_mouse_settings_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\recvmessage_dialog_frame.cpp" />
|
||||
<ClCompile Include="rpcs3qt\render_creator.cpp" />
|
||||
|
@ -831,6 +832,7 @@
|
|||
<ClCompile Include="rpcs3qt\vfs_dialog_usb_input.cpp" />
|
||||
<ClCompile Include="rpcs3qt\vfs_dialog_usb_tab.cpp" />
|
||||
<ClCompile Include="rpcs3qt\vfs_tool_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\video_label.cpp" />
|
||||
<ClCompile Include="rpcs3qt\_discord_utils.cpp" />
|
||||
<ClCompile Include="rpcs3qt\find_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\game_compatibility.cpp" />
|
||||
|
@ -1514,6 +1516,7 @@
|
|||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\qt_video_source.h" />
|
||||
<ClInclude Include="rpcs3qt\richtext_item_delegate.h" />
|
||||
<CustomBuild Include="rpcs3qt\sendmessage_dialog_frame.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
|
@ -1669,6 +1672,7 @@
|
|||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DHAVE_SDL3 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\uuid.h" />
|
||||
<ClInclude Include="rpcs3qt\video_label.h" />
|
||||
<ClInclude Include="rpcs3qt\_discord_utils.h" />
|
||||
<CustomBuild Include="rpcs3qt\find_dialog.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
|
|
|
@ -196,6 +196,12 @@
|
|||
<Filter Include="Gui\savestates">
|
||||
<UniqueIdentifier>{9b51636c-b371-425b-86d3-be003774a1b7}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Io\video">
|
||||
<UniqueIdentifier>{2bb5cec5-5acb-40c0-a388-68db05dff305}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Gui\widgets">
|
||||
<UniqueIdentifier>{149c596b-83e7-43f8-b5db-6108694434ef}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
|
@ -1176,6 +1182,12 @@
|
|||
<ClCompile Include="rpcs3qt\gui_game_info.cpp">
|
||||
<Filter>Gui\game list</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\qt_video_source.cpp">
|
||||
<Filter>Io\video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\video_label.cpp">
|
||||
<Filter>Gui\widgets</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
|
@ -1220,9 +1232,6 @@
|
|||
<ClInclude Include="rpcs3qt\qt_utils.h">
|
||||
<Filter>Gui\utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\custom_dock_widget.h">
|
||||
<Filter>Gui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\user_account.h">
|
||||
<Filter>Gui\user accounts</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1385,6 +1394,15 @@
|
|||
<ClInclude Include="rpcs3qt\gui_game_info.h">
|
||||
<Filter>Gui\game list</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\qt_video_source.h">
|
||||
<Filter>Io\video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\custom_dock_widget.h">
|
||||
<Filter>Gui\widgets</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\video_label.h">
|
||||
<Filter>Gui\widgets</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace rpcs3
|
|||
// Currently accessible by Windows and Linux build scripts, see implementations when doing MACOSX
|
||||
const utils::version& get_version()
|
||||
{
|
||||
static constexpr utils::version version{ 0, 0, 35, utils::version_type::alpha, 1, RPCS3_GIT_VERSION };
|
||||
static constexpr utils::version version{ 0, 0, 36, utils::version_type::alpha, 1, RPCS3_GIT_VERSION };
|
||||
return version;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ add_library(rpcs3_ui STATIC
|
|||
qt_camera_video_sink.cpp
|
||||
qt_music_handler.cpp
|
||||
qt_utils.cpp
|
||||
qt_video_source.cpp
|
||||
raw_mouse_settings_dialog.cpp
|
||||
register_editor_dialog.cpp
|
||||
recvmessage_dialog_frame.cpp
|
||||
|
@ -112,6 +113,7 @@ add_library(rpcs3_ui STATIC
|
|||
vfs_dialog_usb_input.cpp
|
||||
vfs_dialog_usb_tab.cpp
|
||||
vfs_tool_dialog.cpp
|
||||
video_label.cpp
|
||||
welcome_dialog.cpp
|
||||
|
||||
about_dialog.ui
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
game_list::game_list() : QTableWidget(), game_list_base()
|
||||
{
|
||||
m_icon_ready_callback = [this](const game_info& game)
|
||||
m_icon_ready_callback = [this](const movie_item_base* item)
|
||||
{
|
||||
Q_EMIT IconReady(game);
|
||||
Q_EMIT IconReady(item);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -139,6 +139,8 @@ void game_list::mouseMoveEvent(QMouseEvent* event)
|
|||
}
|
||||
|
||||
m_last_hover_item = new_item;
|
||||
|
||||
QTableWidget::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void game_list::mouseDoubleClickEvent(QMouseEvent* ev)
|
||||
|
@ -169,13 +171,15 @@ void game_list::keyPressEvent(QKeyEvent* event)
|
|||
QTableWidget::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void game_list::leaveEvent(QEvent* /*event*/)
|
||||
void game_list::leaveEvent(QEvent* event)
|
||||
{
|
||||
if (m_last_hover_item)
|
||||
{
|
||||
m_last_hover_item->set_active(false);
|
||||
m_last_hover_item = nullptr;
|
||||
}
|
||||
|
||||
QTableWidget::leaveEvent(event);
|
||||
}
|
||||
|
||||
void game_list::FocusAndSelectFirstEntryIfNoneIs()
|
||||
|
|
|
@ -36,7 +36,7 @@ public Q_SLOTS:
|
|||
|
||||
Q_SIGNALS:
|
||||
void FocusToSearchBar();
|
||||
void IconReady(const game_info& game);
|
||||
void IconReady(const movie_item_base* item);
|
||||
|
||||
protected:
|
||||
movie_item* m_last_hover_item = nullptr;
|
||||
|
|
|
@ -33,7 +33,7 @@ void game_list_base::repaint_icons(std::vector<game_info>& game_data, const QCol
|
|||
IconLoadFunction(game, device_pixel_ratio, cancel);
|
||||
});
|
||||
|
||||
item->call_icon_func();
|
||||
item->image_change_callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ void game_list_base::IconLoadFunction(game_info game, qreal device_pixel_ratio,
|
|||
if (!cancel || !cancel->load())
|
||||
{
|
||||
if (m_icon_ready_callback)
|
||||
m_icon_ready_callback(game);
|
||||
m_icon_ready_callback(game->item);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ protected:
|
|||
QPixmap PaintedPixmap(const QPixmap& icon, qreal device_pixel_ratio, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& compatibility_color = {}) const;
|
||||
QColor GetGridCompatibilityColor(const QString& string) const;
|
||||
|
||||
std::function<void(const game_info&)> m_icon_ready_callback{};
|
||||
std::function<void(const movie_item_base*)> m_icon_ready_callback{};
|
||||
bool m_draw_compat_status_to_grid{};
|
||||
bool m_is_list_layout{};
|
||||
QSize m_icon_size{};
|
||||
|
|
|
@ -14,15 +14,14 @@ game_list_grid::game_list_grid()
|
|||
setObjectName("game_list_grid");
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
m_icon_ready_callback = [this](const game_info& game)
|
||||
m_icon_ready_callback = [this](const movie_item_base* item)
|
||||
{
|
||||
Q_EMIT IconReady(game);
|
||||
Q_EMIT IconReady(item);
|
||||
};
|
||||
|
||||
connect(this, &game_list_grid::IconReady, this, [this](const game_info& game)
|
||||
connect(this, &game_list_grid::IconReady, this, [this](const movie_item_base* item)
|
||||
{
|
||||
if (!game || !game->item) return;
|
||||
game->item->call_icon_func();
|
||||
if (item) item->image_change_callback();
|
||||
}, Qt::QueuedConnection); // The default 'AutoConnection' doesn't seem to work in this specific case...
|
||||
|
||||
connect(this, &flow_widget::ItemSelectionChanged, this, [this](int index)
|
||||
|
@ -82,7 +81,7 @@ void game_list_grid::populate(
|
|||
item->setToolTip(tr("%0 [%1]").arg(title).arg(serial));
|
||||
}
|
||||
|
||||
item->set_icon_func([this, item, game](const QVideoFrame& frame)
|
||||
item->set_image_change_callback([this, item, game](const QVideoFrame& frame)
|
||||
{
|
||||
if (!item || !game)
|
||||
{
|
||||
|
@ -110,7 +109,7 @@ void game_list_grid::populate(
|
|||
|
||||
if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam))
|
||||
{
|
||||
item->set_movie_path(QString::fromStdString(game->info.movie_path));
|
||||
item->set_video_path(game->info.movie_path);
|
||||
}
|
||||
|
||||
if (selected_item_id == game->info.path + game->info.icon_path)
|
||||
|
@ -155,7 +154,7 @@ void game_list_grid::repaint_icons(std::vector<game_info>& game_data, const QCol
|
|||
{
|
||||
// We don't have an icon. Set a placeholder to initialize the layout.
|
||||
game->pxmap = placeholder;
|
||||
item->call_icon_func();
|
||||
item->image_change_callback();
|
||||
}
|
||||
|
||||
item->set_icon_load_func([this, game, device_pixel_ratio, cancel = item->icon_loading_aborted()](int)
|
||||
|
|
|
@ -33,5 +33,5 @@ Q_SIGNALS:
|
|||
void FocusToSearchBar();
|
||||
void ItemDoubleClicked(const game_info& game);
|
||||
void ItemSelectionChanged(const game_info& game);
|
||||
void IconReady(const game_info& game);
|
||||
void IconReady(const movie_item_base* item);
|
||||
};
|
||||
|
|
|
@ -52,10 +52,9 @@ game_list_table::game_list_table(game_list_frame* frame, std::shared_ptr<persist
|
|||
}
|
||||
});
|
||||
|
||||
connect(this, &game_list::IconReady, this, [this](const game_info& game)
|
||||
connect(this, &game_list::IconReady, this, [this](const movie_item_base* item)
|
||||
{
|
||||
if (!game || !game->item) return;
|
||||
game->item->call_icon_func();
|
||||
if (item) item->image_change_callback();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -243,7 +242,7 @@ void game_list_table::populate(
|
|||
custom_table_widget_item* icon_item = new custom_table_widget_item;
|
||||
game->item = icon_item;
|
||||
|
||||
icon_item->set_icon_func([this, icon_item, game](const QVideoFrame& frame)
|
||||
icon_item->set_image_change_callback([this, icon_item, game](const QVideoFrame& frame)
|
||||
{
|
||||
if (!icon_item || !game)
|
||||
{
|
||||
|
@ -293,7 +292,7 @@ void game_list_table::populate(
|
|||
|
||||
if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam))
|
||||
{
|
||||
icon_item->set_movie_path(QString::fromStdString(game->info.movie_path));
|
||||
icon_item->set_video_path(game->info.movie_path);
|
||||
}
|
||||
|
||||
icon_item->setData(Qt::UserRole, index, true);
|
||||
|
|
|
@ -68,8 +68,6 @@ gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon,
|
|||
, m_start_games_fullscreen(force_fullscreen)
|
||||
, m_renderer(g_cfg.video.renderer)
|
||||
{
|
||||
load_gui_settings();
|
||||
|
||||
m_window_title = Emu.GetFormattedTitle(0);
|
||||
|
||||
if (!g_cfg_recording.load())
|
||||
|
@ -121,8 +119,7 @@ gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon,
|
|||
create();
|
||||
}
|
||||
|
||||
m_shortcut_handler = new shortcut_handler(gui::shortcuts::shortcut_handler_id::game_window, this, m_gui_settings);
|
||||
connect(m_shortcut_handler, &shortcut_handler::shortcut_activated, this, &gs_frame::handle_shortcut);
|
||||
load_gui_settings();
|
||||
|
||||
// Change cursor when in fullscreen.
|
||||
connect(this, &QWindow::visibilityChanged, this, [this](QWindow::Visibility visibility)
|
||||
|
@ -174,6 +171,20 @@ void gs_frame::load_gui_settings()
|
|||
m_lock_mouse_in_fullscreen = m_gui_settings->GetValue(gui::gs_lockMouseFs).toBool();
|
||||
m_hide_mouse_after_idletime = m_gui_settings->GetValue(gui::gs_hideMouseIdle).toBool();
|
||||
m_hide_mouse_idletime = m_gui_settings->GetValue(gui::gs_hideMouseIdleTime).toUInt();
|
||||
|
||||
if (m_disable_kb_hotkeys)
|
||||
{
|
||||
if (m_shortcut_handler)
|
||||
{
|
||||
m_shortcut_handler->deleteLater();
|
||||
m_shortcut_handler = nullptr;
|
||||
}
|
||||
}
|
||||
else if (!m_shortcut_handler)
|
||||
{
|
||||
m_shortcut_handler = new shortcut_handler(gui::shortcuts::shortcut_handler_id::game_window, this, m_gui_settings);
|
||||
connect(m_shortcut_handler, &shortcut_handler::shortcut_activated, this, &gs_frame::handle_shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
void gs_frame::update_shortcuts()
|
||||
|
@ -1101,7 +1112,7 @@ void gs_frame::handle_cursor(QWindow::Visibility visibility, bool visibility_cha
|
|||
|
||||
void gs_frame::mouse_hide_timeout()
|
||||
{
|
||||
// Our idle timeout occured, so we update the cursor
|
||||
// Our idle timeout occurred, so we update the cursor
|
||||
if (m_hide_mouse_after_idletime && m_show_mouse)
|
||||
{
|
||||
handle_cursor(visibility(), false, false, false);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "Emu/vfs_config.h"
|
||||
#include "util/init_mutex.hpp"
|
||||
#include "util/console.h"
|
||||
#include "qt_video_source.h"
|
||||
#include "trophy_notification_helper.h"
|
||||
#include "save_data_dialog.h"
|
||||
#include "msg_dialog_frame.h"
|
||||
|
@ -955,6 +956,8 @@ void gui_application::InitializeCallbacks()
|
|||
});
|
||||
};
|
||||
|
||||
callbacks.make_video_source = [](){ return std::make_unique<qt_video_source_wrapper>(); };
|
||||
|
||||
Emu.SetCallbacks(std::move(callbacks));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +1,14 @@
|
|||
#include "stdafx.h"
|
||||
#include "movie_item_base.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
movie_item_base::movie_item_base()
|
||||
movie_item_base::movie_item_base() : qt_video_source()
|
||||
{
|
||||
init_pointers();
|
||||
}
|
||||
|
||||
movie_item_base::~movie_item_base()
|
||||
{
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->stop();
|
||||
}
|
||||
|
||||
if (m_media_player)
|
||||
{
|
||||
m_media_player->stop();
|
||||
}
|
||||
stop_movie();
|
||||
|
||||
wait_for_icon_loading(true);
|
||||
wait_for_size_on_disk_loading(true);
|
||||
|
@ -30,146 +20,6 @@ void movie_item_base::init_pointers()
|
|||
m_size_on_disk_loading_aborted.reset(new atomic_t<bool>(false));
|
||||
}
|
||||
|
||||
void movie_item_base::set_active(bool active)
|
||||
{
|
||||
if (!std::exchange(m_active, active) && active)
|
||||
{
|
||||
init_movie();
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->jumpToFrame(1);
|
||||
m_movie->start();
|
||||
}
|
||||
|
||||
if (m_media_player)
|
||||
{
|
||||
m_media_player->play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void movie_item_base::init_movie()
|
||||
{
|
||||
if (m_movie || m_media_player)
|
||||
{
|
||||
// Already initialized
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_icon_callback || m_movie_path.isEmpty() || !QFile::exists(m_movie_path))
|
||||
{
|
||||
m_movie_path.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString lower = m_movie_path.toLower();
|
||||
|
||||
if (lower.endsWith(".gif"))
|
||||
{
|
||||
m_movie.reset(new QMovie(m_movie_path));
|
||||
m_movie_path.clear();
|
||||
|
||||
if (!m_movie->isValid())
|
||||
{
|
||||
m_movie.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
QObject::connect(m_movie.get(), &QMovie::frameChanged, m_movie.get(), [this](int)
|
||||
{
|
||||
m_icon_callback({});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (lower.endsWith(".pam"))
|
||||
{
|
||||
// We can't set PAM files as source of the video player, so we have to feed them as raw data.
|
||||
QFile file(m_movie_path);
|
||||
if (!file.open(QFile::OpenModeFlag::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Decode the pam properly before pushing it to the player
|
||||
m_movie_data = file.readAll();
|
||||
if (m_movie_data.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_movie_buffer.reset(new QBuffer(&m_movie_data));
|
||||
m_movie_buffer->open(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
m_video_sink.reset(new QVideoSink());
|
||||
QObject::connect(m_video_sink.get(), &QVideoSink::videoFrameChanged, m_video_sink.get(), [this](const QVideoFrame& frame)
|
||||
{
|
||||
m_icon_callback(frame);
|
||||
});
|
||||
|
||||
m_media_player.reset(new QMediaPlayer());
|
||||
m_media_player->setVideoSink(m_video_sink.get());
|
||||
m_media_player->setLoops(QMediaPlayer::Infinite);
|
||||
|
||||
if (m_movie_buffer)
|
||||
{
|
||||
m_media_player->setSourceDevice(m_movie_buffer.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_media_player->setSource(m_movie_path);
|
||||
}
|
||||
}
|
||||
|
||||
void movie_item_base::stop_movie()
|
||||
{
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->stop();
|
||||
}
|
||||
|
||||
m_video_sink.reset();
|
||||
m_media_player.reset();
|
||||
m_movie_buffer.reset();
|
||||
m_movie_data.clear();
|
||||
}
|
||||
|
||||
QPixmap movie_item_base::get_movie_image(const QVideoFrame& frame) const
|
||||
{
|
||||
if (!m_active)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
return m_movie->currentPixmap();
|
||||
}
|
||||
|
||||
if (!frame.isValid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get image. This usually also converts the image to ARGB32.
|
||||
return QPixmap::fromImage(frame.toImage());
|
||||
}
|
||||
|
||||
void movie_item_base::call_icon_func() const
|
||||
{
|
||||
if (m_icon_callback)
|
||||
{
|
||||
m_icon_callback({});
|
||||
}
|
||||
}
|
||||
|
||||
void movie_item_base::set_icon_func(const icon_callback_t& func)
|
||||
{
|
||||
m_icon_callback = func;
|
||||
}
|
||||
|
||||
void movie_item_base::call_icon_load_func(int index)
|
||||
{
|
||||
if (!m_icon_load_callback || m_icon_loading || m_icon_loading_aborted->load())
|
||||
|
|
|
@ -1,25 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "movie_item_base.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Utilities/mutex.h"
|
||||
#include "qt_video_source.h"
|
||||
|
||||
#include <QMovie>
|
||||
#include <QThread>
|
||||
#include <QBuffer>
|
||||
#include <QMediaPlayer>
|
||||
#include <QVideoSink>
|
||||
#include <QVideoFrame>
|
||||
#include <QPixmap>
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
using icon_callback_t = std::function<void(const QVideoFrame&)>;
|
||||
using icon_load_callback_t = std::function<void(int)>;
|
||||
using size_calc_callback_t = std::function<void()>;
|
||||
|
||||
class movie_item_base
|
||||
class movie_item_base : public qt_video_source
|
||||
{
|
||||
public:
|
||||
movie_item_base();
|
||||
|
@ -27,25 +18,6 @@ public:
|
|||
|
||||
void init_pointers();
|
||||
|
||||
void set_active(bool active);
|
||||
|
||||
[[nodiscard]] bool get_active() const
|
||||
{
|
||||
return m_active;
|
||||
}
|
||||
|
||||
void set_movie_path(QString path)
|
||||
{
|
||||
m_movie_path = std::move(path);
|
||||
}
|
||||
|
||||
void init_movie();
|
||||
void stop_movie();
|
||||
QPixmap get_movie_image(const QVideoFrame& frame) const;
|
||||
|
||||
void call_icon_func() const;
|
||||
void set_icon_func(const icon_callback_t& func);
|
||||
|
||||
void call_icon_load_func(int index);
|
||||
void set_icon_load_func(const icon_load_callback_t& func);
|
||||
|
||||
|
@ -77,23 +49,13 @@ public:
|
|||
|
||||
shared_mutex pixmap_mutex;
|
||||
|
||||
protected:
|
||||
QString m_movie_path;
|
||||
QByteArray m_movie_data{};
|
||||
std::unique_ptr<QBuffer> m_movie_buffer;
|
||||
std::unique_ptr<QMediaPlayer> m_media_player;
|
||||
std::shared_ptr<QVideoSink> m_video_sink;
|
||||
std::shared_ptr<QMovie> m_movie;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QThread> m_icon_load_thread;
|
||||
std::unique_ptr<QThread> m_size_calc_thread;
|
||||
bool m_active = false;
|
||||
atomic_t<bool> m_size_on_disk_loading = false;
|
||||
atomic_t<bool> m_icon_loading = false;
|
||||
size_calc_callback_t m_size_calc_callback = nullptr;
|
||||
icon_load_callback_t m_icon_load_callback = nullptr;
|
||||
icon_callback_t m_icon_callback = nullptr;
|
||||
|
||||
std::shared_ptr<atomic_t<bool>> m_icon_loading_aborted;
|
||||
std::shared_ptr<atomic_t<bool>> m_size_on_disk_loading_aborted;
|
||||
|
|
268
rpcs3/rpcs3qt/qt_video_source.cpp
Normal file
268
rpcs3/rpcs3qt/qt_video_source.cpp
Normal file
|
@ -0,0 +1,268 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "qt_video_source.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
qt_video_source::qt_video_source()
|
||||
: video_source()
|
||||
{
|
||||
}
|
||||
|
||||
qt_video_source::~qt_video_source()
|
||||
{
|
||||
stop_movie();
|
||||
}
|
||||
|
||||
void qt_video_source::set_video_path(const std::string& video_path)
|
||||
{
|
||||
m_video_path = QString::fromStdString(video_path);
|
||||
}
|
||||
|
||||
void qt_video_source::set_active(bool active)
|
||||
{
|
||||
if (m_active.exchange(active) == active) return;
|
||||
|
||||
if (active)
|
||||
{
|
||||
start_movie();
|
||||
}
|
||||
else
|
||||
{
|
||||
stop_movie();
|
||||
}
|
||||
}
|
||||
|
||||
void qt_video_source::image_change_callback() const
|
||||
{
|
||||
if (m_image_change_callback)
|
||||
{
|
||||
m_image_change_callback({});
|
||||
}
|
||||
}
|
||||
|
||||
void qt_video_source::set_image_change_callback(const std::function<void(const QVideoFrame&)>& func)
|
||||
{
|
||||
m_image_change_callback = func;
|
||||
}
|
||||
|
||||
void qt_video_source::init_movie()
|
||||
{
|
||||
if (m_movie || m_media_player)
|
||||
{
|
||||
// Already initialized
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_image_change_callback || m_video_path.isEmpty() || !QFile::exists(m_video_path))
|
||||
{
|
||||
m_video_path.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString lower = m_video_path.toLower();
|
||||
|
||||
if (lower.endsWith(".gif"))
|
||||
{
|
||||
m_movie.reset(new QMovie(m_video_path));
|
||||
m_video_path.clear();
|
||||
|
||||
if (!m_movie->isValid())
|
||||
{
|
||||
m_movie.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
QObject::connect(m_movie.get(), &QMovie::frameChanged, m_movie.get(), [this](int)
|
||||
{
|
||||
m_image_change_callback({});
|
||||
m_has_new = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (lower.endsWith(".pam"))
|
||||
{
|
||||
// We can't set PAM files as source of the video player, so we have to feed them as raw data.
|
||||
QFile file(m_video_path);
|
||||
if (!file.open(QFile::OpenModeFlag::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Decode the pam properly before pushing it to the player
|
||||
m_video_data = file.readAll();
|
||||
if (m_video_data.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_video_buffer.reset(new QBuffer(&m_video_data));
|
||||
m_video_buffer->open(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
m_video_sink.reset(new QVideoSink());
|
||||
QObject::connect(m_video_sink.get(), &QVideoSink::videoFrameChanged, m_video_sink.get(), [this](const QVideoFrame& frame)
|
||||
{
|
||||
m_image_change_callback(frame);
|
||||
m_has_new = true;
|
||||
});
|
||||
|
||||
m_media_player.reset(new QMediaPlayer());
|
||||
m_media_player->setVideoSink(m_video_sink.get());
|
||||
m_media_player->setLoops(QMediaPlayer::Infinite);
|
||||
|
||||
if (m_video_buffer)
|
||||
{
|
||||
m_media_player->setSourceDevice(m_video_buffer.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_media_player->setSource(m_video_path);
|
||||
}
|
||||
}
|
||||
|
||||
void qt_video_source::start_movie()
|
||||
{
|
||||
init_movie();
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->jumpToFrame(1);
|
||||
m_movie->start();
|
||||
}
|
||||
|
||||
if (m_media_player)
|
||||
{
|
||||
m_media_player->play();
|
||||
}
|
||||
|
||||
m_active = true;
|
||||
}
|
||||
|
||||
void qt_video_source::stop_movie()
|
||||
{
|
||||
m_active = false;
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->stop();
|
||||
}
|
||||
|
||||
m_video_sink.reset();
|
||||
m_media_player.reset();
|
||||
m_video_buffer.reset();
|
||||
m_video_data.clear();
|
||||
}
|
||||
|
||||
QPixmap qt_video_source::get_movie_image(const QVideoFrame& frame) const
|
||||
{
|
||||
if (!m_active)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
return m_movie->currentPixmap();
|
||||
}
|
||||
|
||||
if (!frame.isValid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get image. This usually also converts the image to ARGB32.
|
||||
return QPixmap::fromImage(frame.toImage());
|
||||
}
|
||||
|
||||
void qt_video_source::get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp)
|
||||
{
|
||||
if (!m_has_new.exchange(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_image_mutex);
|
||||
|
||||
if (m_image.isNull())
|
||||
{
|
||||
w = h = ch = bpp = 0;
|
||||
data.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
w = m_image.width();
|
||||
h = m_image.height();
|
||||
ch = m_image.colorCount();
|
||||
bpp = m_image.depth();
|
||||
|
||||
data.resize(m_image.height() * m_image.bytesPerLine());
|
||||
std::memcpy(data.data(), m_image.constBits(), data.size());
|
||||
}
|
||||
|
||||
qt_video_source_wrapper::~qt_video_source_wrapper()
|
||||
{
|
||||
Emu.BlockingCallFromMainThread([this]()
|
||||
{
|
||||
m_qt_video_source.reset();
|
||||
});
|
||||
}
|
||||
|
||||
void qt_video_source_wrapper::set_video_path(const std::string& video_path)
|
||||
{
|
||||
Emu.CallFromMainThread([this, path = video_path]()
|
||||
{
|
||||
m_qt_video_source = std::make_unique<qt_video_source>();
|
||||
m_qt_video_source->m_image_change_callback = [this](const QVideoFrame& frame)
|
||||
{
|
||||
std::unique_lock lock(m_qt_video_source->m_image_mutex);
|
||||
|
||||
if (m_qt_video_source->m_movie)
|
||||
{
|
||||
m_qt_video_source->m_image = m_qt_video_source->m_movie->currentImage();
|
||||
}
|
||||
else if (frame.isValid())
|
||||
{
|
||||
// Get image. This usually also converts the image to ARGB32.
|
||||
m_qt_video_source->m_image = frame.toImage();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_qt_video_source->m_image.format() != QImage::Format_RGBA8888)
|
||||
{
|
||||
m_qt_video_source->m_image.convertTo(QImage::Format_RGBA8888);
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
notify_update();
|
||||
};
|
||||
m_qt_video_source->set_video_path(path);
|
||||
});
|
||||
}
|
||||
|
||||
void qt_video_source_wrapper::set_active(bool active)
|
||||
{
|
||||
Emu.CallFromMainThread([this, active]()
|
||||
{
|
||||
m_qt_video_source->set_active(true);
|
||||
});
|
||||
}
|
||||
|
||||
bool qt_video_source_wrapper::get_active() const
|
||||
{
|
||||
ensure(m_qt_video_source);
|
||||
|
||||
return m_qt_video_source->get_active();
|
||||
}
|
||||
|
||||
void qt_video_source_wrapper::get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp)
|
||||
{
|
||||
ensure(m_qt_video_source);
|
||||
|
||||
m_qt_video_source->get_image(data, w, h, ch, bpp);
|
||||
}
|
75
rpcs3/rpcs3qt/qt_video_source.h
Normal file
75
rpcs3/rpcs3qt/qt_video_source.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/video_source.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Utilities/mutex.h"
|
||||
|
||||
#include <QMovie>
|
||||
#include <QBuffer>
|
||||
#include <QMediaPlayer>
|
||||
#include <QVideoSink>
|
||||
#include <QVideoFrame>
|
||||
#include <QPixmap>
|
||||
|
||||
class qt_video_source : public video_source
|
||||
{
|
||||
public:
|
||||
qt_video_source();
|
||||
virtual ~qt_video_source();
|
||||
|
||||
void set_video_path(const std::string& video_path) override;
|
||||
const QString& video_path() const { return m_video_path; }
|
||||
|
||||
void get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp) override;
|
||||
bool has_new() const override { return m_has_new; }
|
||||
|
||||
void set_active(bool active) override;
|
||||
bool get_active() const override { return m_active; }
|
||||
|
||||
void start_movie();
|
||||
void stop_movie();
|
||||
|
||||
QPixmap get_movie_image(const QVideoFrame& frame) const;
|
||||
|
||||
void image_change_callback() const;
|
||||
void set_image_change_callback(const std::function<void(const QVideoFrame&)>& func);
|
||||
|
||||
protected:
|
||||
void init_movie();
|
||||
|
||||
shared_mutex m_image_mutex;
|
||||
|
||||
atomic_t<bool> m_active = false;
|
||||
atomic_t<bool> m_has_new = false;
|
||||
|
||||
QString m_video_path;
|
||||
QByteArray m_video_data{};
|
||||
QImage m_image{};
|
||||
std::vector<u8> m_image_path;
|
||||
|
||||
std::unique_ptr<QBuffer> m_video_buffer;
|
||||
std::unique_ptr<QMediaPlayer> m_media_player;
|
||||
std::shared_ptr<QVideoSink> m_video_sink;
|
||||
std::shared_ptr<QMovie> m_movie;
|
||||
|
||||
std::function<void(const QVideoFrame&)> m_image_change_callback = nullptr;
|
||||
|
||||
friend class qt_video_source_wrapper;
|
||||
};
|
||||
|
||||
// Wrapper for emulator usage
|
||||
class qt_video_source_wrapper : public video_source
|
||||
{
|
||||
public:
|
||||
qt_video_source_wrapper() : video_source() {}
|
||||
virtual ~qt_video_source_wrapper();
|
||||
|
||||
void set_video_path(const std::string& video_path) override;
|
||||
void set_active(bool active) override;
|
||||
bool get_active() const override;
|
||||
bool has_new() const override { return m_qt_video_source && m_qt_video_source->has_new(); }
|
||||
void get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<qt_video_source> m_qt_video_source;
|
||||
};
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
LOG_CHANNEL(cellSaveData);
|
||||
|
||||
s32 save_data_dialog::ShowSaveDataList(std::vector<SaveDataEntry>& save_entries, s32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay)
|
||||
s32 save_data_dialog::ShowSaveDataList(const std::string& base_dir, std::vector<SaveDataEntry>& save_entries, s32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay)
|
||||
{
|
||||
cellSaveData.notice("ShowSaveDataList(save_entries=%d, focused=%d, op=0x%x, listSet=*0x%x, enable_overlay=%d)", save_entries.size(), focused, op, listSet, enable_overlay);
|
||||
|
||||
|
@ -29,7 +29,7 @@ s32 save_data_dialog::ShowSaveDataList(std::vector<SaveDataEntry>& save_entries,
|
|||
{
|
||||
cellSaveData.notice("ShowSaveDataList: Showing native UI dialog");
|
||||
|
||||
const s32 result = manager->create<rsx::overlays::save_dialog>()->show(save_entries, focused, op, listSet, enable_overlay);
|
||||
const s32 result = manager->create<rsx::overlays::save_dialog>()->show(base_dir, save_entries, focused, op, listSet, enable_overlay);
|
||||
if (result != rsx::overlays::user_interface::selection_code::error)
|
||||
{
|
||||
cellSaveData.notice("ShowSaveDataList: Native UI dialog returned with selection %d", result);
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
class save_data_dialog : public SaveDialogBase
|
||||
{
|
||||
public:
|
||||
s32 ShowSaveDataList(std::vector<SaveDataEntry>& save_entries, s32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay) override;
|
||||
s32 ShowSaveDataList(const std::string& base_dir, std::vector<SaveDataEntry>& save_entries, s32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet, bool enable_overlay) override;
|
||||
};
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
#include "custom_table_widget_item.h"
|
||||
#include "qt_utils.h"
|
||||
#include "qt_video_source.h"
|
||||
#include "gui_application.h"
|
||||
#include "gui_settings.h"
|
||||
#include "persistent_settings.h"
|
||||
#include "game_list_delegate.h"
|
||||
#include "progress_dialog.h"
|
||||
#include "video_label.h"
|
||||
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
|
@ -55,6 +57,7 @@ enum SaveColumns
|
|||
enum SaveUserRole
|
||||
{
|
||||
Pixmap = Qt::UserRole,
|
||||
PixmapScaled,
|
||||
PixmapLoaded
|
||||
};
|
||||
|
||||
|
@ -77,7 +80,7 @@ save_manager_dialog::save_manager_dialog(std::shared_ptr<gui_settings> gui_setti
|
|||
void save_manager_dialog::Init()
|
||||
{
|
||||
// Table
|
||||
m_list = new QTableWidget(this);
|
||||
m_list = new game_list();
|
||||
m_list->setItemDelegate(new game_list_delegate(m_list));
|
||||
m_list->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
|
||||
m_list->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
|
@ -90,6 +93,7 @@ void save_manager_dialog::Init()
|
|||
m_list->setHorizontalHeaderLabels(QStringList() << tr("Icon") << tr("Title & Subtitle") << tr("Last Modified") << tr("Save ID") << tr("Notes"));
|
||||
m_list->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
|
||||
m_list->horizontalHeader()->setStretchLastSection(true);
|
||||
m_list->setMouseTracking(true);
|
||||
|
||||
// Bottom bar
|
||||
const int icon_size = m_gui_settings->GetValue(gui::sd_icon_size).toInt();
|
||||
|
@ -105,7 +109,7 @@ void save_manager_dialog::Init()
|
|||
push_close->setAutoDefault(true);
|
||||
|
||||
// Details
|
||||
m_details_icon = new QLabel(this);
|
||||
m_details_icon = new video_label(this);
|
||||
m_details_icon->setMinimumSize(320, 176);
|
||||
m_details_title = new QLabel(tr("Select an item to view details"), this);
|
||||
m_details_title->setWordWrap(true);
|
||||
|
@ -203,9 +207,10 @@ void save_manager_dialog::Init()
|
|||
connect(m_list, &QTableWidget::itemSelectionChanged, this, &save_manager_dialog::UpdateDetails);
|
||||
connect(this, &save_manager_dialog::IconReady, this, [this](int index, const QPixmap& pixmap)
|
||||
{
|
||||
if (QTableWidgetItem* icon_item = m_list->item(index, SaveColumns::Icon))
|
||||
if (movie_item* item = static_cast<movie_item*>(m_list->item(index, SaveColumns::Icon)))
|
||||
{
|
||||
icon_item->setData(Qt::DecorationRole, pixmap);
|
||||
item->setData(SaveUserRole::PixmapScaled, pixmap);
|
||||
item->image_change_callback();
|
||||
}
|
||||
});
|
||||
connect(search_bar, &QLineEdit::textChanged, this, &save_manager_dialog::text_changed);
|
||||
|
@ -265,13 +270,12 @@ std::vector<SaveDataEntry> save_manager_dialog::GetSaveEntries(const std::string
|
|||
return;
|
||||
}
|
||||
|
||||
SaveDataEntry save_entry2;
|
||||
SaveDataEntry save_entry2 {};
|
||||
save_entry2.dirName = psf::get_string(psf, "SAVEDATA_DIRECTORY");
|
||||
save_entry2.listParam = psf::get_string(psf, "SAVEDATA_LIST_PARAM");
|
||||
save_entry2.title = psf::get_string(psf, "TITLE");
|
||||
save_entry2.subtitle = psf::get_string(psf, "SUB_TITLE");
|
||||
save_entry2.details = psf::get_string(psf, "DETAIL");
|
||||
save_entry2.size = 0;
|
||||
|
||||
for (const auto& entry2 : fs::dir(base_dir + entry.name))
|
||||
{
|
||||
|
@ -321,34 +325,66 @@ void save_manager_dialog::UpdateList()
|
|||
QPixmap placeholder(320, 176);
|
||||
placeholder.fill(Qt::transparent);
|
||||
|
||||
const s32 language_index = gui_application::get_language_id();
|
||||
const std::string localized_movie = fmt::format("ICON1_%02d.PAM", language_index);
|
||||
|
||||
for (int i = 0; i < static_cast<int>(m_save_entries.size()); ++i)
|
||||
{
|
||||
const SaveDataEntry& entry = ::at32(m_save_entries, i);
|
||||
|
||||
const QString title = QString::fromStdString(entry.title) + QStringLiteral("\n") + QString::fromStdString(entry.subtitle);
|
||||
const QString dir_name = QString::fromStdString(entry.dirName);
|
||||
const std::string dir_path = m_dir + entry.dirName + "/";
|
||||
|
||||
custom_table_widget_item* iconItem = new custom_table_widget_item;
|
||||
iconItem->setData(Qt::DecorationRole, placeholder);
|
||||
iconItem->setData(SaveUserRole::Pixmap, placeholder);
|
||||
iconItem->setData(SaveUserRole::PixmapLoaded, false);
|
||||
iconItem->setFlags(iconItem->flags() & ~Qt::ItemIsEditable);
|
||||
m_list->setItem(i, SaveColumns::Icon, iconItem);
|
||||
custom_table_widget_item* icon_item = new custom_table_widget_item;
|
||||
icon_item->setData(Qt::DecorationRole, placeholder);
|
||||
icon_item->setData(SaveUserRole::Pixmap, placeholder);
|
||||
icon_item->setData(SaveUserRole::PixmapScaled, placeholder);
|
||||
icon_item->setData(SaveUserRole::PixmapLoaded, false);
|
||||
icon_item->setFlags(icon_item->flags() & ~Qt::ItemIsEditable);
|
||||
|
||||
QTableWidgetItem* titleItem = new QTableWidgetItem(title);
|
||||
if (const std::string movie_path = dir_path + localized_movie; fs::is_file(movie_path))
|
||||
{
|
||||
icon_item->set_video_path(movie_path);
|
||||
}
|
||||
else if (const std::string movie_path = dir_path + "ICON1.PAM"; fs::is_file(movie_path))
|
||||
{
|
||||
icon_item->set_video_path(movie_path);
|
||||
}
|
||||
|
||||
icon_item->set_image_change_callback([this, icon_item](const QVideoFrame& frame)
|
||||
{
|
||||
if (!icon_item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (const QPixmap pixmap = icon_item->get_movie_image(frame); icon_item->get_active() && !pixmap.isNull())
|
||||
{
|
||||
icon_item->setData(Qt::DecorationRole, pixmap.scaled(m_icon_size, Qt::KeepAspectRatio));
|
||||
}
|
||||
else
|
||||
{
|
||||
icon_item->setData(Qt::DecorationRole, icon_item->data(SaveUserRole::PixmapScaled).value<QPixmap>());
|
||||
icon_item->stop_movie();
|
||||
}
|
||||
});
|
||||
m_list->setItem(i, SaveColumns::Icon, icon_item);
|
||||
|
||||
custom_table_widget_item* titleItem = new custom_table_widget_item(title);
|
||||
titleItem->setData(Qt::UserRole, i); // For sorting to work properly
|
||||
titleItem->setFlags(titleItem->flags() & ~Qt::ItemIsEditable);
|
||||
m_list->setItem(i, SaveColumns::Name, titleItem);
|
||||
|
||||
QTableWidgetItem* timeItem = new QTableWidgetItem(FormatTimestamp(entry.mtime));
|
||||
custom_table_widget_item* timeItem = new custom_table_widget_item(FormatTimestamp(entry.mtime));
|
||||
timeItem->setFlags(timeItem->flags() & ~Qt::ItemIsEditable);
|
||||
m_list->setItem(i, SaveColumns::Time, timeItem);
|
||||
|
||||
QTableWidgetItem* dirNameItem = new QTableWidgetItem(dir_name);
|
||||
custom_table_widget_item* dirNameItem = new custom_table_widget_item(dir_name);
|
||||
dirNameItem->setFlags(dirNameItem->flags() & ~Qt::ItemIsEditable);
|
||||
m_list->setItem(i, SaveColumns::Dir, dirNameItem);
|
||||
|
||||
QTableWidgetItem* noteItem = new QTableWidgetItem();
|
||||
custom_table_widget_item* noteItem = new custom_table_widget_item();
|
||||
noteItem->setFlags(noteItem->flags() | Qt::ItemIsEditable);
|
||||
if (notes.contains(dir_name))
|
||||
{
|
||||
|
@ -399,6 +435,7 @@ void save_manager_dialog::UpdateIcons()
|
|||
{
|
||||
if (movie_item* icon_item = static_cast<movie_item*>(m_list->item(i, SaveColumns::Icon)))
|
||||
{
|
||||
icon_item->setData(SaveUserRole::PixmapScaled, placeholder);
|
||||
icon_item->setData(Qt::DecorationRole, placeholder);
|
||||
}
|
||||
}
|
||||
|
@ -548,7 +585,7 @@ void save_manager_dialog::OnEntriesRemove()
|
|||
}
|
||||
|
||||
// Pop-up a small context-menu, being a replacement for save_data_manage_dialog
|
||||
void save_manager_dialog::ShowContextMenu(const QPoint &pos)
|
||||
void save_manager_dialog::ShowContextMenu(const QPoint& pos)
|
||||
{
|
||||
const int idx = m_list->currentRow();
|
||||
if (idx == -1)
|
||||
|
@ -604,7 +641,9 @@ void save_manager_dialog::UpdateDetails()
|
|||
{
|
||||
if (const int selected = m_list->selectionModel()->selectedRows().size(); selected != 1)
|
||||
{
|
||||
m_details_icon->setPixmap(QPixmap());
|
||||
m_details_icon->set_thumbnail({});
|
||||
m_details_icon->set_active(false);
|
||||
|
||||
m_details_subtitle->setText("");
|
||||
m_details_modified->setText("");
|
||||
m_details_details->setText("");
|
||||
|
@ -628,7 +667,7 @@ void save_manager_dialog::UpdateDetails()
|
|||
|
||||
const int row = m_list->currentRow();
|
||||
QTableWidgetItem* item = m_list->item(row, SaveColumns::Name);
|
||||
QTableWidgetItem* icon_item = m_list->item(row, SaveColumns::Icon);
|
||||
movie_item* icon_item = static_cast<movie_item*>(m_list->item(row, SaveColumns::Icon));
|
||||
|
||||
if (!item || !icon_item)
|
||||
{
|
||||
|
@ -638,7 +677,10 @@ void save_manager_dialog::UpdateDetails()
|
|||
const int idx = item->data(Qt::UserRole).toInt();
|
||||
const SaveDataEntry& save = ::at32(m_save_entries, idx);
|
||||
|
||||
m_details_icon->setPixmap(icon_item->data(Qt::UserRole).value<QPixmap>());
|
||||
m_details_icon->set_video_path(icon_item->video_path().toStdString());
|
||||
m_details_icon->set_thumbnail(icon_item->data(SaveUserRole::Pixmap).value<QPixmap>());
|
||||
m_details_icon->set_active(false);
|
||||
|
||||
m_details_title->setText(QString::fromStdString(save.title));
|
||||
m_details_subtitle->setText(QString::fromStdString(save.subtitle));
|
||||
m_details_modified->setText(tr("Last modified: %1").arg(FormatTimestamp(save.mtime)));
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "game_list.h"
|
||||
|
||||
#include "Emu/Cell/Modules/cellSaveData.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
@ -10,6 +12,7 @@
|
|||
|
||||
class gui_settings;
|
||||
class persistent_settings;
|
||||
class video_label;
|
||||
|
||||
class save_manager_dialog : public QDialog
|
||||
{
|
||||
|
@ -39,14 +42,14 @@ private:
|
|||
void Init();
|
||||
void UpdateList();
|
||||
void UpdateIcons();
|
||||
void ShowContextMenu(const QPoint &pos);
|
||||
void ShowContextMenu(const QPoint& pos);
|
||||
void WaitForRepaintThreads(bool abort);
|
||||
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
std::vector<SaveDataEntry> GetSaveEntries(const std::string& base_dir);
|
||||
|
||||
QTableWidget* m_list = nullptr;
|
||||
game_list* m_list = nullptr;
|
||||
std::string m_dir;
|
||||
std::vector<SaveDataEntry> m_save_entries;
|
||||
|
||||
|
@ -58,7 +61,7 @@ private:
|
|||
QSize m_icon_size;
|
||||
QColor m_icon_color;
|
||||
|
||||
QLabel* m_details_icon = nullptr;
|
||||
video_label* m_details_icon = nullptr;
|
||||
QLabel* m_details_title = nullptr;
|
||||
QLabel* m_details_subtitle = nullptr;
|
||||
QLabel* m_details_modified = nullptr;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "game_list_base.h"
|
||||
#include "gui_game_info.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
|
|
@ -76,9 +76,9 @@ shortcut_settings::shortcut_settings()
|
|||
{ shortcut::gw_frame_limit, shortcut_info{ "game_window_frame_limit", tr("Toggle Framelimit"), "Ctrl+F10", shortcut_handler_id::game_window, false } },
|
||||
{ shortcut::gw_toggle_mouse_and_keyboard, shortcut_info{ "game_window_toggle_mouse_and_keyboard", tr("Toggle Keyboard"), "Ctrl+F11", shortcut_handler_id::game_window, false } },
|
||||
{ shortcut::gw_home_menu, shortcut_info{ "gw_home_menu", tr("Open Home Menu"), "Shift+F10", shortcut_handler_id::game_window, false } },
|
||||
{ shortcut::gw_mute_unmute, shortcut_info{ "gw_mute_unmute", tr("Mute/Unmute Audio"), "Shift+M", shortcut_handler_id::game_window, false } },
|
||||
{ shortcut::gw_volume_up, shortcut_info{ "gw_volume_up", tr("Volume Up"), "Shift++", shortcut_handler_id::game_window, true } },
|
||||
{ shortcut::gw_volume_down, shortcut_info{ "gw_volume_down", tr("Volume Down"), "Shift+-", shortcut_handler_id::game_window, true } },
|
||||
{ shortcut::gw_mute_unmute, shortcut_info{ "gw_mute_unmute", tr("Mute/Unmute Audio"), "Ctrl+Shift+M", shortcut_handler_id::game_window, false } },
|
||||
{ shortcut::gw_volume_up, shortcut_info{ "gw_volume_up", tr("Volume Up"), "Ctrl+Shift++", shortcut_handler_id::game_window, true } },
|
||||
{ shortcut::gw_volume_down, shortcut_info{ "gw_volume_down", tr("Volume Down"), "Ctrl+Shift+-", shortcut_handler_id::game_window, true } },
|
||||
})
|
||||
{
|
||||
}
|
||||
|
|
|
@ -499,14 +499,14 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||
UInt16 temp_u16[PATH_MAX];
|
||||
u8 temp_u8[PATH_MAX];
|
||||
const usz kInputBufSize = static_cast<usz>(1u << 18u);
|
||||
const ISzAlloc g_Alloc = {SzAlloc, SzFree};
|
||||
const ISzAlloc g_Alloc = {SzAlloc, SzFree};
|
||||
|
||||
ISzAlloc allocImp = g_Alloc;
|
||||
ISzAlloc allocTempImp = g_Alloc;
|
||||
|
||||
if (InFile_Open(&archiveStream.file, tmpfile_path.c_str()))
|
||||
if (const WRes res = InFile_Open(&archiveStream.file, tmpfile_path.c_str()))
|
||||
{
|
||||
update_log.error("Failed to open temporary storage file: %s", tmpfile_path);
|
||||
update_log.error("Failed to open temporary storage file: '%s' (error=%d)", tmpfile_path, static_cast<u64>(res));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -534,7 +534,8 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
|||
SzArEx_Free(&db, &allocImp);
|
||||
ISzAlloc_Free(&allocImp, lookStream.buf);
|
||||
|
||||
File_Close(&archiveStream.file);
|
||||
const WRes res2 = File_Close(&archiveStream.file);
|
||||
if (res2) update_log.warning("7z failed to close file (error=%d)", static_cast<u64>(res2));
|
||||
|
||||
switch (res)
|
||||
{
|
||||
|
|
51
rpcs3/rpcs3qt/video_label.cpp
Normal file
51
rpcs3/rpcs3qt/video_label.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "stdafx.h"
|
||||
#include "video_label.h"
|
||||
|
||||
video_label::video_label(QWidget* parent)
|
||||
: QLabel(parent), qt_video_source()
|
||||
{
|
||||
}
|
||||
|
||||
video_label::~video_label()
|
||||
{
|
||||
}
|
||||
|
||||
void video_label::set_thumbnail(const QPixmap& pxmap)
|
||||
{
|
||||
m_current_pixmap = pxmap;
|
||||
}
|
||||
|
||||
void video_label::set_active(bool active)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
set_image_change_callback([this](const QVideoFrame& frame)
|
||||
{
|
||||
if (const QPixmap pixmap = get_movie_image(frame); get_active() && !pixmap.isNull())
|
||||
{
|
||||
setPixmap(pixmap);
|
||||
}
|
||||
});
|
||||
start_movie();
|
||||
}
|
||||
else
|
||||
{
|
||||
set_image_change_callback({});
|
||||
stop_movie();
|
||||
setPixmap(m_current_pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
void video_label::enterEvent(QEnterEvent* event)
|
||||
{
|
||||
set_active(true);
|
||||
|
||||
QLabel::enterEvent(event);
|
||||
}
|
||||
|
||||
void video_label::leaveEvent(QEvent* event)
|
||||
{
|
||||
set_active(false);
|
||||
|
||||
QLabel::leaveEvent(event);
|
||||
}
|
22
rpcs3/rpcs3qt/video_label.h
Normal file
22
rpcs3/rpcs3qt/video_label.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "qt_video_source.h"
|
||||
#include <QLabel>
|
||||
#include <QEnterEvent>
|
||||
#include <QPixmap>
|
||||
|
||||
class video_label : public QLabel, public qt_video_source
|
||||
{
|
||||
public:
|
||||
video_label(QWidget* parent = nullptr);
|
||||
virtual ~video_label();
|
||||
|
||||
void set_thumbnail(const QPixmap& pxmap);
|
||||
void set_active(bool active) override;
|
||||
|
||||
void enterEvent(QEnterEvent* event) override;
|
||||
void leaveEvent(QEvent* event) override;
|
||||
|
||||
private:
|
||||
QPixmap m_current_pixmap;
|
||||
};
|
|
@ -1192,7 +1192,7 @@ constexpr void write_to_ptr(U&& array, usz pos, const T& value)
|
|||
{
|
||||
static_assert(sizeof(T) % sizeof(array[0]) == 0);
|
||||
if (!std::is_constant_evaluated())
|
||||
std::memcpy(&array[pos], &value, sizeof(value));
|
||||
std::memcpy(static_cast<void*>(&array[pos]), &value, sizeof(value));
|
||||
else
|
||||
ensure(!"Unimplemented");
|
||||
}
|
||||
|
|
33
rpcs3/util/video_source.h
Normal file
33
rpcs3/util/video_source.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.hpp"
|
||||
#include <functional>
|
||||
|
||||
class video_source
|
||||
{
|
||||
public:
|
||||
video_source() {};
|
||||
virtual ~video_source() {};
|
||||
virtual void set_video_path(const std::string& video_path) = 0;
|
||||
virtual void set_active(bool active) = 0;
|
||||
virtual bool get_active() const = 0;
|
||||
virtual bool has_new() const = 0;
|
||||
virtual void get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp) = 0;
|
||||
|
||||
void set_update_callback(std::function<void()> callback)
|
||||
{
|
||||
m_update_callback = callback;
|
||||
}
|
||||
|
||||
protected:
|
||||
void notify_update()
|
||||
{
|
||||
if (m_update_callback)
|
||||
{
|
||||
m_update_callback();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> m_update_callback;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue