Compare commits

...

56 Commits

Author SHA1 Message Date
Nekotekina d7e72797a8 Version 0.0.3 2017-07-20 18:05:34 +03:00
Nekotekina 0dfd2eadc1 Travis: do not zip 2017-07-20 18:02:26 +03:00
Nekotekina 291ec1eeb2 SPU recompiler: minor optimization 2017-07-20 17:22:09 +03:00
Nekotekina b24eb621ae Use RTM instructions (skylake+) 2017-07-20 17:22:09 +03:00
Nekotekina 0fa148e65e Improve CPU feature check
Damn exit on SSSE3 failure
Check AVX for Intel processors
2017-07-20 17:22:09 +03:00
Talkashie 3f6b24d33a Correct typo
No classes or names or any of that poop are being changed here, only a text string.
2017-07-20 15:43:24 +03:00
Danila Malyutin 9fee2ebeb5 Enable optimizations on travis 2017-07-20 01:17:51 +03:00
kd-11 99828a8f15 spu: Clean up asmjit - avoid touching the shared db whenever possible
- Gets around the locking issues when fetching from the shared db
2017-07-19 23:28:33 +03:00
kd-11 2526626646 rsx: Surface cache bug fixes
- Properly handle data 'transfer' when recycling frame buffer images
- Clear 'recycled' surfaces before use
2017-07-19 23:28:33 +03:00
kd-11 582913dc31 spu: Simplify watchdog design (PC is purely HLE and occupies SPU code kernel space only, max 256K) 2017-07-19 23:28:33 +03:00
kd-11 94c1b74a17 fix build; restore asmjit reader_lock for now 2017-07-19 23:28:33 +03:00
kd-11 cbd1b28d0d spu: Add polling loop detection and clean up channel access contention code
- spus run a tight gpu-style kernel with no multitasking on the cores themselves
-- this does not map well to PC processor cores because they never sleep even when doing nothing
-- the poll detection hack tries to find a good place to insert a scheduler yield
-- RdDec is a good spot as it signifies the spu kernel is waiting on a timer
2017-07-19 23:28:33 +03:00
kd-11 05ffb50037 vk/rsx: Bug fixes and improvements
- Improvements to framebuffer usage; Avoid creating new resources every frame
- Handle null fragment program properly
- Collect vertex upload statistics

- vk: Pre-initialize 'unused' varying registers in the vertex shader in case it gets matched with a fs that consumes it
 -- Fixes a crash about fog_c not being declared

gl/dx12/vk: Handle null fragment program

- cleanup - use yield semantic instead of sleep(0) as yield is more cross-platform
 -- sleep(0) is a windows specific scheduler hint
2017-07-19 23:28:33 +03:00
kd-11 72e13ddeb2 spu: Implement concurrent instruction execution watchdog for wrch
- Delays threads by a predetermined amount to 'desync' spurs kernels.
  Largely reduces lock contention issues as well as making spurs kernels
  play nice with reservations
- Also reduces number of lost notifications (SPU_EVENT_LR)
2017-07-19 23:28:33 +03:00
kd-11 78f09d7645 asmjit: Minimal locking when reading, also only lock when actually writing to the db 2017-07-19 23:28:33 +03:00
kd-11 f69121116a rsx/vk: Optimize framebuffer lifetime management
- Significant gains due to avoiding aggressive create-delete cycles every frame
2017-07-19 23:28:33 +03:00
RipleyTom 0136215ef1 Minor sceNpBasicGetEvent Fix 2017-07-19 21:41:37 +03:00
Megamouse ab534ac55d main_window fixerinno
remove sinful toolbar sorcery
2017-07-19 21:16:13 +03:00
Megamouse 67bbd59924 debugger_frame fixerinno
delete zombie dialog
clear breakpoints properly
2017-07-19 21:16:13 +03:00
RipleyTom f0b1a587aa Implements deadzone for xinput 2017-07-19 20:35:12 +03:00
Megamouse ae8f858c56 settings_dialog: get rid of string duplication
use enum to dictate button id
2017-07-19 17:40:27 +03:00
Dangles91 7426eb285f Fixes for save_op_get_list_item
Only return data for dir and bind where requested. Some games like Akiba's Trip call this function but don't ask for any data back.
2017-07-18 15:22:49 +03:00
Nekotekina f91f2e3e6d Patch: add floating point support
Also count applied entries
2017-07-17 18:36:54 +03:00
Nekotekina e39ee10105 Patch system improved
SPU patch rewritten
PPU patch implemented
2017-07-17 16:49:46 +03:00
Nekotekina 2ef2f0f63b PPU LLVM: simplify module loading 2017-07-17 16:49:46 +03:00
Nekotekina e875c91121 Restore timeBeginPeriod 2017-07-17 16:49:46 +03:00
Dangles 6610abcd5f Minor changes for save data utility (#3005) 2017-07-16 19:10:45 +03:00
Megamouse aed9b31294 add system_info.h (#2985)
add system info to log
add SSSE3 Error message to main_window
2017-07-16 17:36:38 +03:00
Zion Nimchuk 6597eb27c7 Fix PIE being on by default 2017-07-16 16:59:46 +03:00
Megamouse 035a39a9a8 GUI: minor figgcess (#3010)
* emulator tab: minor layout adjustment

* tool bar: adjust size dynamically for hidpi

* gamelist tool bar: adjust size dynamically for hidpi

* gamelist search bar: adjust style

* move game_list.h into rpcs3qt

* hide game data by default
2017-07-15 21:22:24 +03:00
Learath2 9c547d5eef Fix bug with std::stof being locale-dependant (#3012) 2017-07-15 03:38:10 +03:00
Learath2 504fa5ffda Add version to install firmware dialog (#2958) 2017-07-14 18:34:05 +03:00
Megamouse ad66dbfd0b Add game window resize on boot (#3000) 2017-07-14 18:10:38 +03:00
Megamouse 0adb579736 rename controls to keyboard 2017-07-14 17:45:47 +03:00
Megamouse d4c83e5dd2 Settings: fix cameraBox 2017-07-14 17:45:47 +03:00
Megamouse 821a8c6e65 settings_dialog: add icon hints to color buttons 2017-07-14 17:20:41 +03:00
Megamouse ef60809219 move colorizedIcon to gui_settings 2017-07-14 17:20:41 +03:00
Megamouse a21ed06d27 Icons (blue): fix inconsistent colors 2017-07-14 17:20:41 +03:00
Megamouse 97f59405aa game_list_frame: repaint tool icons 2017-07-14 17:20:41 +03:00
Megamouse b586bd3b10 gui_settings: add gamelist tool icon color 2017-07-14 17:20:41 +03:00
Megamouse c617b17037 settings_dialog: add color picker for gamelist tool icons 2017-07-14 17:20:41 +03:00
Megamouse 73bbfcb246 main_window: repaint tool icons 2017-07-14 17:20:41 +03:00
Megamouse cfa7d04c49 settings_dialog: add color picker for main window tool icons 2017-07-14 17:20:41 +03:00
Megamouse fb191693d1 gui_settings: add tool icon color 2017-07-14 17:20:41 +03:00
Megamouse 743a19027a game_list_frame: add method colorizedIcons 2017-07-14 17:20:41 +03:00
Robbie ced539579e Murdering a wx zombie 2017-07-14 14:20:07 +03:00
Robbie 225af34ce9 For the Neko 2017-07-14 14:20:07 +03:00
Robbie eb80b7ec34 Small main_window refactoring. Fixed one of the guiconfig crashes too somehow. 2017-07-14 14:20:07 +03:00
Talkashie 3df960bb52 Fix various typos and capitalisation inconsistencies.
also Jarves please implement sysutils
2017-07-14 13:54:13 +03:00
Nekotekina 25fcde9507 Rearrange /dev_bdvd mounting logic
Prevent booting patches without /dev_bdvd
Prevent booting with foreign /dev_bdvd
2017-07-14 03:39:22 +03:00
Nekotekina d8abe75526 Improved sprx boot (for devs only) 2017-07-14 01:47:00 +03:00
Nekotekina 15fe8f1c51 Fix cmdline start 2017-07-14 01:47:00 +03:00
scribam ce7b55871b Add sceNp2* functions 2017-07-14 01:46:07 +03:00
scribam 9d8fa28dd9 Add sceNp* functions 2017-07-14 01:46:07 +03:00
scribam 3a3e9770f3 Add cellHttp* functions 2017-07-14 01:46:07 +03:00
scribam 2d037fa130 Remove "#pragma once" directive in a cpp file 2017-07-14 01:46:07 +03:00
86 changed files with 1995 additions and 504 deletions
+1 -2
View File
@@ -41,7 +41,6 @@
/Vulkan/Vulkan-build
/Vulkan/glslang-build
/wxWidgets/lib
/bin/rpcs3.ini
/bin/rpcs3.ipdb
/bin/rpcs3.iobj
@@ -92,4 +91,4 @@ rpcs3/rpcs3_*_cotire.cmake
moc_*.cpp
qrc_resources.cpp
rpcs3_automoc.cpp
ui_*.h
ui_*.h
+1 -2
View File
@@ -87,11 +87,10 @@ before_script:
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs ;
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -appimage ;
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq ;
zip -r9 rpcs3.zip ./RPCS3*.AppImage;
if [ -z "$WITHOUT_LLVM" ]; then
export LLVM="-LLVM";
fi;
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}${LLVM}_linux64 --upload-file rpcs3.zip;
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}${LLVM}_linux64 --upload-file ./RPCS3*.AppImage;
fi;
script:
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 3.0.2)
option(WITH_GDB "WITH_GDB" OFF)
option(WITHOUT_LLVM "WITHOUT_LLVM" OFF)
+37 -9
View File
@@ -12,6 +12,7 @@
#include "File.h"
#include "Log.h"
#include "mutex.h"
#include "sysinfo.h"
#include "VirtualMemory.h"
#ifdef _MSC_VER
@@ -343,22 +344,30 @@ public:
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
}
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
{
std::string name = m_path;
name.append(module->getName());
if (fs::file cached{name, fs::read})
if (fs::file cached{path, fs::read})
{
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
return buf;
}
return nullptr;
}
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
{
std::string path = m_path;
path.append(module->getName());
if (auto buf = load(path))
{
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
return buf;
}
else
{
return nullptr;
}
return nullptr;
}
};
@@ -369,6 +378,20 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, std::uintptr_t>
if (m_cpu.empty())
{
m_cpu = llvm::sys::getHostCPUName();
if (m_cpu == "sandybridge" ||
m_cpu == "ivybridge" ||
m_cpu == "haswell" ||
m_cpu == "broadwell" ||
m_cpu == "skylake" ||
m_cpu == "skylake-avx512" ||
m_cpu == "cannonlake")
{
if (!utils::has_avx())
{
m_cpu = "nehalem";
}
}
}
std::string result;
@@ -430,6 +453,11 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string&
}
}
void jit_compiler::add(const std::string& path)
{
m_engine->addObjectFile(std::move(llvm::object::ObjectFile::createObjectFile(*ObjectCache::load(path)).get()));
}
void jit_compiler::fin()
{
m_engine->finalizeObject();
+4 -1
View File
@@ -49,9 +49,12 @@ public:
return m_context;
}
// Add module
// Add module (path to obj cache dir)
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
// Add object (path to obj file)
void add(const std::string& path);
// Finalize
void fin();
+35 -4
View File
@@ -14,9 +14,13 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
case patch_type::le16: return "le16";
case patch_type::le32: return "le32";
case patch_type::le64: return "le64";
case patch_type::bef32: return "bef32";
case patch_type::bef64: return "bef64";
case patch_type::be16: return "be16";
case patch_type::be32: return "be32";
case patch_type::be64: return "be64";
case patch_type::lef32: return "lef32";
case patch_type::lef64: return "lef64";
}
return unknown;
@@ -39,23 +43,44 @@ void patch_engine::append(const std::string& patch)
u64 type64 = 0;
cfg::try_to_enum_value(&type64, &fmt_class_string<patch_type>::format, patch[0].Scalar());
struct patch info;
struct patch info{};
info.type = static_cast<patch_type>(type64);
info.offset = patch[1].as<u32>();
info.value = patch[2].as<u64>();
switch (info.type)
{
case patch_type::bef32:
case patch_type::lef32:
{
info.value_as<f32>() = patch[2].as<f32>();
break;
}
case patch_type::bef64:
case patch_type::lef64:
{
info.value_as<f64>() = patch[2].as<f64>();
break;
}
default:
{
info.value = patch[2].as<u64>();
break;
}
}
data.emplace_back(info);
}
}
}
}
void patch_engine::apply(const std::string& name, u8* dst) const
std::size_t patch_engine::apply(const std::string& name, u8* dst) const
{
const auto found = m_map.find(name);
if (found == m_map.cend())
{
return;
return 0;
}
// Apply modifications sequentially
@@ -76,11 +101,13 @@ void patch_engine::apply(const std::string& name, u8* dst) const
break;
}
case patch_type::le32:
case patch_type::lef32:
{
*reinterpret_cast<le_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
break;
}
case patch_type::le64:
case patch_type::lef64:
{
*reinterpret_cast<le_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
break;
@@ -91,15 +118,19 @@ void patch_engine::apply(const std::string& name, u8* dst) const
break;
}
case patch_type::be32:
case patch_type::bef32:
{
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
break;
}
case patch_type::be64:
case patch_type::bef64:
{
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
break;
}
}
}
return found->second.size();
}
+12 -2
View File
@@ -11,9 +11,13 @@ enum class patch_type
le16,
le32,
le64,
lef32,
lef64,
be16,
be32,
be64,
bef32,
bef64,
};
class patch_engine
@@ -23,6 +27,12 @@ class patch_engine
patch_type type;
u32 offset;
u64 value;
template <typename T>
T& value_as()
{
return *reinterpret_cast<T*>(reinterpret_cast<char*>(&value));
}
};
// Database
@@ -32,6 +42,6 @@ public:
// Load from file
void append(const std::string& path);
// Apply patch
void apply(const std::string& name, u8* dst) const;
// Apply patch (returns the number of entries applied)
std::size_t apply(const std::string& name, u8* dst) const;
};
+61
View File
@@ -0,0 +1,61 @@
#include "sysinfo.h"
#include "StrFmt.h"
#ifdef _WIN32
#include "windows.h"
#else
#include <unistd.h>
#endif
std::string utils::get_system_info()
{
std::string result;
std::string brand;
if (get_cpuid(0x80000000, 0)[0] >= 0x80000004)
{
for (u32 i = 0; i < 3; i++)
{
brand.append(reinterpret_cast<const char*>(get_cpuid(0x80000002 + i, 0).data()), 16);
}
}
else
{
brand = "Unknown CPU";
}
brand.erase(0, brand.find_first_not_of(' '));
brand.erase(brand.find_last_not_of(' ') + 1);
while (auto found = brand.find(" ") + 1)
{
brand.erase(brand.begin() + found);
}
#ifdef _WIN32
::SYSTEM_INFO sysInfo;
::GetNativeSystemInfo(&sysInfo);
::MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(memInfo);
::GlobalMemoryStatusEx(&memInfo);
const u32 num_proc = sysInfo.dwNumberOfProcessors;
const u64 mem_total = memInfo.ullTotalPhys;
#else
const u32 num_proc = ::sysconf(_SC_NPROCESSORS_ONLN);
const u64 mem_total = ::sysconf(_SC_PHYS_PAGES) * ::sysconf(_SC_PAGE_SIZE);
#endif
fmt::append(result, "%s | %d Threads | %.2f GiB RAM", brand, num_proc, mem_total / (1024.0f * 1024 * 1024));
if (has_avx())
{
result += " | AVX";
}
if (has_rtm())
{
result += " | TSX";
}
return result;
}
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include "types.h"
#include <string>
namespace utils
{
inline std::array<u32, 4> get_cpuid(u32 func, u32 subfunc)
{
int regs[4];
#ifdef _MSC_VER
__cpuidex(regs, func, subfunc);
#else
__asm__ volatile("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) : "a" (func), "c" (subfunc));
#endif
return {0u+regs[0], 0u+regs[1], 0u+regs[2], 0u+regs[3]};
}
inline bool has_ssse3()
{
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x200;
}
inline bool has_avx()
{
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000;
}
inline bool has_rtm()
{
// Check RTM and MPX extensions in order to filter out TSX on Haswell CPUs
return get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4800) == 0x4800;
}
inline bool transaction_enter()
{
while (true)
{
const auto status = _xbegin();
if (status == _XBEGIN_STARTED)
{
return true;
}
if (!(status & _XABORT_RETRY))
{
return false;
}
}
}
std::string get_system_info();
}
+2 -2
View File
@@ -1,4 +1,4 @@
version: '0.0.2-{build}'
version: '0.0.3-{build}'
os: Visual Studio 2015
platform: x64
@@ -32,7 +32,7 @@ install:
artifacts:
- path: bin
name: 'rpcs3-v0.0.2-$(Date)-$(COMMIT_SHA)_win64'
name: 'rpcs3-v0.0.3-$(Date)-$(COMMIT_SHA)_win64'
type: zip
cache:
+15 -8
View File
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.0.2)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake_modules")
set(RES_FILES "")
set(CMAKE_CXX_STANDARD 14)
include(CheckCCompilerFlag)
# Qt section
find_package(Qt5 5.7 COMPONENTS Widgets)
@@ -84,9 +85,6 @@ else()
endif()
if(NOT MSVC)
if($ENV{CI})
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1") # fix for travis gcc OoM crash. Might be fixed with the move to containers.
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
if(WIN32)
set(CMAKE_RC_COMPILER_INIT windres)
@@ -97,13 +95,24 @@ if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--allow-multiple-definition")
endif()
add_compile_options(-msse -msse2 -mcx16 -mssse3)
add_compile_options(-msse -msse2 -mcx16 -mssse3 -mrtm)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# This fixes 'some' of the st11range issues. See issue #2516
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -image-base=0x10000")
endif()
# Some distros have the compilers set to use PIE by default, but RPCS3 doesn't work with PIE, so we need to disable it.
CHECK_C_COMPILER_FLAG("-no-pie" HAS_NO_PIE)
if(HAS_NO_PIE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie")
else()
CHECK_C_COMPILER_FLAG("-nopie" HAS_NO_PIE)
if(HAS_NO_PIE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nopie")
endif()
endif()
find_package(GLEW REQUIRED)
find_package(ZLIB REQUIRED)
else()
@@ -268,12 +277,10 @@ if(NOT WIN32 AND NOT "${CMAKE_SYSTEM}" MATCHES "Linux")
set (EXCLUDE_FILES "/RSX/VK/")
endif()
# The Gui folder contains wxWidgets stuff, which we no longer want.
set (EXCLUDE_FILES ${EXCLUDE_FILES} "/Gui/")
# Ignore autogenerated moc_* files if present
set (EXCLUDE_FILES ${EXCLUDE_FILES} "moc_")
set (EXCLUDE_FILES ${EXCLUDE_FILES} "rpcs3_automoc")
set (EXCLUDE_FILES ${EXCLUDE_FILES} "qrc_resources.cpp")
foreach (TMP_PATH ${RPCS3_SRC})
foreach (EXCLUDE_PATH ${EXCLUDE_FILES})
@@ -285,7 +292,7 @@ foreach (TMP_PATH ${RPCS3_SRC})
endforeach(TMP_PATH)
# Remove the Qt moc files as part of clean, they are compiled when generating automoc
file(GLOB_RECURSE TMP_MOC "${RPCS3_SRC_DIR}/moc_*.cpp" "${RPCS3_SRC_DIR}/rpcs3_automoc.cpp")
file(GLOB_RECURSE TMP_MOC "${RPCS3_SRC_DIR}/moc_*.cpp" "${RPCS3_SRC_DIR}/rpcs3_automoc.cpp" "${RPCS3_SRC_DIR}/qrc_resources.cpp")
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${TMP_MOC}")
if (WIN32)
+22 -4
View File
@@ -1,9 +1,12 @@
#include "stdafx.h"
#include "Utilities/sysinfo.h"
#include "Emu/Memory/vm.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/lv2/sys_sync.h"
#include "MFC.h"
const bool s_use_rtm = utils::has_rtm();
template <>
void fmt_class_string<MFC>::format(std::string& out, u64 arg)
{
@@ -145,10 +148,25 @@ void mfc_thread::cpu_task()
vm::reservation_acquire(cmd.eal, 128);
// Store unconditionally
vm::writer_lock lock(0);
data = to_write;
vm::reservation_update(cmd.eal, 128);
vm::notify(cmd.eal, 128);
if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
data = to_write;
vm::reservation_update(cmd.eal, 128);
vm::notify(cmd.eal, 128);
_xend();
}
else
{
vm::writer_lock lock(0);
data = to_write;
vm::reservation_update(cmd.eal, 128);
vm::notify(cmd.eal, 128);
}
}
else if (cmd.cmd & MFC_LIST_MASK)
{
+35
View File
@@ -317,6 +317,24 @@ s32 cellHttpClientSetConnectionWaitStatus()
return CELL_OK;
}
s32 cellHttpClientGetConnectionWaitStatus()
{
UNIMPLEMENTED_FUNC(cellHttp);
return CELL_OK;
}
s32 cellHttpClientSetConnectionWaitTimeout()
{
UNIMPLEMENTED_FUNC(cellHttp);
return CELL_OK;
}
s32 cellHttpClientGetConnectionWaitTimeout()
{
UNIMPLEMENTED_FUNC(cellHttp);
return CELL_OK;
}
s32 cellHttpClientSetRecvTimeout()
{
UNIMPLEMENTED_FUNC(cellHttp);
@@ -641,6 +659,18 @@ s32 cellHttpTransactionGetSslId()
return CELL_OK;
}
s32 cellHttpClientSetMinSslVersion()
{
UNIMPLEMENTED_FUNC(cellHttp);
return CELL_OK;
}
s32 cellHttpClientGetMinSslVersion()
{
UNIMPLEMENTED_FUNC(cellHttp);
return CELL_OK;
}
s32 cellHttpClientSetSslVersion()
{
UNIMPLEMENTED_FUNC(cellHttp);
@@ -719,7 +749,10 @@ DECLARE(ppu_module_manager::cellHttp)("cellHttp", []()
REG_FUNC(cellHttp, cellHttpClientPollConnections);
REG_FUNC(cellHttp, cellHttpClientSetConnectionStateCallback);
REG_FUNC(cellHttp, cellHttpClientGetConnectionWaitStatus);
REG_FUNC(cellHttp, cellHttpClientSetConnectionWaitStatus);
REG_FUNC(cellHttp, cellHttpClientGetConnectionWaitTimeout);
REG_FUNC(cellHttp, cellHttpClientSetConnectionWaitTimeout);
REG_FUNC(cellHttp, cellHttpClientSetRecvTimeout);
REG_FUNC(cellHttp, cellHttpClientGetRecvTimeout);
REG_FUNC(cellHttp, cellHttpClientSetSendTimeout);
@@ -781,6 +814,8 @@ DECLARE(ppu_module_manager::cellHttp)("cellHttp", []()
REG_FUNC(cellHttp, cellHttpTransactionGetSslVersion);
REG_FUNC(cellHttp, cellHttpTransactionGetSslId);
REG_FUNC(cellHttp, cellHttpClientSetMinSslVersion);
REG_FUNC(cellHttp, cellHttpClientGetMinSslVersion);
REG_FUNC(cellHttp, cellHttpClientSetSslVersion);
REG_FUNC(cellHttp, cellHttpClientGetSslVersion);
REG_FUNC(cellHttp, cellHttpClientSetSslIdDestroyCallback);
+111 -44
View File
@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/Modules/cellSysutil.h"
#include "cellSaveData.h"
@@ -63,7 +64,6 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
PFuncFile funcFile, u32 container, u32 unknown, vm::ptr<void> userdata, u32 userId, PFuncDone funcDone)
{
// TODO: check arguments
std::unique_lock<std::mutex> lock(g_savedata_mutex, std::try_to_lock);
if (!lock)
@@ -82,6 +82,8 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
vm::ptr<CellSaveDataFileGet> fileGet = g_savedata_context.ptr(&savedata_context::fileGet);
vm::ptr<CellSaveDataFileSet> fileSet = g_savedata_context.ptr(&savedata_context::fileSet);
//TODO: get current user ID
// userId(0) = CELL_SYSUTIL_USERID_CURRENT
// path of the specified user (00000001 by default)
const std::string& base_dir = vfs::get(fmt::format("/dev_hdd0/home/%08u/savedata/", userId ? userId : 1u));
@@ -100,6 +102,7 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
const auto prefix_list = fmt::split(setList->dirNamePrefix.get_ptr(), { "|" });
// get the saves matching the supplied prefix
for (const auto& entry : fs::dir(base_dir))
{
if (!entry.is_directory)
@@ -211,13 +214,15 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
if (result->result < 0)
{
//TODO: display dialog
cellSaveData.warning("savedata_op(): funcList returned < 0.");
return CELL_SAVEDATA_ERROR_CBRESULT;
}
// if the callback has returned ok, lets return OK.
// typically used at game launch when no list is actually required.
if ((result->result == CELL_SAVEDATA_CBRESULT_OK_LAST) || (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM))
// CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM is only valid for funcFile and funcDone
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST)
{
return CELL_OK;
}
@@ -236,6 +241,16 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
return true;
}), save_entries.end());
// Add any new data (not currently supported by the UI)
//if (listSet->newData)
//{
// SaveDataEntry *_saveDataEntry = new SaveDataEntry();
// _saveDataEntry->dirName = listSet->newData->dirName.get_ptr();
// save_entry.dirName = listSet->newData->dirName.get_ptr();
// save_entries.emplace_back(*_saveDataEntry);
//}
// Focus save data
s32 focused = -1;
@@ -296,6 +311,8 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
}
case CELL_SAVEDATA_FOCUSPOS_NEWDATA:
{
//TODO: If adding the new data to the save_entries vector
// to be displayed in the save mangaer UI, it should be focused here
break;
}
default:
@@ -339,8 +356,19 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
// Fixed Callback
funcFixed(ppu, result, listGet, fixedSet);
// skip all following steps if OK_LAST
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST)
{
return CELL_OK;
}
if (result->result < 0)
{
//TODO: Show msgDialog if required
// depends on fixedSet->option
// 0 = none
// 1 = skip confirmation dialog
cellSaveData.warning("savedata_op(): funcFixed returned < 0.");
return CELL_SAVEDATA_ERROR_CBRESULT;
}
@@ -363,10 +391,7 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
{
save_entry.dirName = fixedSet->dirName.get_ptr();
}
if ((result->result == CELL_SAVEDATA_CBRESULT_OK_LAST) || (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM))
{
return CELL_OK;
}
}
if (selected >= 0)
@@ -479,6 +504,11 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
return CELL_SAVEDATA_ERROR_CBRESULT;
}
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST)
{
return CELL_OK;
}
if (statSet->setParam)
{
// Update PARAM.SFO
@@ -498,18 +528,20 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
{ "TITLE", psf::string(128, statSet->setParam->title) },
});
}
else if (psf.empty())
{
// setParam is NULL for new savedata: abort operation
return CELL_OK;
}
//else if (psf.empty())
//{
// // setParam is specified if something required updating.
// // Do not exit. Recreate mode will handle the rest
// //return CELL_OK;
//}
switch (const u32 mode = statSet->reCreateMode & 0xffff)
{
case CELL_SAVEDATA_RECREATE_NO:
{
cellSaveData.error("Savedata %s considered broken", save_entry.dirName);
//CELL_SAVEDATA_RECREATE_NO = overwrite and let the user know, not data is corrupt.
//cellSaveData.error("Savedata %s considered broken", save_entry.dirName);
//TODO: if this is a save, and it's not auto, then show a dialog
// fallthrough
}
@@ -521,7 +553,8 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
case CELL_SAVEDATA_RECREATE_YES:
case CELL_SAVEDATA_RECREATE_YES_RESET_OWNER:
{
// TODO?
// TODO: Only delete data, not owner info
for (const auto& entry : fs::dir(dir_path))
{
if (!entry.is_directory)
@@ -530,12 +563,13 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
}
}
//TODO: probably not deleting owner info
if (!statSet->setParam)
{
// Savedata deleted and setParam is NULL: delete directory and abort operation
if (fs::remove_dir(dir_path)) cellSaveData.error("savedata_op(): savedata directory %s deleted", save_entry.dirName);
return CELL_OK;
//return CELL_OK;
}
break;
@@ -549,16 +583,13 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
}
}
if ((result->result == CELL_SAVEDATA_CBRESULT_OK_LAST) || (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM))
{
return CELL_OK;
}
// Create save directory if necessary
if (psf.size() && save_entry.isNew && !fs::create_dir(dir_path))
{
// Let's ignore this error for now
{
cellSaveData.warning("savedata_op(): failed to create %s", dir_path);
return CELL_SAVEDATA_ERROR_ACCESS_ERROR;
}
// Enter the loop where the save files are read/created/deleted
@@ -578,9 +609,13 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST || result->result == CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM)
{
//todo: display user prompt
break;
}
//TODO: Show progress
// if it's not an auto load/save
std::string file_path;
switch (const u32 type = fileSet->fileType)
@@ -681,6 +716,57 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
return CELL_OK;
}
s32 static NEVER_INLINE save_op_get_list_item(vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB, u32 userId)
{
//TODO: accurately get the current user
if (userId == 0)
{
userId = 1u;
}
std::string save_path = vfs::get(fmt::format("/dev_hdd0/home/%08u/savedata/%s/", userId, dirName.get_ptr()));
std::string sfo = save_path + "param.sfo";
if (!fs::is_dir(save_path) && !fs::is_file(sfo))
{
cellSaveData.error("cellSaveDataGetListItem(): Savedata at %s does not exist", dirName);
return CELL_SAVEDATA_ERROR_NODATA;
}
auto psf = psf::load_object(fs::file(sfo));
if (sysFileParam)
{
strcpy_trunc(sysFileParam->listParam, psf.at("SAVEDATA_LIST_PARAM").as_string());
strcpy_trunc(sysFileParam->title, psf.at("TITLE").as_string());
strcpy_trunc(sysFileParam->subTitle, psf.at("SUB_TITLE").as_string());
strcpy_trunc(sysFileParam->detail, psf.at("DETAIL").as_string());
}
if (dir)
{
fs::stat_t dir_info{};
if (!fs::stat(save_path, dir_info))
{
return CELL_SAVEDATA_ERROR_INTERNAL;
}
// get file stats, namely directory
strcpy_trunc(dir->dirName, dirName.get_ptr());
dir->atime = dir_info.atime;
dir->ctime = dir_info.ctime;
dir->mtime = dir_info.mtime;
}
if (bind)
{
//TODO: Set bind in accordance to any problems
*bind = 0;
}
return CELL_OK;
}
// Functions
s32 cellSaveDataListSave2(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
@@ -949,27 +1035,8 @@ s32 cellSaveDataFixedExport(ppu_thread& ppu, vm::cptr<char> dirName, u32 maxSize
s32 cellSaveDataGetListItem(vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB)
{
cellSaveData.warning("cellSavaDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x)", dirName, dir, sysFileParam, bind, sizeKB);
std::string save_path = vfs::get(fmt::format("/dev_hdd0/home/00000001/savedata/%s/", dirName.get_ptr()));
std::string sfo = save_path + "param.sfo";
if (!fs::is_dir(save_path) && !fs::is_file(sfo))
{
cellSaveData.error("cellSaveDataGetListItem(): Savedata at %s does not exist", dirName);
return CELL_SAVEDATA_ERROR_NODATA;
}
auto psf = psf::load_object(fs::file(sfo));
if (sysFileParam)
{
strcpy_trunc(sysFileParam->listParam, psf.at("SAVEDATA_LIST_PARAM").as_string());
strcpy_trunc(sysFileParam->title, psf.at("TITLE").as_string());
strcpy_trunc(sysFileParam->subTitle, psf.at("SUB_TITLE").as_string());
strcpy_trunc(sysFileParam->detail, psf.at("DETAIL").as_string());
}
return CELL_OK;
return save_op_get_list_item(dirName, dir, sysFileParam, bind, sizeKB, 0);
}
s32 cellSaveDataUserListDelete(ppu_thread& ppu, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
@@ -1009,9 +1076,9 @@ s32 cellSaveDataUserFixedExport(ppu_thread& ppu, u32 userId, vm::cptr<char> dirN
s32 cellSaveDataUserGetListItem(u32 userId, vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB)
{
UNIMPLEMENTED_FUNC(cellSaveData);
cellSaveData.warning("cellSavaDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x, userID=*0x%x)", dirName, dir, sysFileParam, bind, sizeKB, userId);
return CELL_OK;
return save_op_get_list_item(dirName, dir, sysFileParam, bind, sizeKB, userId);
}
void cellSysutil_SaveData_init()
+1 -1
View File
@@ -1,4 +1,4 @@
#pragma once
#pragma once
namespace vm { using namespace ps3; }
+1 -1
View File
@@ -1,4 +1,4 @@
#pragma once
#pragma once
namespace vm { using namespace ps3; }
+19 -5
View File
@@ -511,9 +511,9 @@ s32 sceNpBasicGetEvent(vm::ptr<s32> event, vm::ptr<SceNpUserInfo> from, vm::ptr<
sceNp.warning("sceNpBasicGetEvent(event=*0x%x, from=*0x%x, data=*0x%x, size=*0x%x)", event, from, data, size);
// TODO: Check for other error and pass other events
*event = SCE_NP_BASIC_EVENT_OFFLINE;
//*event = SCE_NP_BASIC_EVENT_OFFLINE; // This event only indicates a contact is offline, not the current status of the connection
return CELL_OK;
return SCE_NP_BASIC_ERROR_NO_EVENT;
}
s32 sceNpCommerceCreateCtx()
@@ -1717,6 +1717,18 @@ s32 sceNpSignalingGetPeerNetInfoResult()
return CELL_OK;
}
s32 sceNpUtilCanonicalizeNpIdForPs3()
{
UNIMPLEMENTED_FUNC(sceNp);
return CELL_OK;
}
s32 sceNpUtilCanonicalizeNpIdForPsp()
{
UNIMPLEMENTED_FUNC(sceNp);
return CELL_OK;
}
s32 sceNpUtilCmpNpId(vm::ptr<SceNpId> id1, vm::ptr<SceNpId> id2)
{
sceNp.warning("sceNpUtilCmpNpId(id1=*0x%x, id2=*0x%x)", id1, id2);
@@ -2032,11 +2044,13 @@ DECLARE(ppu_module_manager::sceNp)("sceNp", []()
REG_FUNC(sceNp, sceNpSignalingGetPeerNetInfo);
REG_FUNC(sceNp, sceNpSignalingCancelPeerNetInfo);
REG_FUNC(sceNp, sceNpSignalingGetPeerNetInfoResult);
REG_FUNC(sceNp, sceNpUtilCanonicalizeNpIdForPs3);
REG_FUNC(sceNp, sceNpUtilCanonicalizeNpIdForPsp);
REG_FUNC(sceNp, sceNpUtilCmpNpId);
REG_FUNC(sceNp, sceNpUtilCmpNpIdInOrder);
REG_FUNC(sceNp, sceNpUtilCmpOnlineId); // 0x8C760B52
REG_FUNC(sceNp, sceNpUtilGetPlatformType); // 0xC611029A
REG_FUNC(sceNp, sceNpUtilSetPlatformType); // 0xAFC62605
REG_FUNC(sceNp, sceNpUtilCmpOnlineId);
REG_FUNC(sceNp, sceNpUtilGetPlatformType);
REG_FUNC(sceNp, sceNpUtilSetPlatformType);
REG_FUNC(sceNp, _sceNpSysutilClientMalloc);
REG_FUNC(sceNp, _sceNpSysutilClientFree);
REG_FUNC(sceNp, _Z33_sce_np_sysutil_send_empty_packetiPN16sysutil_cxmlutil11FixedMemoryEPKcS3_);
+27
View File
@@ -445,6 +445,29 @@ s32 sceNpMatching2RegisterRoomMessageCallback()
return CELL_OK;
}
s32 sceNpMatching2SignalingCancelPeerNetInfo()
{
UNIMPLEMENTED_FUNC(sceNp2);
return CELL_OK;
}
s32 sceNpMatching2SignalingGetLocalNetInfo()
{
UNIMPLEMENTED_FUNC(sceNp2);
return CELL_OK;
}
s32 sceNpMatching2SignalingGetPeerNetInfo()
{
UNIMPLEMENTED_FUNC(sceNp2);
return CELL_OK;
}
s32 sceNpMatching2SignalingGetPeerNetInfoResult()
{
UNIMPLEMENTED_FUNC(sceNp2);
return CELL_OK;
}
DECLARE(ppu_module_manager::sceNp2)("sceNp2", []()
{
@@ -517,4 +540,8 @@ DECLARE(ppu_module_manager::sceNp2)("sceNp2", []()
REG_FUNC(sceNp2, sceNpMatching2Init2);
REG_FUNC(sceNp2, sceNpMatching2SetLobbyMemberDataInternal);
REG_FUNC(sceNp2, sceNpMatching2RegisterRoomMessageCallback);
REG_FUNC(sceNp2, sceNpMatching2SignalingCancelPeerNetInfo);
REG_FUNC(sceNp2, sceNpMatching2SignalingGetLocalNetInfo);
REG_FUNC(sceNp2, sceNpMatching2SignalingGetPeerNetInfo);
REG_FUNC(sceNp2, sceNpMatching2SignalingGetPeerNetInfoResult);
});
+54 -2
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Utilities/VirtualMemory.h"
#include "Utilities/bin_patch.h"
#include "Crypto/sha1.h"
#include "Crypto/unself.h"
#include "Loader/ELF.h"
@@ -130,6 +131,11 @@ struct ppu_linkage_info
// Initialize static modules.
static void ppu_initialize_modules(const std::shared_ptr<ppu_linkage_info>& link)
{
if (!link->modules.empty())
{
return;
}
ppu_initialize_syscalls();
const std::initializer_list<const ppu_static_module*> registered
@@ -674,6 +680,9 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
// Access linkage information object
const auto link = fxm::get_always<ppu_linkage_info>();
// Initialize HLE modules
ppu_initialize_modules(link);
for (const auto& prog : elf.progs)
{
LOG_NOTICE(LOADER, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
@@ -894,6 +903,15 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
prx->epilogue.set(prx->specials[0x330F7005]);
prx->name = path.substr(path.find_last_of('/') + 1);
prx->path = path;
if (Emu.IsReady() && fxm::import<ppu_module>([&] { return prx; }))
{
// Special loading mode
auto ppu = idm::make_ptr<ppu_thread>("test_thread", 0, 0x100000);
ppu->cmd_push({ppu_cmd::initialize, 0});
}
return prx;
}
@@ -937,6 +955,11 @@ void ppu_load_exec(const ppu_exec_object& elf)
u32 primary_stacksize = 0x100000;
u32 malloc_pagesize = 0x100000;
// Executable hash
sha1_context sha;
sha1_starts(&sha);
u8 sha1_hash[20];
// Allocate memory at fixed positions
for (const auto& prog : elf.progs)
{
@@ -947,7 +970,11 @@ void ppu_load_exec(const ppu_exec_object& elf)
const u32 size = _seg.size = ::narrow<u32>(prog.p_memsz, "p_memsz" HERE);
const u32 type = _seg.type = prog.p_type;
const u32 flag = _seg.flags = prog.p_flags;
// Hash big-endian values
sha1_update(&sha, (uchar*)&prog.p_type, sizeof(prog.p_type));
sha1_update(&sha, (uchar*)&prog.p_flags, sizeof(prog.p_flags));
if (type == 0x1 /* LOAD */ && prog.p_memsz)
{
if (prog.bin.size() > size || prog.bin.size() != prog.p_filesz)
@@ -956,8 +983,11 @@ void ppu_load_exec(const ppu_exec_object& elf)
if (!vm::falloc(addr, size, vm::main))
fmt::throw_exception("vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size);
// Copy segment data
// Copy segment data, hash it
std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
sha1_update(&sha, (uchar*)&prog.p_vaddr, sizeof(prog.p_vaddr));
sha1_update(&sha, (uchar*)&prog.p_memsz, sizeof(prog.p_memsz));
sha1_update(&sha, prog.bin.data(), prog.bin.size());
// Initialize executable code if necessary
if (prog.p_flags & 0x1)
@@ -987,6 +1017,28 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
}
sha1_finish(&sha, sha1_hash);
// Format patch name
std::string hash("PPU-0000000000000000000000000000000000000000");
for (u32 i = 0; i < sizeof(sha1_hash); i++)
{
constexpr auto pal = "0123456789abcdef";
hash[4 + i * 2] = pal[sha1_hash[i] >> 4];
hash[5 + i * 2] = pal[sha1_hash[i] & 15];
}
// Apply the patch
auto applied = fxm::check_unlocked<patch_engine>()->apply(hash, vm::g_base_addr);
if (!Emu.GetTitleID().empty())
{
// Alternative patch
applied += fxm::check_unlocked<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
}
LOG_NOTICE(LOADER, "PPU executable hash: %s (<- %u)", hash, applied);
// Initialize HLE modules
ppu_initialize_modules(link);
+46 -4
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Utilities/VirtualMemory.h"
#include "Utilities/sysinfo.h"
#include "Crypto/sha1.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
@@ -53,6 +54,8 @@
#include <cfenv>
#include "Utilities/GSL.h"
const bool s_use_rtm = utils::has_rtm();
extern u64 get_system_time();
namespace vm { using namespace ps3; }
@@ -825,6 +828,26 @@ extern bool ppu_stwcx(ppu_thread& ppu, u32 addr, u32 reg_value)
return false;
}
if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
const bool result = ppu.rtime == vm::reservation_acquire(addr, sizeof(u32)) && data.compare_and_swap_test(static_cast<u32>(ppu.rdata), reg_value);
if (result)
{
vm::reservation_update(addr, sizeof(u32));
vm::notify(addr, sizeof(u32));
}
_xend();
ppu.raddr = 0;
return result;
}
vm::writer_lock lock(0);
const bool result = ppu.rtime == vm::reservation_acquire(addr, sizeof(u32)) && data.compare_and_swap_test(static_cast<u32>(ppu.rdata), reg_value);
@@ -849,6 +872,26 @@ extern bool ppu_stdcx(ppu_thread& ppu, u32 addr, u64 reg_value)
return false;
}
if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
const bool result = ppu.rtime == vm::reservation_acquire(addr, sizeof(u64)) && data.compare_and_swap_test(ppu.rdata, reg_value);
if (result)
{
vm::reservation_update(addr, sizeof(u64));
vm::notify(addr, sizeof(u64));
}
_xend();
ppu.raddr = 0;
return result;
}
vm::writer_lock lock(0);
const bool result = ppu.rtime == vm::reservation_acquire(addr, sizeof(u64)) && data.compare_and_swap_test(ppu.rdata, reg_value);
@@ -1142,7 +1185,8 @@ extern void ppu_initialize(const ppu_module& info)
if (fs::is_file(cache_path + obj_name))
{
semaphore_lock lock(jmutex);
ppu_initialize2(*jit, part, cache_path, obj_name);
jit->add(cache_path + obj_name);
LOG_SUCCESS(PPU, "LLVM: Loaded module %s", obj_name);
continue;
}
@@ -1173,7 +1217,7 @@ extern void ppu_initialize(const ppu_module& info)
// Proceed with original JIT instance
semaphore_lock lock(jmutex);
ppu_initialize2(*jit, part, cache_path, obj_name);
jit->add(cache_path + obj_name);
});
}
@@ -1286,8 +1330,6 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
std::shared_ptr<MsgDialogBase> dlg;
// Check cached file
if (!fs::is_file(cache_path + obj_name))
{
legacy::FunctionPassManager pm(module.get());
+27 -13
View File
@@ -43,23 +43,27 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
// Key for multimap
const u64 key = entry | u64{ ls[entry / 4] } << 32;
const be_t<u32>* base = ls + entry / 4;
const u32 block_sz = max_limit - entry;
{
reader_lock lock(m_mutex);
// Try to find existing function in the database
if (auto func = find(ls + entry / 4, key, max_limit - entry))
if (auto func = find(base, key, block_sz))
{
return func;
}
}
writer_lock lock(m_mutex);
// Double-check
if (auto func = find(ls + entry / 4, key, max_limit - entry))
{
return func;
writer_lock lock(m_mutex);
// Double-check
if (auto func = find(base, key, block_sz))
{
return func;
}
}
// Initialize block entries with the function entry point
@@ -81,11 +85,15 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
const auto type = s_spu_itype.decode(op.opcode);
// Find existing function
if (pos != entry && find(ls + pos / 4, pos | u64{ op.opcode } << 32, limit - pos))
{
limit = pos;
break;
reader_lock lock(m_mutex);
// Find existing function
if (pos != entry && find(ls + pos / 4, pos | u64{ op.opcode } << 32, limit - pos))
{
limit = pos;
break;
}
}
// Additional analysis at the beginning of the block
@@ -311,10 +319,16 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
// Set whether the function can reset stack
func->does_reset_stack = ila_sp_pos < limit;
// Add function to the database
m_db.emplace(key, func);
// Lock here just before we write to the db
// Its is unlikely that the second check will pass anyway so we delay this step since compiling functions is very fast
{
writer_lock lock(m_mutex);
LOG_SUCCESS(SPU, "Function detected [0x%05x-0x%05x] (size=0x%x)", func->addr, func->addr + func->size, func->size);
// Add function to the database
m_db.emplace(key, func);
}
LOG_NOTICE(SPU, "Function detected [0x%05x-0x%05x] (size=0x%x)", func->addr, func->addr + func->size, func->size);
return func;
}
+11 -2
View File
@@ -5,6 +5,7 @@
#include "SPUThread.h"
#include "SPURecompiler.h"
#include "SPUASMJITRecompiler.h"
#include <algorithm>
extern u64 get_system_time();
@@ -22,8 +23,15 @@ void spu_recompiler_base::enter(SPUThread& spu)
// Get SPU LS pointer
const auto _ls = vm::ps3::_ptr<u32>(spu.offset);
// Always validate (TODO)
const auto func = spu.spu_db->analyse(_ls, spu.pc);
// Search if cached data matches
auto func = spu.compiled_cache[spu.pc / 4];
// Check shared db if we dont have a match
if (!func || !std::equal(func->data.begin(), func->data.end(), _ls + spu.pc / 4, [](const be_t<u32>& l, const be_t<u32>& r) { return *(u32*)(u8*)&l == *(u32*)(u8*)&r; }))
{
func = spu.spu_db->analyse(_ls, spu.pc).get();
spu.compiled_cache[spu.pc / 4] = func;
}
// Reset callstack if necessary
if ((func->does_reset_stack && spu.recursion_level) || spu.recursion_level >= 128)
@@ -32,6 +40,7 @@ void spu_recompiler_base::enter(SPUThread& spu)
return;
}
// Compile if needed
if (!func->compiled)
{
if (!spu.spu_rec)
+129 -10
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Utilities/lockless.h"
#include "Utilities/sysinfo.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
@@ -22,6 +23,8 @@
#include <atomic>
#include <thread>
const bool s_use_rtm = utils::has_rtm();
#ifdef _MSC_VER
bool operator ==(const u128& lhs, const u128& rhs)
{
@@ -55,6 +58,65 @@ void fmt_class_string<spu_decoder_type>::format(std::string& out, u64 arg)
});
}
namespace spu
{
namespace scheduler
{
std::array<std::atomic<u8>, 65536> atomic_instruction_table = {};
constexpr u32 native_jiffy_duration_us = 2000000;
void acquire_pc_address(u32 pc, u32 timeout_ms = 3)
{
const u8 max_concurrent_instructions = (u8)g_cfg.core.preferred_spu_threads;
const u32 pc_offset = pc >> 2;
if (timeout_ms > 0)
{
while (timeout_ms--)
{
if (atomic_instruction_table[pc_offset].load(std::memory_order_consume) >= max_concurrent_instructions)
std::this_thread::sleep_for(1ms);
}
}
else
{
std::this_thread::yield();
}
atomic_instruction_table[pc_offset]++;
}
void release_pc_address(u32 pc)
{
const u32 pc_offset = pc >> 2;
atomic_instruction_table[pc_offset]--;
}
struct concurrent_execution_watchdog
{
u32 pc = 0;
bool active = false;
concurrent_execution_watchdog(SPUThread& spu)
:pc(spu.pc)
{
if (g_cfg.core.preferred_spu_threads > 0)
{
acquire_pc_address(pc, (u32)g_cfg.core.spu_delay_penalty);
active = true;
}
}
~concurrent_execution_watchdog()
{
if (active)
release_pc_address(pc);
}
};
}
}
void spu_int_ctrl_t::set(u64 ints)
{
// leave only enabled interrupts
@@ -483,6 +545,7 @@ void SPUThread::do_dma_transfer(const spu_mfc_cmd& args, bool from_mfc)
void SPUThread::process_mfc_cmd()
{
spu::scheduler::concurrent_execution_watchdog watchdog(*this);
LOG_TRACE(SPU, "DMAC: cmd=%s, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", ch_mfc_cmd.cmd, ch_mfc_cmd.lsa, ch_mfc_cmd.eal, ch_mfc_cmd.tag, ch_mfc_cmd.size);
const auto mfc = fxm::check_unlocked<mfc_thread>();
@@ -499,7 +562,7 @@ void SPUThread::process_mfc_cmd()
}
// TODO: investigate lost notifications
std::this_thread::sleep_for(0us);
std::this_thread::yield();
_mm_lfence();
}
};
@@ -544,9 +607,22 @@ void SPUThread::process_mfc_cmd()
thread_ctrl::wait_for(100);
}
}
else if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
rtime = vm::reservation_acquire(raddr, 128);
rdata = data;
_xend();
_ref<decltype(rdata)>(ch_mfc_cmd.lsa & 0x3ffff) = rdata;
return ch_atomic_stat.set_value(MFC_GETLLAR_SUCCESS);
}
else
{
// Fast path
rdata = data;
_mm_lfence();
}
@@ -577,15 +653,36 @@ void SPUThread::process_mfc_cmd()
if (raddr == ch_mfc_cmd.eal && rtime == vm::reservation_acquire(raddr, 128) && rdata == data)
{
// TODO: vm::check_addr
vm::writer_lock lock;
if (rtime == vm::reservation_acquire(raddr, 128) && rdata == data)
if (s_use_rtm && utils::transaction_enter())
{
data = to_write;
result = true;
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
vm::reservation_update(raddr, 128);
vm::notify(raddr, 128);
if (rtime == vm::reservation_acquire(raddr, 128) && rdata == data)
{
data = to_write;
result = true;
vm::reservation_update(raddr, 128);
vm::notify(raddr, 128);
}
_xend();
}
else
{
vm::writer_lock lock;
if (rtime == vm::reservation_acquire(raddr, 128) && rdata == data)
{
data = to_write;
result = true;
vm::reservation_update(raddr, 128);
vm::notify(raddr, 128);
}
}
}
@@ -621,6 +718,23 @@ void SPUThread::process_mfc_cmd()
// Store unconditionally
// TODO: vm::check_addr
if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
data = to_write;
vm::reservation_update(ch_mfc_cmd.eal, 128);
vm::notify(ch_mfc_cmd.eal, 128);
_xend();
ch_atomic_stat.set_value(MFC_PUTLLUC_SUCCESS);
return;
}
vm::writer_lock lock(0);
data = to_write;
vm::reservation_update(ch_mfc_cmd.eal, 128);
@@ -897,7 +1011,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
if (ctr > 10000)
{
ctr = 0;
std::this_thread::sleep_for(0us);
std::this_thread::yield();
}
else
{
@@ -978,6 +1092,11 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
case SPU_RdDec:
{
out = ch_dec_value - (u32)(get_timebased_time() - ch_dec_start_timestamp);
//Polling: We might as well hint to the scheduler to slot in another thread since this one is counting down
if (g_cfg.core.spu_loop_detection && out > spu::scheduler::native_jiffy_duration_us)
std::this_thread::yield();
return true;
}
+1
View File
@@ -582,6 +582,7 @@ public:
std::exception_ptr pending_exception;
std::array<struct spu_function_t*, 65536> compiled_cache{};
std::shared_ptr<class SPUDatabase> spu_db;
std::shared_ptr<class spu_recompiler_base> spu_rec;
u32 recursion_level = 0;
+39 -28
View File
@@ -34,24 +34,13 @@ void sys_spu_image::load(const fs::file& stream)
const u32 addr = this->segs.addr() + 4096;
sha1_context ctx;
u8 output[20];
sha1_starts(&ctx);
sha1_update(&ctx, reinterpret_cast<const u8*>(&obj.header), sizeof(obj.header));
for (const auto& shdr : obj.shdrs)
{
sha1_update(&ctx, reinterpret_cast<const u8*>(&shdr), sizeof(spu_exec_object::shdr_t));
LOG_NOTICE(SPU, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", shdr.sh_type, shdr.sh_addr, shdr.sh_size, shdr.sh_flags);
}
for (const auto& prog : obj.progs)
{
sha1_update(&ctx, reinterpret_cast<const u8*>(&prog), sizeof(spu_exec_object::phdr_t));
sha1_update(&ctx, reinterpret_cast<const u8*>(prog.bin.data()), prog.bin.size());
LOG_NOTICE(SPU, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
if (prog.p_type == SYS_SPU_SEGMENT_TYPE_COPY)
@@ -85,22 +74,6 @@ void sys_spu_image::load(const fs::file& stream)
LOG_ERROR(SPU, "Unknown program type (0x%x)", prog.p_type);
}
}
sha1_finish(&ctx, output);
// Format patch name
std::string hash("spu-");
for (u8 x : output) fmt::append(hash, "%02x", x);
LOG_NOTICE(LOADER, "Loaded SPU image: %s", hash);
// Apply the patch
fxm::check_unlocked<patch_engine>()->apply(hash, vm::g_base_addr + addr);
if (!Emu.GetTitleID().empty())
{
// Alternative patch
fxm::check_unlocked<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr + addr);
}
}
void sys_spu_image::free()
@@ -113,15 +86,29 @@ void sys_spu_image::free()
void sys_spu_image::deploy(u32 loc)
{
// Segment info dump
std::string dump;
// Executable hash
sha1_context sha;
sha1_starts(&sha);
u8 sha1_hash[20];
for (int i = 0; i < nsegs; i++)
{
auto& seg = segs[i];
LOG_NOTICE(SPU, "*** Deploy: t=0x%x, ls=0x%x, size=0x%x, addr=0x%x", seg.type, seg.ls, seg.size, seg.addr);
fmt::append(dump, "\n\t[%d] t=0x%x, ls=0x%x, size=0x%x, addr=0x%x", i, seg.type, seg.ls, seg.size, seg.addr);
// Hash big-endian values
sha1_update(&sha, (uchar*)&seg.type, sizeof(seg.type));
sha1_update(&sha, (uchar*)&seg.size, sizeof(seg.size));
if (seg.type == SYS_SPU_SEGMENT_TYPE_COPY)
{
std::memcpy(vm::base(loc + seg.ls), vm::base(seg.addr), seg.size);
sha1_update(&sha, (uchar*)&seg.ls, sizeof(seg.ls));
sha1_update(&sha, vm::g_base_addr + seg.addr, seg.size);
}
else if (seg.type == SYS_SPU_SEGMENT_TYPE_FILL)
{
@@ -131,8 +118,32 @@ void sys_spu_image::deploy(u32 loc)
}
std::fill_n(vm::_ptr<u32>(loc + seg.ls), seg.size / 4, seg.addr);
sha1_update(&sha, (uchar*)&seg.ls, sizeof(seg.ls));
sha1_update(&sha, (uchar*)&seg.addr, sizeof(seg.addr));
}
}
sha1_finish(&sha, sha1_hash);
// Format patch name
std::string hash("SPU-0000000000000000000000000000000000000000");
for (u32 i = 0; i < sizeof(sha1_hash); i++)
{
constexpr auto pal = "0123456789abcdef";
hash[4 + i * 2] = pal[sha1_hash[i] >> 4];
hash[5 + i * 2] = pal[sha1_hash[i] & 15];
}
// Apply the patch
auto applied = fxm::check_unlocked<patch_engine>()->apply(hash, vm::g_base_addr + loc);
if (!Emu.GetTitleID().empty())
{
// Alternative patch
applied += fxm::check_unlocked<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr + loc);
}
LOG_NOTICE(LOADER, "Loaded SPU image: %s (<- %u)%s", hash, applied, dump);
}
error_code sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu)
+2 -2
View File
@@ -27,9 +27,9 @@
namespace vm
{
static u8* memory_reserve_4GiB(std::uintptr_t addr = 0)
static u8* memory_reserve_4GiB(std::uintptr_t _addr = 0)
{
for (u64 addr = 0x100000000;; addr += 0x100000000)
for (u64 addr = _addr + 0x100000000;; addr += 0x100000000)
{
if (auto ptr = utils::memory_reserve(0x100000000, (void*)addr))
{
+29 -5
View File
@@ -1,10 +1,13 @@
#include "stdafx.h"
#include "Utilities/sysinfo.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "ARMv7Thread.h"
#include "ARMv7Interpreter.h"
const bool s_use_rtm = utils::has_rtm();
using namespace arm_code::arm_encoding_alias;
#define ARG(arg, ...) const u32 arg = args::arg::extract(__VA_ARGS__);
@@ -2091,13 +2094,34 @@ void arm_interpreter::STREX(ARMv7Thread& cpu, const u32 op, const u32 cond)
return;
}
vm::writer_lock lock(0);
bool result;
const bool result = cpu.rtime == vm::reservation_acquire(addr, cpu.rtime) && data.compare_and_swap_test(cpu.rdata, value);
if (result)
if (s_use_rtm && utils::transaction_enter())
{
vm::reservation_update(addr, sizeof(u32));
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
result = cpu.rtime == vm::reservation_acquire(addr, sizeof(u32)) && data.compare_and_swap_test(cpu.rdata, value);
if (result)
{
vm::reservation_update(addr, sizeof(u32));
}
_xend();
}
else
{
vm::writer_lock lock(0);
result = cpu.rtime == vm::reservation_acquire(addr, sizeof(u32)) && data.compare_and_swap_test(cpu.rdata, value);
if (result)
{
vm::reservation_update(addr, sizeof(u32));
}
}
cpu.raddr = 0;
+79 -5
View File
@@ -93,7 +93,12 @@ namespace rsx
auto It = m_render_targets_storage.find(address);
// TODO: Fix corner cases
// This doesn't take overlapping surface(s) into account.
surface_storage_type old_surface_storage;
surface_storage_type new_surface_storage;
surface_type old_surface = nullptr;
surface_type new_surface = nullptr;
if (It != m_render_targets_storage.end())
{
surface_storage_type &rtt = It->second;
@@ -104,10 +109,43 @@ namespace rsx
}
old_surface = Traits::get(rtt);
invalidated_resources.push_back(std::move(rtt));
old_surface_storage = std::move(rtt);
m_render_targets_storage.erase(address);
}
//Search invalidated resources for a suitable surface
for (auto It = invalidated_resources.begin(); It != invalidated_resources.end(); It++)
{
auto &rtt = *It;
if (Traits::rtt_has_format_width_height(rtt, color_format, width, height, true))
{
new_surface_storage = std::move(rtt);
if (old_surface)
//Exchange this surface with the invalidated one
rtt = std::move(old_surface_storage);
else
//rtt is now empty - erase it
invalidated_resources.erase(It);
new_surface = Traits::get(new_surface_storage);
Traits::invalidate_rtt_surface_contents(command_list, new_surface, old_surface, true);
Traits::prepare_rtt_for_drawing(command_list, new_surface);
break;
}
}
if (old_surface != nullptr && new_surface == nullptr)
//This was already determined to be invalid and is excluded from testing above
invalidated_resources.push_back(std::move(old_surface_storage));
if (new_surface != nullptr)
{
//New surface was found among existing surfaces
m_render_targets_storage[address] = std::move(new_surface_storage);
return new_surface;
}
m_render_targets_storage[address] = Traits::create_new_surface(address, color_format, width, height, old_surface, std::forward<Args>(extra_params)...);
return Traits::get(m_render_targets_storage[address]);
}
@@ -119,7 +157,11 @@ namespace rsx
surface_depth_format depth_format, size_t width, size_t height,
Args&&... extra_params)
{
surface_storage_type old_surface_storage;
surface_storage_type new_surface_storage;
surface_type old_surface = nullptr;
surface_type new_surface = nullptr;
auto It = m_depth_stencil_storage.find(address);
if (It != m_depth_stencil_storage.end())
{
@@ -131,10 +173,42 @@ namespace rsx
}
old_surface = Traits::get(ds);
invalidated_resources.push_back(std::move(ds));
old_surface_storage = std::move(ds);
m_depth_stencil_storage.erase(address);
}
//Search invalidated resources for a suitable surface
for (auto It = invalidated_resources.begin(); It != invalidated_resources.end(); It++)
{
auto &ds = *It;
if (Traits::ds_has_format_width_height(ds, depth_format, width, height, true))
{
new_surface_storage = std::move(ds);
if (old_surface)
//Exchange this surface with the invalidated one
ds = std::move(old_surface_storage);
else
invalidated_resources.erase(It);
new_surface = Traits::get(new_surface_storage);
Traits::prepare_ds_for_drawing(command_list, new_surface);
Traits::invalidate_depth_surface_contents(command_list, new_surface, old_surface, true);
break;
}
}
if (old_surface != nullptr && new_surface == nullptr)
//This was already determined to be invalid and is excluded from testing above
invalidated_resources.push_back(std::move(old_surface_storage));
if (new_surface != nullptr)
{
//New surface was found among existing surfaces
m_depth_stencil_storage[address] = std::move(new_surface_storage);
return new_surface;
}
m_depth_stencil_storage[address] = Traits::create_new_surface(address, depth_format, width, height, old_surface, std::forward<Args>(extra_params)...);
return Traits::get(m_depth_stencil_storage[address]);
}
@@ -358,10 +432,10 @@ namespace rsx
void invalidate_surface_cache_data(command_list_type command_list)
{
for (auto &rtt : m_render_targets_storage)
Traits::invalidate_rtt_surface_contents(command_list, Traits::get(std::get<1>(rtt)));
Traits::invalidate_rtt_surface_contents(command_list, Traits::get(std::get<1>(rtt)), nullptr, false);
for (auto &ds : m_depth_stencil_storage)
Traits::invalidate_depth_surface_contents(command_list, Traits::get(std::get<1>(ds)));
Traits::invalidate_depth_surface_contents(command_list, Traits::get(std::get<1>(ds)), nullptr, true);
}
};
}
}
+3 -4
View File
@@ -223,9 +223,8 @@ namespace
const std::vector<std::pair<u32, u32>>& vertex_ranges, d3d12_data_heap& m_buffer_data)
{
size_t index_count = std::accumulate(
vertex_ranges.begin(), vertex_ranges.end(), 0, [](size_t acc, const auto& pair) {
return acc + get_index_count(
rsx::method_registers.current_draw_clause.primitive, pair.second);
vertex_ranges.begin(), vertex_ranges.end(), 0ll, [](size_t acc, const auto& pair) {
return acc + get_index_count(rsx::method_registers.current_draw_clause.primitive, pair.second);
});
// Alloc
@@ -236,7 +235,7 @@ namespace
void* mapped_buffer =
m_buffer_data.map<void>(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size));
size_t vertex_count = 0;
u32 vertex_count = 0;
for (const auto& pair : vertex_ranges)
vertex_count += pair.second;
+11 -5
View File
@@ -324,6 +324,17 @@ void D3D12GSRender::end()
{
std::chrono::time_point<steady_clock> start_duration = steady_clock::now();
std::chrono::time_point<steady_clock> program_load_start = steady_clock::now();
load_program();
std::chrono::time_point<steady_clock> program_load_end = steady_clock::now();
m_timers.program_load_duration += std::chrono::duration_cast<std::chrono::microseconds>(program_load_end - program_load_start).count();
if (!m_fragment_program.valid)
{
rsx::thread::end();
return;
}
std::chrono::time_point<steady_clock> rtt_duration_start = steady_clock::now();
prepare_render_targets(get_current_resource_storage().command_list.Get());
@@ -344,11 +355,6 @@ void D3D12GSRender::end()
std::chrono::time_point<steady_clock> vertex_index_duration_end = steady_clock::now();
m_timers.vertex_index_duration += std::chrono::duration_cast<std::chrono::microseconds>(vertex_index_duration_end - vertex_index_duration_start).count();
std::chrono::time_point<steady_clock> program_load_start = steady_clock::now();
load_program();
std::chrono::time_point<steady_clock> program_load_end = steady_clock::now();
m_timers.program_load_duration += std::chrono::duration_cast<std::chrono::microseconds>(program_load_end - program_load_start).count();
get_current_resource_storage().command_list->SetGraphicsRootSignature(m_shared_root_signature.Get());
get_current_resource_storage().command_list->OMSetStencilRef(rsx::method_registers.stencil_func_ref());
@@ -56,6 +56,9 @@ void D3D12GSRender::load_program()
m_vertex_program = get_current_vertex_program();
m_fragment_program = get_current_fragment_program(rtt_lookup_func);
if (!m_fragment_program.valid)
return;
D3D12PipelineProperties prop = {};
prop.Topology = get_primitive_topology_type(rsx::method_registers.current_draw_clause.primitive);
+6 -4
View File
@@ -116,28 +116,30 @@ struct render_target_traits
command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(ds, D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_GENERIC_READ));
}
static
void invalidate_rtt_surface_contents(
gsl::not_null<ID3D12GraphicsCommandList*>,
ID3D12Resource*)
ID3D12Resource*, ID3D12Resource*, bool)
{}
static
void invalidate_depth_surface_contents(
gsl::not_null<ID3D12GraphicsCommandList*>,
ID3D12Resource*)
ID3D12Resource*, ID3D12Resource*, bool)
{
//TODO
}
static
bool rtt_has_format_width_height(const ComPtr<ID3D12Resource> &rtt, surface_color_format surface_color_format, size_t width, size_t height)
bool rtt_has_format_width_height(const ComPtr<ID3D12Resource> &rtt, surface_color_format surface_color_format, size_t width, size_t height, bool=false)
{
DXGI_FORMAT dxgi_format = get_color_surface_format(surface_color_format);
return rtt->GetDesc().Format == dxgi_format && rtt->GetDesc().Width == width && rtt->GetDesc().Height == height;
}
static
bool ds_has_format_width_height(const ComPtr<ID3D12Resource> &rtt, surface_depth_format, size_t width, size_t height)
bool ds_has_format_width_height(const ComPtr<ID3D12Resource> &rtt, surface_depth_format, size_t width, size_t height, bool=false)
{
//TODO: Check format
return rtt->GetDesc().Width == width && rtt->GetDesc().Height == height;
+57 -22
View File
@@ -322,17 +322,15 @@ namespace
void GLGSRender::end()
{
if (skip_frame || !framebuffer_status_valid)
std::chrono::time_point<steady_clock> program_start = steady_clock::now();
//Load program here since it is dependent on vertex state
if (skip_frame || !framebuffer_status_valid || !load_program())
{
rsx::thread::end();
return;
}
std::chrono::time_point<steady_clock> program_start = steady_clock::now();
//Load program here since it is dependent on vertex state
load_program();
std::chrono::time_point<steady_clock> program_stop = steady_clock::now();
m_begin_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(program_stop - program_start).count();
@@ -364,44 +362,80 @@ void GLGSRender::end()
surface->old_contents = nullptr;
};
//Check if we have any 'recycled' surfaces in memory and if so, clear them
std::vector<int> buffers_to_clear;
bool clear_all_color = true;
bool clear_depth = false;
for (int index = 0; index < 4; index++)
{
if (std::get<0>(m_rtts.m_bound_render_targets[index]) != 0)
{
if (std::get<1>(m_rtts.m_bound_render_targets[index])->cleared())
clear_all_color = false;
else
buffers_to_clear.push_back(index);
}
}
gl::render_target *ds = std::get<1>(m_rtts.m_bound_depth_stencil);
if (ds && !ds->cleared())
{
//Temporarily disable pixel tests
glDisable(GL_SCISSOR_TEST);
glDepthMask(GL_TRUE);
glClearDepth(1.0);
glClearStencil(255);
clear_depth = true;
}
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//Temporarily disable pixel tests
glDisable(GL_SCISSOR_TEST);
if (g_cfg.video.strict_rendering_mode)
if (clear_depth || buffers_to_clear.size() > 0)
{
GLenum mask = 0;
if (clear_depth)
{
//Copy previous data if any
if (ds->old_contents != nullptr)
copy_rtt_contents(ds);
glDepthMask(GL_TRUE);
glClearDepth(1.0);
glClearStencil(255);
mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
}
glDepthMask(rsx::method_registers.depth_write_enabled());
glEnable(GL_SCISSOR_TEST);
if (clear_all_color)
mask |= GL_COLOR_BUFFER_BIT;
glClear(mask);
if (buffers_to_clear.size() > 0 && !clear_all_color)
{
GLfloat colors[] = { 0.f, 0.f, 0.f, 0.f };
//It is impossible for the render target to be typa A or B here (clear all would have been flagged)
for (auto &i: buffers_to_clear)
glClearBufferfv(draw_fbo.id(), i, colors);
}
if (clear_depth)
glDepthMask(rsx::method_registers.depth_write_enabled());
ds->set_cleared();
}
if (g_cfg.video.strict_rendering_mode)
{
if (ds->old_contents != nullptr)
copy_rtt_contents(ds);
for (auto &rtt : m_rtts.m_bound_render_targets)
{
if (std::get<0>(rtt) != 0)
{
auto surface = std::get<1>(rtt);
if (!surface->cleared() && surface->old_contents != nullptr)
if (surface->old_contents != nullptr)
copy_rtt_contents(surface);
}
}
}
glEnable(GL_SCISSOR_TEST);
std::chrono::time_point<steady_clock> textures_start = steady_clock::now();
//Setup textures
@@ -463,7 +497,6 @@ void GLGSRender::end()
}
else
{
//LOG_ERROR(RSX, "No work is needed for this draw call! Muhahahahahahaha");
skip_upload = true;
}
@@ -841,8 +874,10 @@ bool GLGSRender::load_program()
return std::make_tuple(true, surface->get_native_pitch());
};
RSXVertexProgram vertex_program = get_current_vertex_program();
RSXFragmentProgram fragment_program = get_current_fragment_program(rtt_lookup_func);
if (!fragment_program.valid) return false;
RSXVertexProgram vertex_program = get_current_vertex_program();
u32 unnormalized_rtts = 0;
+2
View File
@@ -173,6 +173,8 @@ OPENGL_PROC(PFNGLMULTIDRAWARRAYSPROC, MultiDrawArrays);
OPENGL_PROC(PFNGLGETTEXTUREIMAGEEXTPROC, GetTextureImageEXT);
OPENGL_PROC(PFNGLGETTEXTUREIMAGEPROC, GetTextureImage);
OPENGL_PROC(PFNGLCLEARBUFFERFVPROC, ClearBufferfv);
//Sampler Objects
OPENGL_PROC(PFNGLGENSAMPLERSPROC, GenSamplers);
OPENGL_PROC(PFNGLDELETESAMPLERSPROC, DeleteSamplers);
+11 -4
View File
@@ -192,6 +192,7 @@ struct gl_render_target_traits
if (old_surface != nullptr && old_surface->get_compatible_internal_format() == internal_fmt)
result->old_contents = old_surface;
result->set_cleared();
return result;
}
@@ -240,19 +241,25 @@ struct gl_render_target_traits
static void prepare_ds_for_drawing(void *, gl::render_target*) {}
static void prepare_ds_for_sampling(void *, gl::render_target*) {}
static void invalidate_rtt_surface_contents(void *, gl::render_target*) {}
static void invalidate_depth_surface_contents(void *, gl::render_target *ds) { ds->set_cleared(false); }
static void invalidate_rtt_surface_contents(void *, gl::render_target *rtt, gl::render_target* /*old*/, bool forced) { if (forced) rtt->set_cleared(false); }
static void invalidate_depth_surface_contents(void *, gl::render_target *ds, gl::render_target* /*old*/, bool) { ds->set_cleared(false); }
static
bool rtt_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_color_format format, size_t width, size_t height)
bool rtt_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_color_format format, size_t width, size_t height, bool check_refs=false)
{
if (check_refs) //TODO
return false;
auto internal_fmt = rsx::internals::sized_internal_format(format);
return rtt->get_compatible_internal_format() == internal_fmt && rtt->width() == width && rtt->height() == height;
}
static
bool ds_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_depth_format, size_t width, size_t height)
bool ds_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_depth_format, size_t width, size_t height, bool check_refs=false)
{
if (check_refs) //TODO
return false;
// TODO: check format
return rtt->width() == width && rtt->height() == height;
}
-3
View File
@@ -1,7 +1,4 @@
#pragma once
#include "stdafx.h"
#include "GLGSRender.h"
#include "GLTextureCache.h"
+3
View File
@@ -239,6 +239,8 @@ struct RSXFragmentProgram
u8 textures_alpha_kill[16];
u32 textures_zfunc[16];
bool valid;
rsx::texture_dimension_extended get_texture_dimension(u8 id) const
{
return (rsx::texture_dimension_extended)((texture_dimensions >> (id * 2)) & 0x3);
@@ -263,6 +265,7 @@ struct RSXFragmentProgram
, ctrl(0)
, unnormalized_coords(0)
, texture_dimensions(0)
, valid(false)
{
}
};
+19 -13
View File
@@ -974,9 +974,17 @@ namespace rsx
RSXFragmentProgram thread::get_current_fragment_program(std::function<std::tuple<bool, u16>(u32, fragment_texture&, bool)> get_surface_info) const
{
RSXFragmentProgram result = {};
u32 shader_program = rsx::method_registers.shader_program_address();
result.offset = shader_program & ~0x3;
result.addr = vm::base(rsx::get_address(result.offset, (shader_program & 0x3) - 1));
const u32 shader_program = rsx::method_registers.shader_program_address();
if (shader_program == 0)
return result;
const u32 program_location = (shader_program & 0x3) - 1;
const u32 program_offset = (shader_program & ~0x3);
result.offset = program_offset;
result.addr = vm::base(rsx::get_address(program_offset, program_location));
result.valid = true;
result.ctrl = rsx::method_registers.shader_control();
result.unnormalized_coords = 0;
result.front_back_color_enabled = !rsx::method_registers.two_side_light_en();
@@ -1174,26 +1182,26 @@ namespace rsx
if (packet.post_upload_func)
packet.post_upload_func(packet.dst_span.data(), packet.type, (u8)packet.vector_width, task.vertex_count);
_mm_sfence();
task.remaining_packets--;
current_job += step;
_mm_sfence();
}
_mm_mfence();
while (task.remaining_packets > 0 && !Emu.IsStopped())
{
std::this_thread::yield();
_mm_lfence();
std::this_thread::sleep_for(0us);
}
_mm_sfence();
task.ready_threads++;
_mm_sfence();
}
else
std::this_thread::sleep_for(0us);
//thread_ctrl::wait();
//busy_wait();
{
std::this_thread::yield();
}
}
});
}
@@ -1201,8 +1209,7 @@ namespace rsx
while (m_vertex_streaming_task.ready_threads != 0 && !Emu.IsStopped())
{
_mm_lfence();
busy_wait();
_mm_pause();
}
m_vertex_streaming_task.vertex_count = vertex_count;
@@ -1214,8 +1221,7 @@ namespace rsx
{
while (m_vertex_streaming_task.remaining_packets > 0 && !Emu.IsStopped())
{
_mm_lfence();
busy_wait();
_mm_pause();
}
m_vertex_streaming_task.packets.resize(0);
+226 -63
View File
@@ -677,6 +677,7 @@ VKGSRender::~VKGSRender()
m_buffer_view_to_clean.clear();
m_sampler_to_clean.clear();
m_framebuffer_to_clean.clear();
m_draw_fbo.reset();
//Render passes
for (auto &render_pass : m_render_passes)
@@ -874,17 +875,17 @@ void VKGSRender::begin_render_pass()
size_t idx = vk::get_render_pass_location(
vk::get_compatible_surface_format(rsx::method_registers.surface_color()).first,
vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, rsx::method_registers.surface_depth_fmt()),
(u8)vk::get_draw_buffers(rsx::method_registers.surface_color_target()).size());
(u8)m_draw_buffers_count);
VkRenderPass current_render_pass = m_render_passes[idx];
VkRenderPassBeginInfo rp_begin = {};
rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rp_begin.renderPass = current_render_pass;
rp_begin.framebuffer = m_framebuffer_to_clean.back()->value;
rp_begin.framebuffer = m_draw_fbo->value;
rp_begin.renderArea.offset.x = 0;
rp_begin.renderArea.offset.y = 0;
rp_begin.renderArea.extent.width = m_framebuffer_to_clean.back()->width();
rp_begin.renderArea.extent.height = m_framebuffer_to_clean.back()->height();
rp_begin.renderArea.extent.width = m_draw_fbo->width();
rp_begin.renderArea.extent.height = m_draw_fbo->height();
vkCmdBeginRenderPass(*m_current_command_buffer, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
render_pass_open = true;
@@ -941,10 +942,15 @@ void VKGSRender::end()
}
//Load program here since it is dependent on vertex state
load_program(is_instanced);
if (!load_program(is_instanced))
{
LOG_ERROR(RSX, "No valid program bound to pipeline. Skipping draw");
rsx::thread::end();
return;
}
std::chrono::time_point<steady_clock> program_stop = steady_clock::now();
m_setup_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(program_stop - program_start).count();
//m_setup_time += std::chrono::duration_cast<std::chrono::microseconds>(program_stop - program_start).count();
if (is_instanced)
{
@@ -999,14 +1005,14 @@ void VKGSRender::end()
{
auto surface = std::get<1>(rtt);
if (surface->dirty && surface->old_contents != nullptr)
if (surface->old_contents != nullptr)
copy_rtt_contents(surface);
}
}
if (auto ds = std::get<1>(m_rtts.m_bound_depth_stencil))
{
if (ds->dirty && ds->old_contents != nullptr)
if (ds->old_contents != nullptr)
copy_rtt_contents(ds);
}
}
@@ -1114,6 +1120,11 @@ void VKGSRender::end()
vkCmdBindPipeline(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline);
vkCmdBindDescriptorSets(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets, 0, nullptr);
//Clear any 'dirty' surfaces - possible is a recycled cache surface is used
std::vector<VkClearAttachment> buffers_to_clear;
buffers_to_clear.reserve(4);
const auto targets = vk::get_draw_buffers(rsx::method_registers.surface_color_target());
if (auto ds = std::get<1>(m_rtts.m_bound_depth_stencil))
{
if (ds->dirty)
@@ -1123,28 +1134,41 @@ void VKGSRender::end()
depth_clear_value.depthStencil.depth = 1.f;
depth_clear_value.depthStencil.stencil = 255;
VkClearRect clear_rect = { 0, 0, m_framebuffer_to_clean.back()->width(), m_framebuffer_to_clean.back()->height(), 0, 1 };
VkClearAttachment clear_desc = { ds->attachment_aspect_flag, 0, depth_clear_value };
vkCmdClearAttachments(*m_current_command_buffer, 1, &clear_desc, 1, &clear_rect);
buffers_to_clear.push_back(clear_desc);
ds->dirty = false;
}
}
std::optional<std::tuple<VkDeviceSize, VkIndexType> > index_info = std::get<2>(upload_info);
if (m_attrib_ring_info.mapped)
for (int index = 0; index < targets.size(); ++index)
{
wait_for_vertex_upload_task();
m_attrib_ring_info.unmap();
if (std::get<0>(m_rtts.m_bound_render_targets[index]) != 0 && std::get<1>(m_rtts.m_bound_render_targets[index])->dirty)
{
const u32 real_index = (index == 1 && targets.size() == 1) ? 0 : static_cast<u32>(index);
buffers_to_clear.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, real_index, {} });
std::get<1>(m_rtts.m_bound_render_targets[index])->dirty = false;
}
}
if (buffers_to_clear.size() > 0)
{
VkClearRect clear_rect = { 0, 0, m_draw_fbo->width(), m_draw_fbo->height(), 0, 1 };
vkCmdClearAttachments(*m_current_command_buffer, static_cast<u32>(buffers_to_clear.size()), buffers_to_clear.data(), 1, &clear_rect);
}
std::optional<std::tuple<VkDeviceSize, VkIndexType> > index_info = std::get<2>(upload_info);
std::chrono::time_point<steady_clock> vertex_end = steady_clock::now();
m_vertex_upload_time += std::chrono::duration_cast<std::chrono::microseconds>(vertex_end - textures_end).count();
if (!index_info)
{
vkCmdDraw(*m_current_command_buffer, std::get<1>(upload_info), 1, 0, 0);
const auto vertex_count = std::get<1>(upload_info);
vkCmdDraw(*m_current_command_buffer, vertex_count, 1, 0, 0);
m_last_vertex_count = vertex_count;
m_last_draw_indexed = false;
}
else
@@ -1175,6 +1199,22 @@ void VKGSRender::end()
copy_render_targets_to_dma_location();
m_draw_calls++;
if (g_cfg.video.overlay)
{
if (m_last_vertex_count < 1024)
m_uploads_small++;
else if (m_last_vertex_count < 2048)
m_uploads_1k++;
else if (m_last_vertex_count < 4096)
m_uploads_2k++;
else if (m_last_vertex_count < 8192)
m_uploads_4k++;
else if (m_last_vertex_count < 16384)
m_uploads_8k++;
else
m_uploads_16k++;
}
rsx::thread::end();
}
@@ -1251,8 +1291,6 @@ void VKGSRender::clear_surface(u32 mask)
u32 depth_stencil_mask = 0;
std::vector<VkClearAttachment> clear_descriptors;
std::vector<VkClearRect> clear_regions;
VkClearValue depth_stencil_clear_values, color_clear_values;
u16 scissor_x = rsx::method_registers.scissor_origin_x();
@@ -1260,8 +1298,8 @@ void VKGSRender::clear_surface(u32 mask)
u16 scissor_y = rsx::method_registers.scissor_origin_y();
u16 scissor_h = rsx::method_registers.scissor_height();
const u32 fb_width = m_framebuffer_to_clean.back()->width();
const u32 fb_height = m_framebuffer_to_clean.back()->height();
const u32 fb_width = m_draw_fbo->width();
const u32 fb_height = m_draw_fbo->height();
//clip region
std::tie(scissor_x, scissor_y, scissor_w, scissor_h) = rsx::clip_region<u16>(fb_width, fb_height, scissor_x, scissor_y, scissor_w, scissor_h, true);
@@ -1309,8 +1347,8 @@ void VKGSRender::clear_surface(u32 mask)
for (int index = 0; index < targets.size(); ++index)
{
clear_descriptors.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, (uint32_t)index, color_clear_values });
clear_regions.push_back(region);
const u32 real_index = (index == 1 && targets.size() == 1) ? 0 : static_cast<u32>(index);
clear_descriptors.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, real_index, color_clear_values });
}
for (auto &rtt : m_rtts.m_bound_render_targets)
@@ -1324,13 +1362,10 @@ void VKGSRender::clear_surface(u32 mask)
}
if (mask & 0x3)
{
clear_descriptors.push_back({ (VkImageAspectFlags)depth_stencil_mask, 0, depth_stencil_clear_values });
clear_regions.push_back(region);
}
begin_render_pass();
vkCmdClearAttachments(*m_current_command_buffer, (u32)clear_descriptors.size(), clear_descriptors.data(), (u32)clear_regions.size(), clear_regions.data());
vkCmdClearAttachments(*m_current_command_buffer, (u32)clear_descriptors.size(), clear_descriptors.data(), 1, &region);
if (mask & 0x3)
{
@@ -1392,6 +1427,12 @@ void VKGSRender::copy_render_targets_to_dma_location()
void VKGSRender::flush_command_queue(bool hard_sync)
{
if (m_attrib_ring_info.mapped)
{
wait_for_vertex_upload_task();
m_attrib_ring_info.unmap();
}
close_render_pass();
close_and_submit_command_buffer({}, m_current_command_buffer->submit_fence);
@@ -1472,7 +1513,7 @@ void VKGSRender::process_swap_request()
//Feed back damaged resources to the main texture cache for management...
//m_texture_cache.merge_dirty_textures(m_rtts.invalidated_resources);
m_rtts.invalidated_resources.clear();
m_rtts.free_invalidated();
m_texture_cache.flush();
if (g_cfg.video.invalidate_surface_cache_every_frame)
@@ -1480,7 +1521,13 @@ void VKGSRender::process_swap_request()
m_buffer_view_to_clean.clear();
m_sampler_to_clean.clear();
m_framebuffer_to_clean.clear();
m_framebuffer_to_clean.remove_if([](std::unique_ptr<vk::framebuffer_holder>& fbo)
{
if (fbo->deref_count >= 2) return true;
fbo->deref_count++;
return false;
});
if (g_cfg.video.overlay)
{
@@ -1545,8 +1592,10 @@ bool VKGSRender::load_program(bool fast_update)
return std::make_tuple(true, surface->native_pitch);
};
vertex_program = get_current_vertex_program();
fragment_program = get_current_fragment_program(rtt_lookup_func);
if (!fragment_program.valid) return false;
vertex_program = get_current_vertex_program();
vk::pipeline_props properties = {};
@@ -1691,7 +1740,7 @@ bool VKGSRender::load_program(bool fast_update)
size_t idx = vk::get_render_pass_location(
vk::get_compatible_surface_format(rsx::method_registers.surface_color()).first,
vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, rsx::method_registers.surface_depth_fmt()),
(u8)vk::get_draw_buffers(rsx::method_registers.surface_color_target()).size());
(u8)m_draw_buffers_count);
properties.render_pass = m_render_passes[idx];
@@ -1853,6 +1902,7 @@ void VKGSRender::prepare_rtts()
{
LOG_ERROR(RSX, "Invalid framebuffer setup, w=%d, h=%d", clip_width, clip_height);
framebuffer_status_valid = false;
return;
}
framebuffer_status_valid = true;
@@ -1863,6 +1913,35 @@ void VKGSRender::prepare_rtts()
const u32 surface_pitchs[] = { rsx::method_registers.surface_a_pitch(), rsx::method_registers.surface_b_pitch(),
rsx::method_registers.surface_c_pitch(), rsx::method_registers.surface_d_pitch() };
if (m_draw_fbo)
{
const u32 fb_width = m_draw_fbo->width();
const u32 fb_height = m_draw_fbo->height();
bool really_changed = false;
if (fb_width == clip_width && fb_height == clip_height)
{
for (u8 i = 0; i < rsx::limits::color_buffers_count; ++i)
{
if (m_surface_info[i].address != surface_addresses[i])
{
really_changed = true;
break;
}
}
if (!really_changed)
{
if (zeta_address == m_depth_surface_info.address)
{
//Nothing has changed, we're still using the same framebuffer
return;
}
}
}
}
m_rtts.prepare_render_target(&*m_current_command_buffer,
rsx::method_registers.surface_color(), rsx::method_registers.surface_depth_fmt(),
clip_width, clip_height,
@@ -1886,20 +1965,16 @@ void VKGSRender::prepare_rtts()
//Bind created rtts as current fbo...
std::vector<u8> draw_buffers = vk::get_draw_buffers(rsx::method_registers.surface_color_target());
std::vector<std::unique_ptr<vk::image_view>> fbo_images;
//Search old framebuffers for this same configuration
bool framebuffer_found = false;
std::vector<vk::image*> bound_images;
bound_images.reserve(5);
for (u8 index : draw_buffers)
{
vk::image *raw = std::get<1>(m_rtts.m_bound_render_targets[index]);
VkImageSubresourceRange subres = {};
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subres.baseArrayLayer = 0;
subres.baseMipLevel = 0;
subres.layerCount = 1;
subres.levelCount = 1;
fbo_images.push_back(std::make_unique<vk::image_view>(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres));
bound_images.push_back(std::get<1>(m_rtts.m_bound_render_targets[index]));
m_surface_info[index].address = surface_addresses[index];
m_surface_info[index].pitch = surface_pitchs[index];
@@ -1912,20 +1987,9 @@ void VKGSRender::prepare_rtts()
}
}
m_draw_buffers_count = static_cast<u32>(fbo_images.size());
if (std::get<1>(m_rtts.m_bound_depth_stencil) != nullptr)
if (std::get<0>(m_rtts.m_bound_depth_stencil) != 0)
{
vk::image *raw = (std::get<1>(m_rtts.m_bound_depth_stencil));
VkImageSubresourceRange subres = {};
subres.aspectMask = (rsx::method_registers.surface_depth_fmt() == rsx::surface_depth_format::z24s8) ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) : VK_IMAGE_ASPECT_DEPTH_BIT;
subres.baseArrayLayer = 0;
subres.baseMipLevel = 0;
subres.layerCount = 1;
subres.levelCount = 1;
fbo_images.push_back(std::make_unique<vk::image_view>(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres));
bound_images.push_back(std::get<1>(m_rtts.m_bound_depth_stencil));
m_depth_surface_info.address = zeta_address;
m_depth_surface_info.pitch = rsx::method_registers.surface_z_pitch();
@@ -1934,6 +1998,8 @@ void VKGSRender::prepare_rtts()
m_depth_surface_info.pitch = 0;
}
m_draw_buffers_count = static_cast<u32>(draw_buffers.size());
if (g_cfg.video.write_color_buffers)
{
for (u8 index : draw_buffers)
@@ -1942,7 +2008,7 @@ void VKGSRender::prepare_rtts()
const u32 range = m_surface_info[index].pitch * m_surface_info[index].height;
m_texture_cache.lock_memory_region(std::get<1>(m_rtts.m_bound_render_targets[index]), m_surface_info[index].address, range,
m_surface_info[index].width, m_surface_info[index].height);
m_surface_info[index].width, m_surface_info[index].height);
}
}
@@ -1959,10 +2025,59 @@ void VKGSRender::prepare_rtts()
}
}
size_t idx = vk::get_render_pass_location(vk::get_compatible_surface_format(rsx::method_registers.surface_color()).first, vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, rsx::method_registers.surface_depth_fmt()), (u8)draw_buffers.size());
VkRenderPass current_render_pass = m_render_passes[idx];
for (auto &fbo : m_framebuffer_to_clean)
{
if (fbo->matches(bound_images, clip_width, clip_height))
{
m_draw_fbo.swap(fbo);
m_draw_fbo->reset_refs();
framebuffer_found = true;
//LOG_ERROR(RSX, "Matching framebuffer exists, using that instead");
break;
}
}
m_framebuffer_to_clean.push_back(std::make_unique<vk::framebuffer>(*m_device, current_render_pass, clip_width, clip_height, std::move(fbo_images)));
if (!framebuffer_found)
{
std::vector<std::unique_ptr<vk::image_view>> fbo_images;
fbo_images.reserve(5);
for (u8 index : draw_buffers)
{
vk::image *raw = std::get<1>(m_rtts.m_bound_render_targets[index]);
VkImageSubresourceRange subres = {};
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subres.baseArrayLayer = 0;
subres.baseMipLevel = 0;
subres.layerCount = 1;
subres.levelCount = 1;
fbo_images.push_back(std::make_unique<vk::image_view>(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres));
}
if (std::get<1>(m_rtts.m_bound_depth_stencil) != nullptr)
{
vk::image *raw = (std::get<1>(m_rtts.m_bound_depth_stencil));
VkImageSubresourceRange subres = {};
subres.aspectMask = (rsx::method_registers.surface_depth_fmt() == rsx::surface_depth_format::z24s8) ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) : VK_IMAGE_ASPECT_DEPTH_BIT;
subres.baseArrayLayer = 0;
subres.baseMipLevel = 0;
subres.layerCount = 1;
subres.levelCount = 1;
fbo_images.push_back(std::make_unique<vk::image_view>(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres));
}
size_t idx = vk::get_render_pass_location(vk::get_compatible_surface_format(rsx::method_registers.surface_color()).first, vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, rsx::method_registers.surface_depth_fmt()), (u8)draw_buffers.size());
VkRenderPass current_render_pass = m_render_passes[idx];
if (m_draw_fbo)
m_framebuffer_to_clean.push_back(std::move(m_draw_fbo));
m_draw_fbo.reset(new vk::framebuffer_holder(*m_device, current_render_pass, clip_width, clip_height, std::move(fbo_images)));
}
}
@@ -1981,6 +2096,13 @@ void VKGSRender::flip(int buffer)
m_setup_time = 0;
m_vertex_upload_time = 0;
m_textures_upload_time = 0;
m_uploads_small = 0;
m_uploads_1k = 0;
m_uploads_2k = 0;
m_uploads_4k = 0;
m_uploads_8k = 0;
m_uploads_16k = 0;
}
return;
@@ -2060,7 +2182,7 @@ void VKGSRender::flip(int buffer)
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_present_image), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, range);
}
std::unique_ptr<vk::framebuffer> direct_fbo;
std::unique_ptr<vk::framebuffer_holder> direct_fbo;
std::vector<std::unique_ptr<vk::image_view>> swap_image_view;
if (g_cfg.video.overlay)
{
@@ -2082,9 +2204,24 @@ void VKGSRender::flip(int buffer)
size_t idx = vk::get_render_pass_location(m_swap_chain->get_surface_format(), VK_FORMAT_UNDEFINED, 1);
VkRenderPass single_target_pass = m_render_passes[idx];
swap_image_view.push_back(std::make_unique<vk::image_view>(*m_device, target_image, VK_IMAGE_VIEW_TYPE_2D, m_swap_chain->get_surface_format(), vk::default_component_map(), subres));
direct_fbo.reset(new vk::framebuffer(*m_device, single_target_pass, m_client_width, m_client_height, std::move(swap_image_view)));
for (auto It = m_framebuffer_to_clean.begin(); It != m_framebuffer_to_clean.end(); It++)
{
auto &fbo = *It;
if (fbo->attachments[0]->info.image == target_image)
{
direct_fbo.swap(fbo);
direct_fbo->reset_refs();
m_framebuffer_to_clean.erase(It);
break;
}
}
if (!direct_fbo)
{
swap_image_view.push_back(std::make_unique<vk::image_view>(*m_device, target_image, VK_IMAGE_VIEW_TYPE_2D, m_swap_chain->get_surface_format(), vk::default_component_map(), subres));
direct_fbo.reset(new vk::framebuffer_holder(*m_device, single_target_pass, m_client_width, m_client_height, std::move(swap_image_view)));
}
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 0, direct_fbo->width(), direct_fbo->height(), "draw calls: " + std::to_string(m_draw_calls) + ", instanced repeats: " + std::to_string(m_instanced_draws));
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 18, direct_fbo->width(), direct_fbo->height(), "draw call setup: " + std::to_string(m_setup_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 36, direct_fbo->width(), direct_fbo->height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
@@ -2092,10 +2229,29 @@ void VKGSRender::flip(int buffer)
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 72, direct_fbo->width(), direct_fbo->height(), "draw call execution: " + std::to_string(m_draw_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 90, direct_fbo->width(), direct_fbo->height(), "submit and flip: " + std::to_string(m_flip_time) + "us");
//Vertex upload statistics
u32 _small, _1k, _2k, _4k, _8k, _16k;
if (m_draw_calls > 0)
{
_small = m_uploads_small * 100 / m_draw_calls;
_1k = m_uploads_1k * 100 / m_draw_calls;
_2k = m_uploads_2k * 100 / m_draw_calls;
_4k = m_uploads_4k * 100 / m_draw_calls;
_8k = m_uploads_8k * 100 / m_draw_calls;
_16k = m_uploads_16k * 100 / m_draw_calls;
}
else
{
_small = _1k = _2k = _4k = _8k = _16k = 0;
}
std::string message = fmt::format("Vertex sizes: < 1k: %d%%, 1k+: %d%%, 2k+: %d%%, 4k+: %d%%, 8k+: %d%%, 16k+: %d%%", _small, _1k, _2k, _4k, _8k, _16k);
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 108, direct_fbo->width(), direct_fbo->height(), message);
vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subres);
m_framebuffer_to_clean.push_back(std::move(direct_fbo));
}
m_framebuffer_to_clean.push_back(std::move(direct_fbo));
queue_swap_request();
}
else
@@ -2193,4 +2349,11 @@ void VKGSRender::flip(int buffer)
m_setup_time = 0;
m_vertex_upload_time = 0;
m_textures_upload_time = 0;
m_uploads_small = 0;
m_uploads_1k = 0;
m_uploads_2k = 0;
m_uploads_4k = 0;
m_uploads_8k = 0;
m_uploads_16k = 0;
}
+12 -1
View File
@@ -149,15 +149,26 @@ private:
vk::descriptor_pool descriptor_pool;
std::vector<std::unique_ptr<vk::buffer_view> > m_buffer_view_to_clean;
std::vector<std::unique_ptr<vk::framebuffer> > m_framebuffer_to_clean;
std::vector<std::unique_ptr<vk::sampler> > m_sampler_to_clean;
std::list<std::unique_ptr<vk::framebuffer_holder> > m_framebuffer_to_clean;
std::unique_ptr<vk::framebuffer_holder> m_draw_fbo;
u32 m_client_width = 0;
u32 m_client_height = 0;
// Draw call stats
u32 m_draw_calls = 0;
u32 m_instanced_draws = 0;
// Vertex buffer usage stats
u32 m_uploads_small = 0;
u32 m_uploads_1k = 0;
u32 m_uploads_2k = 0;
u32 m_uploads_4k = 0;
u32 m_uploads_8k = 0;
u32 m_uploads_16k = 0;
// Timers
s64 m_setup_time = 0;
s64 m_vertex_upload_time = 0;
s64 m_textures_upload_time = 0;
+22 -4
View File
@@ -650,17 +650,17 @@ namespace vk
{
VkFramebuffer value;
VkFramebufferCreateInfo info = {};
std::vector<std::unique_ptr<vk::image_view>> attachements;
std::vector<std::unique_ptr<vk::image_view>> attachments;
u32 m_width = 0;
u32 m_height = 0;
public:
framebuffer(VkDevice dev, VkRenderPass pass, u32 width, u32 height, std::vector<std::unique_ptr<vk::image_view>> &&atts)
: m_device(dev), attachements(std::move(atts))
: m_device(dev), attachments(std::move(atts))
{
std::vector<VkImageView> image_view_array(attachements.size());
std::vector<VkImageView> image_view_array(attachments.size());
size_t i = 0;
for (const auto &att : attachements)
for (const auto &att : attachments)
{
image_view_array[i++] = att->value;
}
@@ -694,6 +694,24 @@ namespace vk
return m_height;
}
bool matches(std::vector<vk::image*> fbo_images, u32 width, u32 height)
{
if (m_width != width || m_height != height)
return false;
if (fbo_images.size() != attachments.size())
return false;
for (int n = 0; n < fbo_images.size(); ++n)
{
if (attachments[n]->info.image != fbo_images[n]->value ||
attachments[n]->info.format != fbo_images[n]->info.format)
return false;
}
return true;
}
framebuffer(const framebuffer&) = delete;
framebuffer(framebuffer&&) = delete;
+65 -10
View File
@@ -7,9 +7,16 @@
#include "../Common/TextureUtils.h"
#include "VKFormats.h"
struct ref_counted
{
u8 deref_count = 0;
void reset_refs() { deref_count = 0; }
};
namespace vk
{
struct render_target : public image
struct render_target : public image, public ref_counted
{
bool dirty = false;
u16 native_pitch = 0;
@@ -34,6 +41,17 @@ namespace vk
mipmaps, layers, samples, initial_layout, tiling, usage, image_flags)
{}
};
struct framebuffer_holder: public vk::framebuffer, public ref_counted
{
framebuffer_holder(VkDevice dev,
VkRenderPass pass,
u32 width, u32 height,
std::vector<std::unique_ptr<vk::image_view>> &&atts)
: framebuffer(dev, pass, width, height, std::move(atts))
{}
};
}
namespace rsx
@@ -147,6 +165,9 @@ namespace rsx
{
VkImageSubresourceRange range = vk::get_image_subresource_range(0, 0, 1, 1, surface->attachment_aspect_flag);
change_image_layout(*pcmd, surface, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, range);
//Reset deref count
surface->deref_count = 0;
}
static void prepare_rtt_for_sampling(vk::command_buffer* pcmd, vk::render_target *surface)
@@ -159,6 +180,9 @@ namespace rsx
{
VkImageSubresourceRange range = vk::get_image_subresource_range(0, 0, 1, 1, surface->attachment_aspect_flag);
change_image_layout(*pcmd, surface, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, range);
//Reset deref count
surface->deref_count = 0;
}
static void prepare_ds_for_sampling(vk::command_buffer* pcmd, vk::render_target *surface)
@@ -167,15 +191,26 @@ namespace rsx
change_image_layout(*pcmd, surface, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range);
}
static void invalidate_rtt_surface_contents(vk::command_buffer*, vk::render_target*) {}
static void invalidate_rtt_surface_contents(vk::command_buffer* pcmd, vk::render_target *rtt, vk::render_target *old_surface, bool forced)
{
if (forced)
{
rtt->old_contents = old_surface;
rtt->dirty = true;
}
}
static void invalidate_depth_surface_contents(vk::command_buffer* /*pcmd*/, vk::render_target *ds)
static void invalidate_depth_surface_contents(vk::command_buffer* /*pcmd*/, vk::render_target *ds, vk::render_target *old_surface, bool /*forced*/)
{
ds->dirty = true;
ds->old_contents = old_surface;
}
static bool rtt_has_format_width_height(const std::unique_ptr<vk::render_target> &rtt, surface_color_format format, size_t width, size_t height)
static bool rtt_has_format_width_height(const std::unique_ptr<vk::render_target> &rtt, surface_color_format format, size_t width, size_t height, bool check_refs=false)
{
if (check_refs && rtt->deref_count == 0) //Surface may still have read refs from data 'copy'
return false;
VkFormat fmt = vk::get_compatible_surface_format(format).first;
if (rtt->info.format == fmt &&
@@ -186,15 +221,24 @@ namespace rsx
return false;
}
static bool ds_has_format_width_height(const std::unique_ptr<vk::render_target> &ds, surface_depth_format, size_t width, size_t height)
static bool ds_has_format_width_height(const std::unique_ptr<vk::render_target> &ds, surface_depth_format format, size_t width, size_t height, bool check_refs=false)
{
// TODO: check format
//VkFormat fmt = vk::get_compatible_depth_surface_format(format);
if (check_refs && ds->deref_count == 0) //Surface may still have read refs from data 'copy'
return false;
if (//tex.get_format() == fmt &&
ds->info.extent.width == width &&
if (ds->info.extent.width == width &&
ds->info.extent.height == height)
return true;
{
//Check format
switch (ds->info.format)
{
case VK_FORMAT_D16_UNORM:
return format == surface_depth_format::z16;
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return format == surface_depth_format::z24s8;
}
}
return false;
}
@@ -237,5 +281,16 @@ namespace rsx
m_depth_stencil_storage.clear();
invalidated_resources.clear();
}
void free_invalidated()
{
invalidated_resources.remove_if([](std::unique_ptr<vk::render_target> &rtt)
{
if (rtt->deref_count >= 2) return true;
rtt->deref_count++;
return false;
});
}
};
}
+1 -1
View File
@@ -477,7 +477,7 @@ namespace
{
const auto &vbo = vertex_buffers[i];
if (vbo.which() == 0 && vertex_count > 128 && vertex_buffers.size() > 2 && rsxthr->vertex_upload_task_ready())
if (vbo.which() == 0 && vertex_count >= (u32)g_cfg.video.mt_vertex_upload_threshold && vertex_buffers.size() > 1 && rsxthr->vertex_upload_task_ready())
{
//vertex array buffer. We can thread this thing heavily
const auto& v = vbo.get<rsx::vertex_array_buffer>();
+22 -11
View File
@@ -146,8 +146,9 @@ void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std
static const vertex_reg_info reg_table[] =
{
{ "gl_Position", false, "dst_reg0", "", false },
{ "back_diff_color", true, "dst_reg1", "", false },
{ "back_spec_color", true, "dst_reg2", "", false },
//Technically these two are for both back and front
{ "back_diff_color", true, "dst_reg1", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_FRONTDIFFUSE },
{ "back_spec_color", true, "dst_reg2", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_FRONTSPECULAR },
{ "front_diff_color", true, "dst_reg3", "", false },
{ "front_spec_color", true, "dst_reg4", "", false },
{ "fog_c", true, "dst_reg5", ".xxxx", true, "", "", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_FOG },
@@ -159,15 +160,15 @@ static const vertex_reg_info reg_table[] =
{ "gl_ClipDistance[3]", false, "dst_reg6", ".y * userClipFactor[0].w", false, "userClipEnabled[0].w > 0", "0.5", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_UC3 },
{ "gl_ClipDistance[4]", false, "dst_reg6", ".z * userClipFactor[1].x", false, "userClipEnabled[1].x > 0", "0.5", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_UC4 },
{ "gl_ClipDistance[5]", false, "dst_reg6", ".w * userClipFactor[1].y", false, "userClipEnabled[1].y > 0", "0.5", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_UC5 },
{ "tc0", true, "dst_reg7", "", false },
{ "tc1", true, "dst_reg8", "", false },
{ "tc2", true, "dst_reg9", "", false },
{ "tc3", true, "dst_reg10", "", false },
{ "tc4", true, "dst_reg11", "", false },
{ "tc5", true, "dst_reg12", "", false },
{ "tc6", true, "dst_reg13", "", false },
{ "tc7", true, "dst_reg14", "", false },
{ "tc8", true, "dst_reg15", "", false },
{ "tc0", true, "dst_reg7", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX0 },
{ "tc1", true, "dst_reg8", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX1 },
{ "tc2", true, "dst_reg9", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX2 },
{ "tc3", true, "dst_reg10", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX3 },
{ "tc4", true, "dst_reg11", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX4 },
{ "tc5", true, "dst_reg12", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX5 },
{ "tc6", true, "dst_reg13", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX6 },
{ "tc7", true, "dst_reg14", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX7 },
{ "tc8", true, "dst_reg15", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX8 },
{ "tc9", true, "dst_reg6", "", false, "", "", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX9 } // In this line, dst_reg6 is correct since dst_reg goes from 0 to 15.
};
@@ -195,6 +196,16 @@ void VKVertexDecompilerThread::insertOutputs(std::stringstream & OS, const std::
const vk::varying_register_t &reg = vk::get_varying_register(i.name);
OS << "layout(location=" << reg.reg_location << ") out vec4 " << i.name << ";\n";
}
else
{
//Force some outputs to be declared even if unused so we can set default values
//NOTE: Registers that can be skept will not have their check_mask_value set
if (i.need_declare && (rsx_vertex_program.output_mask & i.check_mask_value) > 0)
{
const vk::varying_register_t &reg = vk::get_varying_register(i.name);
OS << "layout(location=" << reg.reg_location << ") out vec4 " << i.name << ";\n";
}
}
}
if (insert_back_diffuse && insert_front_diffuse)
+23 -13
View File
@@ -381,6 +381,7 @@ void Emulator::Load()
}
}
// Booting disc game
if (_cat == "DG" && bdvd_dir.empty())
{
// Mount /dev_bdvd/ if necessary
@@ -390,6 +391,7 @@ void Emulator::Load()
}
}
// Booting patch data
if (_cat == "GD" && bdvd_dir.empty())
{
// Load /dev_bdvd/ from game list if available
@@ -397,24 +399,19 @@ void Emulator::Load()
{
bdvd_dir = node.Scalar();
}
}
if (!bdvd_dir.empty() && fs::is_dir(bdvd_dir))
{
vfs::mount("dev_bdvd", bdvd_dir);
LOG_NOTICE(LOADER, "Disc: %s", vfs::get("/dev_bdvd"));
else
{
LOG_FATAL(LOADER, "Disc directory not found. Try to run the game from the actual game disc directory.");
}
}
// Check /dev_bdvd/
if (_cat == "DG")
if (!bdvd_dir.empty() && fs::is_dir(bdvd_dir))
{
fs::file sfb_file;
if (bdvd_dir.empty())
{
LOG_ERROR(LOADER, "Failed to mount disc directory for the disc game %s", m_title_id);
return;
}
vfs::mount("dev_bdvd", bdvd_dir);
LOG_NOTICE(LOADER, "Disc: %s", vfs::get("/dev_bdvd"));
if (!sfb_file.open(vfs::get("/dev_bdvd/PS3_DISC.SFB")) || sfb_file.size() < 4 || sfb_file.read<u32>() != ".SFB"_u32)
{
@@ -422,12 +419,25 @@ void Emulator::Load()
return;
}
const std::string bdvd_title_id = psf::get_string(psf::load_object(fs::file{vfs::get("/dev_bdvd/PS3_GAME/PARAM.SFO")}), "TITLE_ID");
if (bdvd_title_id != m_title_id)
{
LOG_ERROR(LOADER, "Unexpected disc directory for the disc game %s (found %s)", m_title_id, bdvd_title_id);
return;
}
// Store /dev_bdvd/ location
games[m_title_id] = bdvd_dir;
YAML::Emitter out;
out << games;
fs::file(fs::get_config_dir() + "/games.yml", fs::rewrite).write(out.c_str(), out.size());
}
else if (_cat == "DG" || _cat == "GD")
{
LOG_ERROR(LOADER, "Failed to mount disc directory for the disc game %s", m_title_id);
return;
}
// Check game updates
const std::string hdd0_boot = hdd0_game + m_title_id + "/USRDIR/EBOOT.BIN";
@@ -536,7 +546,7 @@ void Emulator::Load()
m_state = system_state::ready;
GetCallbacks().on_ready();
vm::ps3::init();
ppu_load_prx(ppu_prx, "");
ppu_load_prx(ppu_prx, m_path);
}
else if (spu_exec.open(elf_file) == elf_error::ok)
{
+8 -4
View File
@@ -272,6 +272,9 @@ struct cfg_root : cfg::node
cfg::_bool lower_spu_priority{this, "Lower SPU thread priority"};
cfg::_bool spu_debug{this, "SPU Debug"};
cfg::_int<32, 16384> max_spu_immediate_write_size{this, "Maximum immediate DMA write size", 16384}; // Maximum size that an SPU thread can write directly without posting to MFC
cfg::_int<0, 6> preferred_spu_threads{this, "Preferred SPU Threads", 0}; //Numnber of hardware threads dedicated to heavy simultaneous spu tasks
cfg::_int<0, 16> spu_delay_penalty{this, "SPU delay penalty", 3}; //Number of milliseconds to block a thread if a virtual 'core' isn't free
cfg::_bool spu_loop_detection{this, "SPU loop detection", false}; //Try to detect wait loops and trigger thread yield
cfg::_enum<lib_loading_type> lib_loading{this, "Lib Loader", lib_loading_type::automatic};
cfg::_bool hook_functions{this, "Hook static functions"};
@@ -320,12 +323,13 @@ struct cfg_root : cfg::node
cfg::_bool invalidate_surface_cache_every_frame{this, "Invalidate Cache Every Frame", true};
cfg::_bool strict_rendering_mode{this, "Strict Rendering Mode"};
cfg::_bool batch_instanced_geometry{this, "Batch Instanced Geometry", false};
cfg::_int<1, 16> vertex_upload_threads{ this, "Vertex Upload Threads", 1 };
cfg::_bool batch_instanced_geometry{this, "Batch Instanced Geometry", false}; //Avoid re-uploading geometry if the same draw command is repeated
cfg::_int<1, 16> vertex_upload_threads{ this, "Vertex Upload Threads", 1 }; //Max number of threads to use for parallel vertex processing
cfg::_int<32, 65536> mt_vertex_upload_threshold{ this, "Multithreaded Vertex Upload Threshold", 4096}; //Minimum vertex count to parallelize
cfg::_bool frame_skip_enabled{this, "Enable Frame Skip"};
cfg::_int<1, 8> consequtive_frames_to_draw{this, "Consequtive Frames Drawn", 1};
cfg::_int<1, 8> consequtive_frames_to_skip{this, "Consequtive Frames Skept", 1};
cfg::_int<1, 8> consequtive_frames_to_draw{this, "Consecutive Frames Drawn", 1};
cfg::_int<1, 8> consequtive_frames_to_skip{this, "Consecutive Frames Skept", 1};
struct node_d3d12 : cfg::node
{
Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

+8 -8
View File
@@ -7,13 +7,13 @@
},
"cpu": {
"PPU": {
"precise": "This is the most accurate Interpreter, but very slow to play games with.\nYou may try this as a last resort if you encounter odd bugs or crashes.\nIf unsure, use PPU Interpreter Fast or PPU recompiler (LLVM).",
"precise": "This is the most accurate Interpreter, but very slow to play games with.\nYou may try this as a last resort if you encounter odd bugs or crashes.\nIf unsure, use PPU Interpreter Fast or PPU Recompiler (LLVM).",
"fast": "This is the fastest interpreter.\nTrades accuracy for speed, and it very rarely breaks games even in comparison to the Precise option.\nTry this if PPU Recompiler (LLVM) fails.",
"LLVM": "Recompiles the game's executable once before running it for the first time.\nThis is by far the fastest option and should always be used.\nShould you face compatibility issues, fall back to one of the Interpreters and retry.\nIf unsure, use this option."
},
"SPU": {
"precise": "This is extremely slow but may fix broken graphics in some games.",
"fast": "This is slower than the SPU recompiler but significantly faster than the precise interpreter.\nGames rarely need this however.",
"fast": "This is slower than the SPU Recompiler but significantly faster than the precise interpreter.\nGames rarely need this however.",
"ASMJIT": "This is the fastest option with very good compatibility.\nIf unsure, use this option.",
"LLVM": "This doesn't exist (yet)"
},
@@ -46,7 +46,7 @@
"comboboxes": {
"renderBox": "Vulkan is the fastest renderer. OpenGL is the most accurate renderer.\nIf unsure, use Vulkan. Should you have any compatibility issues, fall back to OpenGL.\nDirectX 12 is deprecated and should never be used.",
"resBox": "Leave this on 1280x720, every PS3 game is compatible with this resolution.\nSet it to 1920x1080 only if supported by the game. Lower resolutions may work but are not practical.\nHowever rarely due to emulation bugs some games will only render at low resolutions like 480p.",
"graphicsAdapterBox": "On multi GPU systems select which GPU to use in RPCS3 when using Vulkan or DirectX 12.\nThis is not needed whe using OpenGL.",
"graphicsAdapterBox": "On multi GPU systems select which GPU to use in RPCS3 when using Vulkan or DirectX 12.\nThis is not needed when using OpenGL.",
"aspectBox": "Leave this on 16:9 unless you have a 4:3 monitor.\nAuto also works well especially if you use a resolution that is not 720p.",
"frameLimitBox": "Auto is the most compatible option.\nSome games can work with frame limit off, but it may cause bugs and crashes.\nSet to off if you get severe hitching and stuttering."
},
@@ -61,19 +61,19 @@
"stretchToDisplayArea": "Overrides the aspect ratio and stretches the image to the full display area."
},
"debug": {
"glLegacyBuffers": "Enables use of classic openGL buffers which allows capturing tools to work with rpcs3 e.g RenderDoc.\nIf unsure, don't use this option.",
"glLegacyBuffers": "Enables use of classic OpenGL buffers which allows capturing tools to work with RPCS3 e.g RenderDoc.\nIf unsure, don't use this option.",
"scrictModeRendering": "Enforces strict compliance to the API specification.\nMight result in degraded performance in some games.\nCan resolve rare cases of missing graphics and flickering.\nIf unsure, don't use this option.",
"forceHighpZ": "Only useful when debugging differences in GPU hardware.\nNot necessary for average users.\nIf unsure, don't use this option.",
"debugOutput": "Enables the selected API's inbuilt debugging functionality.\nWill cause severe performance degradation especially with vulkan.\nOnly useful for developers.\nIf unsure, don't use this option.",
"debugOutput": "Enables the selected API's inbuilt debugging functionality.\nWill cause severe performance degradation especially with Vulkan.\nOnly useful for developers.\nIf unsure, don't use this option.",
"debugOverlay": "Provides a graphical overlay of various debugging information.\nIf unsure, don't use this option.",
"logProg": "Dump game shaders to file. Only useful to developers.\nIf unsure, don't use this option."
}
},
"input": {
"padHandlerBox": "If you want to use the keyboard to control, select the Keyboard option.\nYou can change the button mappings in Configuration --> Controls.\nIf you have a DualShock 4, select DualShock 4.\nWindows: If you have an Xbox controller, or another compatible device, use XInput.\nOlder controllers such as PS2 controllers with an adapter usually work fine with mmjoystick.\nCheck button mappings in the Windows control panel.\n\nLinux: evdev input is WIP.",
"padHandlerBox": "If you want to use the keyboard to control, select the Keyboard option.\nYou can change the button mappings in Configuration --> Controls.\nIf you have a DualShock 4, select DualShock 4.\nWindows: If you have an Xbox controller, or another compatible device, use XInput.\nOlder controllers such as PS2 controllers with an adapter usually work fine with MMJoystick.\nCheck button mappings in the Windows control panel.\n\nLinux: evdev input is WIP.",
"keyboardHandlerBox": "Some games support native keyboard input.\nBasic will work in these cases.",
"mouseHandlerBox": "Some games support native mouse input.\nBasic will work in these cases.",
"useFakeCamera": "Camera support is not implemented, leave this on null.",
"cameraBox": "Camera support is not implemented, leave this on null.",
"cameraTypeBox": "Camera support is not implemented, leave this on unknown."
},
"network": {
@@ -83,4 +83,4 @@
"sysLangBox": "Some games may fail to boot if the system language is not available in the game itself.\nOther games will switch language automatically to what is selected here.\nIt is recommended leaving this on a language supported by the game.",
"enableHostRoot": "Required for some Homebrew.\nIf unsure, don't use this option."
}
}
}
+2 -2
View File
@@ -98,13 +98,13 @@ struct elf_phdr<en_t, u32>
template<template<typename T> class en_t, typename sz_t>
struct elf_prog final : elf_phdr<en_t, sz_t>
{
std::vector<char> bin;
std::vector<uchar> bin;
using base = elf_phdr<en_t, sz_t>;
elf_prog() = default;
elf_prog(u32 type, u32 flags, sz_t vaddr, sz_t memsz, sz_t align, std::vector<char>&& bin)
elf_prog(u32 type, u32 flags, sz_t vaddr, sz_t memsz, sz_t align, std::vector<uchar>&& bin)
: bin(std::move(bin))
{
base::p_type = type;
+8
View File
@@ -102,6 +102,13 @@
<ClCompile Include="..\Utilities\StrFmt.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Utilities\sysinfo.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Utilities\Thread.cpp" />
<ClCompile Include="..\Utilities\version.cpp" />
<ClCompile Include="..\Utilities\VirtualMemory.cpp" />
@@ -426,6 +433,7 @@
<ClInclude Include="..\Utilities\rXml.h" />
<ClInclude Include="..\Utilities\StrFmt.h" />
<ClInclude Include="..\Utilities\StrUtil.h" />
<ClInclude Include="..\Utilities\sysinfo.h" />
<ClInclude Include="..\Utilities\Thread.h" />
<ClInclude Include="..\Utilities\Timer.h" />
<ClInclude Include="..\Utilities\types.h" />
+6
View File
@@ -923,6 +923,9 @@
<ClCompile Include="Emu\Cell\lv2\sys_ss.cpp">
<Filter>Emu\Cell\lv2</Filter>
</ClCompile>
<ClCompile Include="..\Utilities\sysinfo.cpp">
<Filter>Utilities</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">
@@ -1777,5 +1780,8 @@
<ClInclude Include="..\Utilities\CRC.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="..\Utilities\sysinfo.h">
<Filter>Utilities</Filter>
</ClInclude>
</ItemGroup>
</Project>
+16 -3
View File
@@ -3,6 +3,8 @@
#include <QApplication>
#include <QCommandLineParser>
#include <QFileInfo>
#include <QTimer>
#include "rpcs3_app.h"
#ifdef _WIN32
@@ -17,6 +19,13 @@ int main(int argc, char** argv)
SetProcessDPIAware();
WSADATA wsa_data;
WSAStartup(MAKEWORD(2, 2), &wsa_data);
timeBeginPeriod(1);
atexit([]
{
timeEndPeriod(1);
WSACleanup();
});
#else
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
#endif
@@ -34,9 +43,13 @@ int main(int argc, char** argv)
if (parser.positionalArguments().length() > 0)
{
Emu.SetPath(sstr(parser.positionalArguments().at(0)));
Emu.Load();
Emu.Run();
// Ugly workaround
QTimer::singleShot(2, [path = sstr(QFileInfo(parser.positionalArguments().at(0)).canonicalFilePath())]
{
Emu.SetPath(path);
Emu.Load();
Emu.Run();
});
}
return app.exec();
+1 -1
View File
@@ -924,7 +924,6 @@
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<ClInclude Include="ds4_pad_handler.h" />
<ClInclude Include="game_list.h" />
<ClInclude Include="keyboard_pad_handler.h" />
<CustomBuild Include="rpcs3qt\gs_frame.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing gs_frame.h...</Message>
@@ -1202,6 +1201,7 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\game_list.h" />
<ClInclude Include="rpcs3qt\game_list_grid_delegate.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="rpcs3qt\gl_gs_frame.h" />
+1 -1
View File
@@ -514,7 +514,7 @@
<ClInclude Include="QTGeneratedFiles\ui_welcome_dialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="game_list.h">
<ClInclude Include="rpcs3qt\game_list.h">
<Filter>Gui</Filter>
</ClInclude>
</ItemGroup>
+18 -9
View File
@@ -1,7 +1,6 @@
#include "rpcs3_app.h"
#include "rpcs3qt/welcome_dialog.h"
#include "rpcs3qt/gui_settings.h"
#include "Emu/System.h"
#include "rpcs3qt/gs_frame.h"
@@ -56,8 +55,10 @@ void rpcs3_app::Init()
{
Emu.Init();
guiSettings.reset(new gui_settings());
// Create the main window
RPCS3MainWin = new main_window(nullptr);
RPCS3MainWin = new main_window(guiSettings, nullptr);
// Reset the pads -- see the method for why this is currently needed.
ResetPads();
@@ -68,15 +69,15 @@ void rpcs3_app::Init()
// Create connects to propagate events throughout Gui.
InitializeConnects();
RPCS3MainWin->Init();
setApplicationName("RPCS3");
RPCS3MainWin->show();
// Create the thumbnail toolbar after the main_window is created
RPCS3MainWin->CreateThumbnailToolbar();
// Slightly inneficient to make a gui_settings instance right here.
// But, I don't really feel like adding this as a dependency injection into RPCS3MainWin.
if (gui_settings().GetValue(GUI::ib_show_welcome).toBool())
if (guiSettings->GetValue(GUI::ib_show_welcome).toBool())
{
welcome_dialog* welcome = new welcome_dialog();
welcome->exec();
@@ -146,14 +147,22 @@ void rpcs3_app::InitializeCallbacks()
extern const std::unordered_map<video_resolution, std::pair<int, int>, value_hash<video_resolution>> g_video_out_resolution_map;
const auto size = g_video_out_resolution_map.at(g_cfg.video.resolution);
int w = size.first;
int h = size.second;
if (guiSettings->GetValue(GUI::gs_resize).toBool())
{
w = guiSettings->GetValue(GUI::gs_width).toInt();
h = guiSettings->GetValue(GUI::gs_height).toInt();
}
switch (video_renderer type = g_cfg.video.renderer)
{
case video_renderer::null: return std::make_unique<gs_frame>("Null", size.first, size.second, RPCS3MainWin->GetAppIcon());
case video_renderer::opengl: return std::make_unique<gl_gs_frame>(size.first, size.second, RPCS3MainWin->GetAppIcon());
case video_renderer::vulkan: return std::make_unique<gs_frame>("Vulkan", size.first, size.second, RPCS3MainWin->GetAppIcon());
case video_renderer::null: return std::make_unique<gs_frame>("Null", w, h, RPCS3MainWin->GetAppIcon());
case video_renderer::opengl: return std::make_unique<gl_gs_frame>(w, h, RPCS3MainWin->GetAppIcon());
case video_renderer::vulkan: return std::make_unique<gs_frame>("Vulkan", w, h, RPCS3MainWin->GetAppIcon());
#ifdef _MSC_VER
case video_renderer::dx12: return std::make_unique<gs_frame>("DirectX 12", size.first, size.second, RPCS3MainWin->GetAppIcon());
case video_renderer::dx12: return std::make_unique<gs_frame>("DirectX 12", w, h, RPCS3MainWin->GetAppIcon());
#endif
default: fmt::throw_exception("Invalid video renderer: %s" HERE, type);
}
+3
View File
@@ -15,6 +15,7 @@
#include "rpcs3qt/msg_dialog_frame.h"
#include "rpcs3qt/main_window.h"
#include "rpcs3qt/gui_settings.h"
#include <QApplication>
@@ -51,4 +52,6 @@ private:
std::shared_ptr<basic_mouse_handler> m_basicMouseHandler;
main_window* RPCS3MainWin;
std::shared_ptr<gui_settings> guiSettings;
};
+1 -1
View File
@@ -4,5 +4,5 @@
namespace rpcs3
{
const extern utils::version version{ 0, 0, 2, utils::version_type::alpha, 1, RPCS3_GIT_VERSION };
const extern utils::version version{ 0, 0, 3, utils::version_type::alpha, 1, RPCS3_GIT_VERSION };
}
+7 -5
View File
@@ -190,11 +190,6 @@ void debugger_frame::UpdateUI()
}
}
}
if (Emu.IsStopped())
{
g_breakpoints.clear();
}
}
void debugger_frame::UpdateUnitList()
@@ -388,6 +383,8 @@ void debugger_frame::Show_Val()
}
m_list->ShowAddr(CentrePc(pc));
}
diag->deleteLater();
}
void debugger_frame::Show_PC()
@@ -424,6 +421,11 @@ void debugger_frame::EnableButtons(bool enable)
m_btn_run->setEnabled(enable);
}
void debugger_frame::ClearBreakpoints()
{
g_breakpoints.clear();
}
debugger_list::debugger_list(debugger_frame* parent) : QListWidget(parent)
{
m_pc = 0;
+1
View File
@@ -73,6 +73,7 @@ public:
void DoUpdate();
void WriteRegs();
void EnableButtons(bool enable);
void ClearBreakpoints();
void OnUpdate();
+45 -20
View File
@@ -67,35 +67,28 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> settings, const R
m_Tool_Bar->setContextMenuPolicy(Qt::PreventContextMenu);
// ToolBar Actions
m_catActHDD = { new QAction(""), QIcon(":/Icons/hdd_blue.png"), QIcon(":/Icons/hdd_gray.png") };
m_catActHDD.action->setIcon(xgui_settings->GetValue(GUI::cat_hdd_game).toBool() ? m_catActHDD.colored : m_catActHDD.gray);
m_catActHDD = { new QAction(""), QIcon(":/Icons/hdd_blue.png"), QIcon(":/Icons/hdd_gray.png"), xgui_settings->GetValue(GUI::cat_hdd_game).toBool() };
m_catActHDD.action->setToolTip(tr("Show HDD Categories"));
m_catActDisc = { new QAction(""), QIcon(":/Icons/disc_blue.png"), QIcon(":/Icons/disc_gray.png") };
m_catActDisc.action->setIcon(xgui_settings->GetValue(GUI::cat_disc_game).toBool() ? m_catActDisc.colored : m_catActDisc.gray);
m_catActDisc = { new QAction(""), QIcon(":/Icons/disc_blue.png"), QIcon(":/Icons/disc_gray.png"), xgui_settings->GetValue(GUI::cat_disc_game).toBool() };
m_catActDisc.action->setToolTip(tr("Show Disc Categories"));
m_catActHome = { new QAction(""), QIcon(":/Icons/home_blue.png"), QIcon(":/Icons/home_gray.png") };
m_catActHome.action->setIcon(xgui_settings->GetValue(GUI::cat_home).toBool() ? m_catActHome.colored : m_catActHome.gray);
m_catActHome = { new QAction(""), QIcon(":/Icons/home_blue.png"), QIcon(":/Icons/home_gray.png"), xgui_settings->GetValue(GUI::cat_home).toBool() };
m_catActHome.action->setToolTip(tr("Show Home Categories"));
m_catActAudioVideo = { new QAction(""), QIcon(":/Icons/media_blue.png"), QIcon(":/Icons/media_gray.png") };
m_catActAudioVideo.action->setIcon(xgui_settings->GetValue(GUI::cat_audio_video).toBool() ? m_catActAudioVideo.colored : m_catActAudioVideo.gray);
m_catActAudioVideo = { new QAction(""), QIcon(":/Icons/media_blue.png"), QIcon(":/Icons/media_gray.png"), xgui_settings->GetValue(GUI::cat_audio_video).toBool() };
m_catActAudioVideo.action->setToolTip(tr("Show Audio/Video Categories"));
m_catActGameData = { new QAction(""), QIcon(":/Icons/data_blue.png"), QIcon(":/Icons/data_gray.png") };
m_catActGameData.action->setIcon(xgui_settings->GetValue(GUI::cat_game_data).toBool() ? m_catActGameData.colored : m_catActGameData.gray);
m_catActGameData = { new QAction(""), QIcon(":/Icons/data_blue.png"), QIcon(":/Icons/data_gray.png"), xgui_settings->GetValue(GUI::cat_game_data).toBool() };
m_catActGameData.action->setToolTip(tr("Show GameData Categories"));
m_catActUnknown = { new QAction(""), QIcon(":/Icons/unknown_blue.png"), QIcon(":/Icons/unknown_gray.png") };
m_catActUnknown.action->setIcon(xgui_settings->GetValue(GUI::cat_unknown).toBool() ? m_catActUnknown.colored : m_catActUnknown.gray);
m_catActUnknown = { new QAction(""), QIcon(":/Icons/unknown_blue.png"), QIcon(":/Icons/unknown_gray.png"), xgui_settings->GetValue(GUI::cat_unknown).toBool() };
m_catActUnknown.action->setToolTip(tr("Show Unknown Categories"));
m_catActOther = { new QAction(""), QIcon(":/Icons/other_blue.png"), QIcon(":/Icons/other_gray.png") };
m_catActOther.action->setIcon(xgui_settings->GetValue(GUI::cat_other).toBool() ? m_catActOther.colored : m_catActOther.gray);
m_catActOther = { new QAction(""), QIcon(":/Icons/other_blue.png"), QIcon(":/Icons/other_gray.png"), xgui_settings->GetValue(GUI::cat_other).toBool() };
m_catActOther.action->setToolTip(tr("Show Other Categories"));
m_categoryButtons = { m_catActHDD , m_catActDisc, m_catActHome, m_catActAudioVideo, m_catActGameData, m_catActUnknown, m_catActOther };
m_categoryButtons = { &m_catActHDD , &m_catActDisc, &m_catActHome, &m_catActAudioVideo, &m_catActGameData, &m_catActUnknown, &m_catActOther };
m_categoryActs = new QActionGroup(m_Tool_Bar);
m_categoryActs->addAction(m_catActHDD.action);
@@ -108,11 +101,9 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> settings, const R
m_categoryActs->setEnabled(m_isListLayout);
m_modeActList = { new QAction(""), QIcon(":/Icons/list_blue.png"), QIcon(":/Icons/list_gray.png") };
m_modeActList.action->setIcon(m_isListLayout ? m_modeActList.colored : m_modeActList.gray);
m_modeActList.action->setToolTip(tr("Enable List Mode"));
m_modeActGrid = { new QAction(""), QIcon(":/Icons/grid_blue.png"), QIcon(":/Icons/grid_gray.png") };
m_modeActGrid.action->setIcon(m_isListLayout ? m_modeActGrid.gray : m_modeActGrid.colored);
m_modeActGrid.action->setToolTip(tr("Enable Grid Mode"));
m_modeActs = new QActionGroup(m_Tool_Bar);
@@ -122,6 +113,9 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> settings, const R
// Search Bar
m_Search_Bar = new QLineEdit(m_Tool_Bar);
m_Search_Bar->setPlaceholderText(tr("Search games ..."));
m_Search_Bar->setMinimumWidth(m_Tool_Bar->height() * 5);
m_Search_Bar->setFrame(false);
m_Search_Bar->setStyleSheet("background:transparent;");
connect(m_Search_Bar, &QLineEdit::textChanged, [this](const QString& text) {
m_searchText = text;
Refresh();
@@ -131,7 +125,7 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> settings, const R
m_Slider_Size = new QSlider(Qt::Horizontal , m_Tool_Bar);
m_Slider_Size->setRange(0, GUI::gl_icon_size.size() - 1);
m_Slider_Size->setSliderPosition(icon_size_index);
m_Slider_Size->setFixedWidth(100);
m_Slider_Size->setFixedWidth(m_Tool_Bar->height() * 3);
m_Tool_Bar->addWidget(m_Search_Bar);
m_Tool_Bar->addWidget(new QLabel(" "));
@@ -152,6 +146,8 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> settings, const R
m_Game_Dock->addToolBar(m_Tool_Bar);
setWidget(m_Game_Dock);
RepaintToolBarIcons();
bool showText = (m_Icon_Size_Str != GUI::gl_icon_key_small && m_Icon_Size_Str != GUI::gl_icon_key_tiny);
m_xgrid = new game_list_grid(m_Icon_Size, m_Icon_Color, m_Margin_Factor, m_Text_Factor, showText);
@@ -565,6 +561,7 @@ void game_list_frame::doubleClickedSlot(const QModelIndex& index)
{
LOG_SUCCESS(LOADER, "Boot from gamelist per doubleclick: done");
RequestAddRecentGame(q_string_pair(qstr(Emu.GetBoot()), qstr("[" + m_game_data[i].info.serial + "] " + m_game_data[i].info.name)));
Refresh(true);
}
}
else
@@ -782,7 +779,8 @@ bool game_list_frame::GetToolBarVisible()
void game_list_frame::SetCategoryActIcon(const int& id, const bool& active)
{
m_categoryButtons.at(id).action->setIcon(active ? m_categoryButtons.at(id).colored : m_categoryButtons.at(id).gray);
m_categoryButtons.at(id)->action->setIcon(active ? m_categoryButtons.at(id)->colored : m_categoryButtons.at(id)->gray);
m_categoryButtons.at(id)->isActive = active;
}
void game_list_frame::SetSearchText(const QString& text)
@@ -791,6 +789,33 @@ void game_list_frame::SetSearchText(const QString& text)
Refresh();
}
void game_list_frame::RepaintToolBarIcons()
{
QColor newColor = xgui_settings->GetValue(GUI::gl_toolIconColor).value<QColor>();
m_catActHDD.colored = gui_settings::colorizedIcon(QIcon(":/Icons/hdd_blue.png"), GUI::gl_tool_icon_color, newColor, true);
m_catActDisc.colored = gui_settings::colorizedIcon(QIcon(":/Icons/disc_blue.png"), GUI::gl_tool_icon_color, newColor, true);
m_catActHome.colored = gui_settings::colorizedIcon(QIcon(":/Icons/home_blue.png"), GUI::gl_tool_icon_color, newColor);
m_catActAudioVideo.colored = gui_settings::colorizedIcon(QIcon(":/Icons/media_blue.png"), GUI::gl_tool_icon_color, newColor, true);
m_catActGameData.colored = gui_settings::colorizedIcon(QIcon(":/Icons/data_blue.png"), GUI::gl_tool_icon_color, newColor, true);
m_catActUnknown.colored = gui_settings::colorizedIcon(QIcon(":/Icons/unknown_blue.png"), GUI::gl_tool_icon_color, newColor, true);
m_catActOther.colored = gui_settings::colorizedIcon(QIcon(":/Icons/other_blue.png"), GUI::gl_tool_icon_color, newColor);
for (const auto& butt : m_categoryButtons)
{
butt->action->setIcon(butt->isActive ? butt->colored : butt->gray);
}
m_modeActList.colored = gui_settings::colorizedIcon(QIcon(":/Icons/list_blue.png"), GUI::gl_tool_icon_color, newColor);
m_modeActList.action->setIcon(m_isListLayout ? m_modeActList.colored : m_modeActList.gray);
m_modeActGrid.colored = gui_settings::colorizedIcon(QIcon(":/Icons/grid_blue.png"), GUI::gl_tool_icon_color, newColor);
m_modeActGrid.action->setIcon(m_isListLayout ? m_modeActGrid.gray : m_modeActGrid.colored);
m_Slider_Size->setStyleSheet(QString("QSlider::handle:horizontal{ background: rgba(%1, %2, %3, %4); }")
.arg(newColor.red()).arg(newColor.green()).arg(newColor.blue()).arg(newColor.alpha()));
}
void game_list_frame::closeEvent(QCloseEvent *event)
{
QDockWidget::closeEvent(event);
@@ -863,7 +888,7 @@ void game_list_frame::PopulateGameGrid(uint maxCols, const QSize& image_size, co
std::string selected_item = CurrentSelectionIconPath();
delete m_xgrid;
m_xgrid->deleteLater();
bool showText = m_Icon_Size_Str != GUI::gl_icon_key_small && m_Icon_Size_Str != GUI::gl_icon_key_tiny;
+3 -1
View File
@@ -162,6 +162,7 @@ typedef struct Tool_Bar_Button
QAction* action;
QIcon colored;
QIcon gray;
bool isActive;
};
class game_list_frame : public QDockWidget {
@@ -194,6 +195,7 @@ public Q_SLOTS:
void SetToolBarVisible(const bool& showToolBar);
void SetCategoryActIcon(const int& id, const bool& active);
void SetSearchText(const QString& text);
void RepaintToolBarIcons();
private Q_SLOTS:
void Boot(int row);
@@ -256,7 +258,7 @@ private:
Tool_Bar_Button m_catActUnknown;
Tool_Bar_Button m_catActOther;
QList<Tool_Bar_Button> m_categoryButtons;
QList<Tool_Bar_Button*> m_categoryButtons;
QActionGroup* m_categoryActs;
+47
View File
@@ -78,6 +78,53 @@ q_pair_list gui_settings::Var2List(const QVariant& var)
return list;
}
QIcon gui_settings::colorizedIcon(const QIcon& icon, const QColor& oldColor, const QColor& newColor, bool useSpecialMasks)
{
QPixmap pixmap = icon.pixmap(icon.availableSizes().at(0));
QBitmap mask = pixmap.createMaskFromColor(oldColor, Qt::MaskOutColor);
pixmap.fill(newColor);
pixmap.setMask(mask);
// special masks for disc icon and others
if (useSpecialMasks)
{
auto saturatedColor = [](const QColor& col, float sat /* must be < 1 */)
{
int r = col.red() + sat * (255 - col.red());
int g = col.green() + sat * (255 - col.green());
int b = col.blue() + sat * (255 - col.blue());
return QColor(r, g, b, col.alpha());
};
QColor colorS1(Qt::white);
QPixmap pixmapS1 = icon.pixmap(icon.availableSizes().at(0));
QBitmap maskS1 = pixmapS1.createMaskFromColor(colorS1, Qt::MaskOutColor);
pixmapS1.fill(colorS1);
pixmapS1.setMask(maskS1);
QColor colorS2(0, 173, 246, 255);
QPixmap pixmapS2 = icon.pixmap(icon.availableSizes().at(0));
QBitmap maskS2 = pixmapS2.createMaskFromColor(colorS2, Qt::MaskOutColor);
pixmapS2.fill(saturatedColor(newColor, 0.6f));
pixmapS2.setMask(maskS2);
QColor colorS3(0, 132, 244, 255);
QPixmap pixmapS3 = icon.pixmap(icon.availableSizes().at(0));
QBitmap maskS3 = pixmapS3.createMaskFromColor(colorS3, Qt::MaskOutColor);
pixmapS3.fill(saturatedColor(newColor, 0.3f));
pixmapS3.setMask(maskS3);
QPainter painter(&pixmap);
painter.drawPixmap(QPoint(0, 0), pixmapS1);
painter.drawPixmap(QPoint(0, 0), pixmapS2);
painter.drawPixmap(QPoint(0, 0), pixmapS3);
painter.end();
}
return QIcon(pixmap);
}
void gui_settings::SetValue(const GUI_SAVE& entry, const QVariant& value)
{
settings.beginGroup(entry.key);
+22 -3
View File
@@ -7,6 +7,7 @@
#include <QVariant>
#include <QSize>
#include <QColor>
#include <QBitmap>
typedef struct GUI_SAVE
{
@@ -51,9 +52,12 @@ namespace GUI
const QString logger = "Logger";
const QString meta = "Meta";
const QString fs = "FileSystem";
const QString gs_frame = "GSFrame";
const QColor mw_tool_bar_color = QColor(227, 227, 227, 255);
const QColor gl_icon_color = QColor(209, 209, 209, 255);
const QColor mw_tool_bar_color = QColor(227, 227, 227, 255);
const QColor mw_tool_icon_color = QColor(64, 64, 64, 255);
const QColor gl_icon_color = QColor(209, 209, 209, 255);
const QColor gl_tool_icon_color = QColor(0, 100, 231, 255);
const GUI_SAVE rg_freeze = GUI_SAVE(main_window, "recentGamesFrozen", false);
const GUI_SAVE rg_entries = GUI_SAVE(main_window, "recentGamesNames", QVariant::fromValue(q_pair_list()));
@@ -74,6 +78,7 @@ namespace GUI
const GUI_SAVE mw_gamelist = GUI_SAVE( main_window, "gamelistVisible", true );
const GUI_SAVE mw_toolBarVisible = GUI_SAVE( main_window, "toolBarVisible", true );
const GUI_SAVE mw_toolBarColor = GUI_SAVE( main_window, "toolBarColor", mw_tool_bar_color);
const GUI_SAVE mw_toolIconColor = GUI_SAVE( main_window, "toolIconColor", mw_tool_icon_color);
const GUI_SAVE mw_geometry = GUI_SAVE( main_window, "geometry", QByteArray() );
const GUI_SAVE mw_windowState = GUI_SAVE( main_window, "windowState", QByteArray() );
const GUI_SAVE mw_mwState = GUI_SAVE( main_window, "wwState", QByteArray() );
@@ -82,7 +87,7 @@ namespace GUI
const GUI_SAVE cat_disc_game = GUI_SAVE( game_list, "categoryVisibleDiscGame", true );
const GUI_SAVE cat_home = GUI_SAVE( game_list, "categoryVisibleHome", true );
const GUI_SAVE cat_audio_video = GUI_SAVE( game_list, "categoryVisibleAudioVideo", true );
const GUI_SAVE cat_game_data = GUI_SAVE( game_list, "categoryVisibleGameData", true );
const GUI_SAVE cat_game_data = GUI_SAVE( game_list, "categoryVisibleGameData", false );
const GUI_SAVE cat_unknown = GUI_SAVE( game_list, "categoryVisibleUnknown", true );
const GUI_SAVE cat_other = GUI_SAVE( game_list, "categoryVisibleOther", true );
@@ -95,6 +100,7 @@ namespace GUI
const GUI_SAVE gl_textFactor = GUI_SAVE( game_list, "textFactor", (qreal) 2.0 );
const GUI_SAVE gl_marginFactor = GUI_SAVE( game_list, "marginFactor", (qreal) 0.09 );
const GUI_SAVE gl_toolBarVisible = GUI_SAVE( game_list, "toolBarVisible", false);
const GUI_SAVE gl_toolIconColor = GUI_SAVE( game_list, "toolIconColor", gl_tool_icon_color);
const GUI_SAVE fs_emulator_dir_list = GUI_SAVE(fs, "emulator_dir_list", QStringList());
const GUI_SAVE fs_dev_hdd0_list = GUI_SAVE(fs, "dev_hdd0_list", QStringList());
@@ -108,6 +114,10 @@ namespace GUI
const GUI_SAVE m_currentConfig = GUI_SAVE(meta, "currentConfig", QObject::tr("CurrentSettings"));
const GUI_SAVE m_currentStylesheet = GUI_SAVE(meta, "currentStylesheet", QObject::tr("default"));
const GUI_SAVE gs_resize = GUI_SAVE(gs_frame, "resize", false);
const GUI_SAVE gs_width = GUI_SAVE(gs_frame, "width", 1280);
const GUI_SAVE gs_height = GUI_SAVE(gs_frame, "height", 720);
}
/** Class for GUI settings..
@@ -142,6 +152,15 @@ public:
QStringList GetStylesheetEntries();
QStringList GetGameListCategoryFilters();
/**
Creates a custom colored QIcon based on another QIcon
@param icon the icon to colorize
@param oldColor the current color of icon
@param newColor the desired color for the new icon
@param useSpecialMasks only used for icons with white parts and disc game icon
*/
static QIcon colorizedIcon(const QIcon& icon, const QColor& oldColor, const QColor& newColor, bool useSpecialMasks = false);
public Q_SLOTS:
void Reset(bool removeMeta = false);
+2 -1
View File
@@ -2,6 +2,7 @@
#include <stdafx.h>
#include "rpcs3_version.h"
#include "Utilities/sysinfo.h"
#include <QMenu>
#include <QActionGroup>
@@ -39,7 +40,7 @@ struct gui_listener : logs::listener
read = new packet;
last = new packet;
read->next = last.load();
last->msg = fmt::format("RPCS3 v%s\n", rpcs3::version.to_string());
last->msg = fmt::format("RPCS3 v%s\n%s\n", rpcs3::version.to_string(), utils::get_system_info());
// Self-registration
logs::listener::add(this);
+111 -34
View File
@@ -1,4 +1,4 @@

#include <QApplication>
#include <QMenuBar>
#include <QMessageBox>
@@ -41,22 +41,37 @@
#include "Utilities/StrUtil.h"
#include "rpcs3_version.h"
#include "Utilities/sysinfo.h"
#include "ui_main_window.h"
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
main_window::main_window(QWidget *parent) : QMainWindow(parent), m_sys_menu_opened(false), ui(new Ui::main_window)
main_window::main_window(std::shared_ptr<gui_settings> guiSettings, QWidget *parent) : QMainWindow(parent), guiSettings(guiSettings), m_sys_menu_opened(false), ui(new Ui::main_window)
{
}
main_window::~main_window()
{
}
auto Pause = []()
{
if (Emu.IsReady()) Emu.Run();
else if (Emu.IsPaused()) Emu.Resume();
else if (Emu.IsRunning()) Emu.Pause();
else if (!Emu.GetPath().empty()) Emu.Load();
};
/* An init method is used so that RPCS3App can create the necessary connects before calling init (specifically the stylesheet connect).
* Simplifies logic a bit.
*/
void main_window::Init()
{
ui->setupUi(this);
guiSettings.reset(new gui_settings());
// Load Icons: This needs to happen before any actions or buttons are created
icon_play = QIcon(":/Icons/play.png");
icon_pause = QIcon(":/Icons/pause.png");
icon_stop = QIcon(":/Icons/stop.png");
icon_restart = QIcon(":/Icons/restart.png");
RepaintToolBarIcons();
appIcon = QIcon(":/rpcs3.ico");
// add toolbar widgets (crappy Qt designer is not able to)
@@ -77,6 +92,13 @@ main_window::main_window(QWidget *parent) : QMainWindow(parent), m_sys_menu_open
ui->toolBar->addSeparator();
ui->toolBar->addWidget(ui->searchBar);
// for highdpi resize toolbar icons and height dynamically
// choose factors to mimic Gui-Design in main_window.ui
const int toolBarHeight = menuBar()->sizeHint().height() * 2;
ui->toolBar->setIconSize(QSize(toolBarHeight, toolBarHeight));
ui->sizeSliderContainer->setFixedWidth(toolBarHeight * 5);
ui->sizeSlider->setFixedHeight(toolBarHeight * 0.625f);
CreateActions();
CreateDockWindows();
@@ -87,26 +109,19 @@ main_window::main_window(QWidget *parent) : QMainWindow(parent), m_sys_menu_open
setWindowTitle(QString::fromStdString("RPCS3 v" + rpcs3::version.to_string()));
!appIcon.isNull() ? setWindowIcon(appIcon) : LOG_WARNING(GENERAL, "AppImage could not be loaded!");
QTimer::singleShot(1, [=]() {
// Need to have this happen fast, but not now because connects aren't created yet.
// So, a tricky balance in terms of time but this works.
RequestGlobalStylesheetChange(guiSettings->GetCurrentStylesheetPath());
ConfigureGuiFromSettings(true);
});
}
RequestGlobalStylesheetChange(guiSettings->GetCurrentStylesheetPath());
ConfigureGuiFromSettings(true);
if (!utils::has_ssse3())
{
QMessageBox::critical(this, "SSSE3 Error (with three S, not two)",
"Your system does not meet the minimum requirements needed to run RPCS3.\n"
"Your CPU does not support SSSE3 (with three S, not two).\n");
main_window::~main_window()
{
std::exit(EXIT_FAILURE);
}
}
auto Pause = []()
{
if (Emu.IsReady()) Emu.Run();
else if (Emu.IsPaused()) Emu.Resume();
else if (Emu.IsRunning()) Emu.Pause();
else if (!Emu.GetPath().empty()) Emu.Load();
};
void main_window::CreateThumbnailToolbar()
{
#ifdef _WIN32
@@ -240,6 +255,7 @@ void main_window::BootElf()
const std::string serial = Emu.GetTitleID().empty() ? "" : "[" + Emu.GetTitleID() + "] ";
AddRecentAction(q_string_pair(qstr(Emu.GetBoot()), qstr(serial + Emu.GetTitle())));
gameListFrame->Refresh(true);
}
}
@@ -276,6 +292,7 @@ void main_window::BootGame()
const std::string serial = Emu.GetTitleID().empty() ? "" : "[" + Emu.GetTitleID() + "] ";
AddRecentAction(q_string_pair(qstr(Emu.GetBoot()), qstr(serial + Emu.GetTitle())));
gameListFrame->Refresh(true);
}
}
@@ -444,7 +461,19 @@ void main_window::InstallPup()
updatefilenames.begin(), updatefilenames.end(), [](std::string s) { return s.find("dev_flash_") == std::string::npos; }),
updatefilenames.end());
QProgressDialog pdlg(tr("Installing firmware ... please wait ..."), tr("Cancel"), 0, static_cast<int>(updatefilenames.size()), this);
std::string version_string = pup.get_file(0x100).to_string();
version_string.erase(version_string.find('\n'));
const std::string cur_version = "4.81";
if (version_string < cur_version &&
QMessageBox::question(this, tr("RPCS3 Firmware Installer"), tr("Old firmware detected.\nThe newest firmware version is %1 and you are trying to install version %2\nContinue installation?").arg(QString::fromStdString(cur_version), QString::fromStdString(version_string)),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::No)
{
return;
}
QProgressDialog pdlg(tr("Installing firmware version %1\nPlease wait...").arg(QString::fromStdString(version_string)), tr("Cancel"), 0, static_cast<int>(updatefilenames.size()), this);
pdlg.setWindowTitle(tr("RPCS3 Firmware Installer"));
pdlg.setWindowModality(Qt::WindowModal);
pdlg.setFixedSize(500, pdlg.height());
@@ -530,7 +559,7 @@ void main_window::InstallPup()
if (progress > 0)
{
LOG_SUCCESS(GENERAL, "Successfully installed PS3 firmware.");
LOG_SUCCESS(GENERAL, "Successfully installed PS3 firmware version %s.", version_string);
guiSettings->ShowInfoBox(GUI::ib_pup_success, tr("Success!"), tr("Successfully installed PS3 firmware and LLE Modules!"), this);
#ifdef _WIN32
@@ -612,6 +641,53 @@ void main_window::SaveWindowState()
gameListFrame->SaveSettings();
}
void main_window::RepaintToolBarIcons()
{
QColor newColor = guiSettings->GetValue(GUI::mw_toolIconColor).value<QColor>();
icon_play = gui_settings::colorizedIcon(QIcon(":/Icons/play.png"), GUI::mw_tool_icon_color, newColor);
icon_pause = gui_settings::colorizedIcon(QIcon(":/Icons/pause.png"), GUI::mw_tool_icon_color, newColor);
icon_stop = gui_settings::colorizedIcon(QIcon(":/Icons/stop.png"), GUI::mw_tool_icon_color, newColor);
icon_restart = gui_settings::colorizedIcon(QIcon(":/Icons/restart.png"), GUI::mw_tool_icon_color, newColor);
icon_fullscreen_on = gui_settings::colorizedIcon(QIcon(":/Icons/fullscreen.png"), GUI::mw_tool_icon_color, newColor);
icon_fullscreen_off = gui_settings::colorizedIcon(QIcon(":/Icons/fullscreen_invert.png"), GUI::mw_tool_icon_color, newColor);
ui->toolbar_config->setIcon(gui_settings::colorizedIcon(QIcon(":/Icons/configure.png"), GUI::mw_tool_icon_color, newColor));
ui->toolbar_controls->setIcon(gui_settings::colorizedIcon(QIcon(":/Icons/controllers.png"), GUI::mw_tool_icon_color, newColor));
ui->toolbar_disc->setIcon(gui_settings::colorizedIcon(QIcon(":/Icons/disc.png"), GUI::mw_tool_icon_color, newColor));
ui->toolbar_grid->setIcon(gui_settings::colorizedIcon(QIcon(":/Icons/grid.png"), GUI::mw_tool_icon_color, newColor));
ui->toolbar_list->setIcon(gui_settings::colorizedIcon(QIcon(":/Icons/list.png"), GUI::mw_tool_icon_color, newColor));
ui->toolbar_refresh->setIcon(gui_settings::colorizedIcon(QIcon(":/Icons/refresh.png"), GUI::mw_tool_icon_color, newColor));
ui->toolbar_snap->setIcon(gui_settings::colorizedIcon(QIcon(":/Icons/screenshot.png"), GUI::mw_tool_icon_color, newColor));
ui->toolbar_sort->setIcon(gui_settings::colorizedIcon(QIcon(":/Icons/sort.png"), GUI::mw_tool_icon_color, newColor));
ui->toolbar_stop->setIcon(gui_settings::colorizedIcon(QIcon(":/Icons/stop.png"), GUI::mw_tool_icon_color, newColor));
if (Emu.IsRunning())
{
ui->toolbar_start->setIcon(icon_pause);
}
else if (Emu.IsStopped() && !Emu.GetPath().empty())
{
ui->toolbar_start->setIcon(icon_restart);
}
else
{
ui->toolbar_start->setIcon(icon_play);
}
if (isFullScreen())
{
ui->toolbar_fullscreen->setIcon(icon_fullscreen_on);
}
else
{
ui->toolbar_fullscreen->setIcon(icon_fullscreen_off);
}
ui->sizeSlider->setStyleSheet(QString("QSlider::handle:horizontal{ background: rgba(%1, %2, %3, %4); }")
.arg(newColor.red()).arg(newColor.green()).arg(newColor.blue()).arg(newColor.alpha()));
}
void main_window::OnEmuRun()
{
debuggerFrame->EnableButtons(true);
@@ -653,6 +729,8 @@ void main_window::OnEmuPause()
void main_window::OnEmuStop()
{
debuggerFrame->EnableButtons(false);
debuggerFrame->ClearBreakpoints();
ui->sysPauseAct->setText(Emu.IsReady() ? tr("&Start\tCtrl+E") : tr("&Resume\tCtrl+E"));
ui->sysPauseAct->setIcon(icon_play);
#ifdef _WIN32
@@ -791,6 +869,7 @@ void main_window::BootRecentAction(const QAction* act)
{
LOG_SUCCESS(LOADER, "Boot from Recent List: done");
AddRecentAction(q_string_pair(qstr(Emu.GetBoot()), nam));
gameListFrame->Refresh(true);
}
};
@@ -971,6 +1050,8 @@ void main_window::CreateConnects()
connect(&dlg, &settings_dialog::GuiSettingsSaveRequest, this, &main_window::SaveWindowState);
connect(&dlg, &settings_dialog::GuiSettingsSyncRequest, [=]() {ConfigureGuiFromSettings(true); });
connect(&dlg, &settings_dialog::GuiStylesheetRequest, this, &main_window::RequestGlobalStylesheetChange);
connect(&dlg, &settings_dialog::ToolBarRepaintRequest, this, &main_window::RepaintToolBarIcons);
connect(&dlg, &settings_dialog::ToolBarRepaintRequest, gameListFrame, &game_list_frame::RepaintToolBarIcons);
connect(&dlg, &settings_dialog::accepted, [this](){
gameListFrame->LoadSettings();
QColor tbc = guiSettings->GetValue(GUI::mw_toolBarColor).value<QColor>();
@@ -1110,10 +1191,6 @@ void main_window::CreateConnects()
gameListFrame->SetListMode(isList);
categoryVisibleActGroup->setEnabled(isList);
});
connect(ui->toolBar, &QToolBar::visibilityChanged, [=](bool checked) {
ui->showToolBarAct->setChecked(checked);
guiSettings->SetValue(GUI::mw_toolBarVisible, checked);
});
connect(ui->toolbar_disc, &QAction::triggered, this, &main_window::BootGame);
connect(ui->toolbar_refresh, &QAction::triggered, [=]() { gameListFrame->Refresh(true); });
connect(ui->toolbar_stop, &QAction::triggered, [=]() { Emu.Stop(); });
@@ -1123,12 +1200,12 @@ void main_window::CreateConnects()
if (isFullScreen())
{
showNormal();
ui->toolbar_fullscreen->setIcon(QIcon(":/Icons/fullscreen.png"));
ui->toolbar_fullscreen->setIcon(icon_fullscreen_on);
}
else
{
showFullScreen();
ui->toolbar_fullscreen->setIcon(QIcon(":/Icons/fullscreen_invert.png"));
ui->toolbar_fullscreen->setIcon(icon_fullscreen_off);
}
});
connect(ui->toolbar_controls, &QAction::triggered, [=]() { pad_settings_dialog dlg(this); dlg.exec(); });
@@ -1307,7 +1384,7 @@ void main_window::mouseDoubleClickEvent(QMouseEvent *event)
if (event->button() == Qt::LeftButton)
{
showNormal();
ui->toolbar_fullscreen->setIcon(QIcon(":/Icons/fullscreen.png"));
ui->toolbar_fullscreen->setIcon(icon_fullscreen_on);
}
}
}
+6 -1
View File
@@ -37,6 +37,8 @@ class main_window : public QMainWindow
QIcon icon_pause;
QIcon icon_stop;
QIcon icon_restart;
QIcon icon_fullscreen_on;
QIcon icon_fullscreen_off;
#ifdef _WIN32
QIcon icon_thumb_play;
@@ -54,7 +56,8 @@ class main_window : public QMainWindow
#endif
public:
explicit main_window(QWidget *parent = 0);
explicit main_window(std::shared_ptr<gui_settings> guiSettings, QWidget *parent = 0);
void Init();
~main_window();
void CreateThumbnailToolbar();
QIcon GetAppIcon();
@@ -77,6 +80,7 @@ private Q_SLOTS:
void DecryptSPRXLibraries();
void SaveWindowState();
void RepaintToolBarIcons();
protected:
void closeEvent(QCloseEvent *event) override;
@@ -87,6 +91,7 @@ private:
void CreateDockWindows();
void ConfigureGuiFromSettings(bool configureAll = false);
void EnableMenus(bool enabled);
void keyPressEvent(QKeyEvent *keyEvent);
void mouseDoubleClickEvent(QMouseEvent *event);
+7 -10
View File
@@ -117,7 +117,7 @@ margin-left:14px;</string>
<item>
<widget class="QSlider" name="sizeSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -128,12 +128,6 @@ margin-left:14px;</string>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">QSlider::handle:horizontal {
background: #404040;
}
</string>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -154,6 +148,9 @@ margin-left:14px;</string>
<height>21</height>
</rect>
</property>
<property name="contextMenuPolicy">
<enum>Qt::PreventContextMenu</enum>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
@@ -434,7 +431,7 @@ QLineEdit { background-color: rgba(227, 227, 227, 255); }</string>
</action>
<action name="confPadAct">
<property name="text">
<string>Controls</string>
<string>Keyboard</string>
</property>
<property name="toolTip">
<string>Configure Controls</string>
@@ -816,10 +813,10 @@ QLineEdit { background-color: rgba(227, 227, 227, 255); }</string>
<normaloff>:/Icons/controls.png</normaloff>:/Icons/controls.png</iconset>
</property>
<property name="text">
<string>controls</string>
<string>Keyboard</string>
</property>
<property name="toolTip">
<string>Configure controls</string>
<string>Configure keyboard</string>
</property>
</action>
<action name="toolbar_snap">
+1 -1
View File
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>Configure Controls</string>
<string>Configure keyboard</string>
</property>
<property name="windowIcon">
<iconset resource="../resources.qrc">
+117 -38
View File
@@ -8,9 +8,11 @@
#include <QJsonObject>
#include <QJsonDocument>
#include <QColorDialog>
#include <QSpinBox>
#include <QApplication>
#include <QDesktopWidget>
#include "settings_dialog.h"
#include "emu_settings.h"
#include "ui_settings_dialog.h"
@@ -81,6 +83,7 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> xSettings, const
}
std::vector<std::string> selected_ls = std::vector<std::string>(selectedlle.begin(), selectedlle.end());
xemu_settings->SaveSelectedLibraries(selected_ls);
ToolBarRepaintRequest();
});
connect(ui->okButton, &QAbstractButton::clicked, xemu_settings.get(), &emu_settings::SaveSettings);
connect(ui->okButton, &QAbstractButton::clicked, this, &QDialog::accept);
@@ -109,23 +112,32 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> xSettings, const
ui->ppu_fast->setToolTip(json_cpu_ppu["fast"].toString());
ui->ppu_llvm->setToolTip(json_cpu_ppu["LLVM"].toString());
QButtonGroup *ppuBG = new QButtonGroup(this);
ppuBG->addButton(ui->ppu_precise, (int)ppu_decoder_type::precise);
ppuBG->addButton(ui->ppu_fast, (int)ppu_decoder_type::fast);
ppuBG->addButton(ui->ppu_llvm, (int)ppu_decoder_type::llvm);
{ // PPU Stuff
QString selectedPPU = qstr(xemu_settings->GetSetting(emu_settings::PPUDecoder));
for (const auto& button : ui->ppuBG->buttons())
QStringList ppu_list = xemu_settings->GetSettingOptions(emu_settings::PPUDecoder);
for (int i = 0; i < ppu_list.count(); i++)
{
QString current = button->text();
button->setCheckable(true);
if (current == selectedPPU)
ppuBG->button(i)->setText(ppu_list[i]);
if (ppu_list[i] == selectedPPU)
{
button->setChecked(true);
ppuBG->button(i)->setChecked(true);
}
#ifndef LLVM_AVAILABLE
if (current == "Recompiler (LLVM)")
if (ppu_list[i].toLower().contains("llvm"))
{
button->setEnabled(false);
ppuBG->button(i)->setEnabled(false);
}
#endif
connect(button, &QAbstractButton::pressed, [=]() {xemu_settings->SetSetting(emu_settings::PPUDecoder, sstr(current)); });
connect(ppuBG->button(i), &QAbstractButton::pressed, [=]() {xemu_settings->SetSetting(emu_settings::PPUDecoder, sstr(ppu_list[i])); });
}
}
@@ -135,21 +147,26 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> xSettings, const
ui->spu_asmjit->setToolTip(json_cpu_spu["ASMJIT"].toString());
ui->spu_llvm->setToolTip(json_cpu_spu["LLVM"].toString());
QButtonGroup *spuBG = new QButtonGroup(this);
spuBG->addButton(ui->spu_precise, (int)spu_decoder_type::precise);
spuBG->addButton(ui->spu_fast, (int)spu_decoder_type::fast);
spuBG->addButton(ui->spu_asmjit, (int)spu_decoder_type::asmjit);
spuBG->addButton(ui->spu_llvm, (int)spu_decoder_type::llvm);
{ // Spu stuff
QString selectedSPU = qstr(xemu_settings->GetSetting(emu_settings::SPUDecoder));
for (const auto& button : ui->spuBG->buttons())
QStringList spu_list = xemu_settings->GetSettingOptions(emu_settings::SPUDecoder);
for (int i = 0; i < spu_list.count(); i++)
{
QString current = button->text();
if (current == "Recompiler (LLVM)")
spuBG->button(i)->setText(spu_list[i]);
if (spu_list[i] == selectedSPU)
{
button->setEnabled(false);
spuBG->button(i)->setChecked(true);
}
button->setCheckable(true);
if (current == selectedSPU)
{
button->setChecked(true);
}
connect(button, &QAbstractButton::pressed, [=]() {xemu_settings->SetSetting(emu_settings::SPUDecoder, sstr(current)); });
connect(spuBG->button(i), &QAbstractButton::pressed, [=]() {xemu_settings->SetSetting(emu_settings::SPUDecoder, sstr(spu_list[i])); });
}
}
@@ -161,22 +178,25 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> xSettings, const
// creating this in ui file keeps scrambling the order...
QButtonGroup *libModeBG = new QButtonGroup(this);
libModeBG->addButton(ui->lib_auto, 0);
libModeBG->addButton(ui->lib_manu, 1);
libModeBG->addButton(ui->lib_both, 2);
libModeBG->addButton(ui->lib_lv2, 3);
libModeBG->addButton(ui->lib_auto, (int)lib_loading_type::automatic);
libModeBG->addButton(ui->lib_manu, (int)lib_loading_type::manual);
libModeBG->addButton(ui->lib_both, (int)lib_loading_type::both);
libModeBG->addButton(ui->lib_lv2, (int)lib_loading_type::liblv2only);
{// Handle lib loading options
QString selectedLib = qstr(xemu_settings->GetSetting(emu_settings::LibLoadOptions));
for (const auto& button : libModeBG->buttons())
QStringList libmode_list = xemu_settings->GetSettingOptions(emu_settings::LibLoadOptions);
for (int i = 0; i < libmode_list.count(); i++)
{
QString current = button->text();
button->setCheckable(true);
if (current == selectedLib)
libModeBG->button(i)->setText(libmode_list[i]);
if (libmode_list[i] == selectedLib)
{
button->setChecked(true);
libModeBG->button(i)->setChecked(true);
}
connect(button, &QAbstractButton::pressed, [=]() {xemu_settings->SetSetting(emu_settings::LibLoadOptions, sstr(current)); });
connect(libModeBG->button(i), &QAbstractButton::pressed, [=]() {xemu_settings->SetSetting(emu_settings::LibLoadOptions, sstr(libmode_list[i])); });
}
}
@@ -225,7 +245,7 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> xSettings, const
auto l_OnLibButtonClicked = [=](int ind)
{
if (ind == 1 || ind == 2)
if (ind == (int)lib_loading_type::manual || ind == (int)lib_loading_type::both)
{
ui->searchBox->setEnabled(true);
ui->lleList->setEnabled(true);
@@ -583,10 +603,8 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> xSettings, const
xemu_settings->EnhanceComboBox(ui->cameraTypeBox, emu_settings::CameraType);
ui->cameraTypeBox->setToolTip(json_input["cameraTypeBox"].toString());
// Checkboxes
xemu_settings->EnhanceCheckBox(ui->useFakeCamera, emu_settings::Camera);
ui->useFakeCamera->setToolTip(json_input["useFakeCamera"].toString());
xemu_settings->EnhanceComboBox(ui->cameraBox, emu_settings::Camera);
ui->cameraBox->setToolTip(json_input["cameraBox"].toString());
// _____ _ _______ _
// / ____| | | |__ __| | |
@@ -685,8 +703,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> xSettings, const
connect(ui->pb_apply_stylesheet, &QAbstractButton::clicked, this, &settings_dialog::OnApplyStylesheet);
connect(ui->pb_open_folder, &QAbstractButton::clicked, [=]() {QDesktopServices::openUrl(xgui_settings->GetSettingsDir()); });
connect(ui->cb_show_welcome, &QCheckBox::clicked, [=](bool val) {xgui_settings->SetValue(GUI::ib_show_welcome, val); });
auto colorDialog = [&](const GUI_SAVE& color, const QString& title){
QColorDialog dlg(xgui_settings->GetValue(color).value<QColor>(), this);
auto colorDialog = [&](const GUI_SAVE& color, const QString& title, QPushButton *button){
QColor oldColor = xgui_settings->GetValue(color).value<QColor>();
QColorDialog dlg(oldColor, this);
dlg.setWindowTitle(title);
dlg.setOptions(QColorDialog::ShowAlphaChannel);
for (int i = 0; i < dlg.customCount(); i++)
@@ -700,10 +719,70 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> xSettings, const
xgui_settings->SetCustomColor(i, dlg.customColor(i));
}
xgui_settings->SetValue(color, dlg.selectedColor());
button->setIcon(gui_settings::colorizedIcon(button->icon(), oldColor, dlg.selectedColor(), true));
}
};
connect(ui->pb_icon_color, &QAbstractButton::clicked, [=]() { colorDialog(GUI::gl_iconColor, "Choose icon color"); });
connect(ui->pb_tool_bar_color, &QAbstractButton::clicked, [=]() { colorDialog(GUI::mw_toolBarColor, "Choose tool bar color"); });
connect(ui->pb_gl_icon_color, &QAbstractButton::clicked, [=]() { colorDialog(GUI::gl_iconColor, tr("Choose gamelist icon color"), ui->pb_gl_icon_color); });
connect(ui->pb_gl_tool_icon_color, &QAbstractButton::clicked, [=]() { colorDialog(GUI::gl_toolIconColor, tr("Choose gamelist tool icon color"), ui->pb_gl_tool_icon_color); });
connect(ui->pb_tool_bar_color, &QAbstractButton::clicked, [=]() { colorDialog(GUI::mw_toolBarColor, tr("Choose tool bar color"), ui->pb_tool_bar_color); });
connect(ui->pb_tool_icon_color, &QAbstractButton::clicked, [=]() { colorDialog(GUI::mw_toolIconColor, tr("Choose tool icon color"), ui->pb_tool_icon_color); });
// colorize preview icons
auto addColoredIcon = [&](QPushButton *button, const QColor& color, const QIcon& icon = QIcon(), const QColor& iconColor = QColor()){
QLabel* text = new QLabel(button->text());
text->setAlignment(Qt::AlignCenter);
text->setAttribute(Qt::WA_TransparentForMouseEvents, true);
if (icon.isNull())
{
QPixmap pixmap(100, 100);
pixmap.fill(color);
button->setIcon(pixmap);
}
else
{
button->setIcon(gui_settings::colorizedIcon(icon, iconColor, color));
}
button->setText("");
button->setStyleSheet("text-align:left;");
button->setLayout(new QGridLayout);
button->layout()->setContentsMargins(0, 0, 0, 0);
button->layout()->addWidget(text);
};
addColoredIcon(ui->pb_gl_icon_color, xgui_settings->GetValue(GUI::gl_iconColor).value<QColor>());
addColoredIcon(ui->pb_tool_bar_color, xgui_settings->GetValue(GUI::mw_toolBarColor).value<QColor>());
addColoredIcon(ui->pb_gl_tool_icon_color, xgui_settings->GetValue(GUI::gl_toolIconColor).value<QColor>(), QIcon(":/Icons/home_blue.png"), GUI::gl_tool_icon_color);
addColoredIcon(ui->pb_tool_icon_color, xgui_settings->GetValue(GUI::mw_toolIconColor).value<QColor>(), QIcon(":/Icons/stop.png"), GUI::mw_tool_icon_color);
bool enableButtons = xgui_settings->GetValue(GUI::gs_resize).toBool();
ui->gs_resizeOnBoot->setChecked(enableButtons);
ui->gs_width->setEnabled(enableButtons);
ui->gs_height->setEnabled(enableButtons);
QRect rec = QApplication::desktop()->screenGeometry();
int width = xgui_settings->GetValue(GUI::gs_width).toInt();
int height = xgui_settings->GetValue(GUI::gs_height).toInt();
const int max_width = rec.width();
const int max_height = rec.height();
ui->gs_width->setValue(width < max_width ? width : max_width);
ui->gs_height->setValue(height < max_height ? height : max_height);
connect(ui->gs_resizeOnBoot, &QCheckBox::clicked, [=](bool val) {
xgui_settings->SetValue(GUI::gs_resize, val);
ui->gs_width->setEnabled(val);
ui->gs_height->setEnabled(val);
});
connect(ui->gs_width, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), [=](int w) {
int width = QApplication::desktop()->screenGeometry().width();
w = w > width ? width : w;
ui->gs_width->setValue(w);
xgui_settings->SetValue(GUI::gs_width, w);
});
connect(ui->gs_height, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), [=](int h) {
int height = QApplication::desktop()->screenGeometry().height();
h = h > height ? height : h;
ui->gs_height->setValue(h);
xgui_settings->SetValue(GUI::gs_height, h);
});
AddConfigs();
AddStylesheets();
+1
View File
@@ -24,6 +24,7 @@ Q_SIGNALS:
void GuiSettingsSyncRequest();
void GuiStylesheetRequest(const QString& path);
void GuiSettingsSaveRequest();
void ToolBarRepaintRequest();
private Q_SLOTS:
void OnBackupCurrentConfig();
void OnApplyConfig();
+136 -38
View File
@@ -58,9 +58,6 @@
<property name="text">
<string>Interpreter (precise)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">ppuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -68,9 +65,6 @@
<property name="text">
<string>Interpreter (fast)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">ppuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -78,9 +72,6 @@
<property name="text">
<string>Recompiler (LLVM)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">ppuBG</string>
</attribute>
</widget>
</item>
</layout>
@@ -97,9 +88,6 @@
<property name="text">
<string>Interpreter (precise)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">spuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -107,9 +95,6 @@
<property name="text">
<string>Interpreter (fast)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">spuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -117,9 +102,6 @@
<property name="text">
<string>Recompiler (ASMJIT)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">spuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -130,9 +112,6 @@
<property name="text">
<string>Recompiler (LLVM)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">spuBG</string>
</attribute>
</widget>
</item>
</layout>
@@ -232,7 +211,7 @@
</sizepolicy>
</property>
<property name="text">
<string>Load automatic and manual libraries</string>
<string>Load automatic and manual selection</string>
</property>
</widget>
</item>
@@ -774,11 +753,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_31">
<item>
<widget class="QCheckBox" name="useFakeCamera">
<property name="text">
<string>Use Fake Camera</string>
</property>
</widget>
<widget class="QComboBox" name="cameraBox"/>
</item>
</layout>
</widget>
@@ -1027,7 +1002,7 @@
<attribute name="title">
<string>Emulator</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_46">
<layout class="QVBoxLayout" name="verticalLayout_47">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_17">
<item>
@@ -1151,7 +1126,123 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_19">
<item>
<widget class="QWidget" name="widget_6" native="true"/>
<widget class="QWidget" name="widget_6" native="true">
<layout class="QVBoxLayout" name="verticalLayout_46">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Viewport</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_50">
<item>
<widget class="QCheckBox" name="gs_resizeOnBoot">
<property name="text">
<string>Resize game window on boot</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_20">
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Width</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_49">
<item>
<widget class="QSpinBox" name="gs_width">
<property name="accelerated">
<bool>true</bool>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectToNearestValue</enum>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Height</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_48">
<item>
<widget class="QSpinBox" name="gs_height">
<property name="frame">
<bool>true</bool>
</property>
<property name="accelerated">
<bool>true</bool>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectToNearestValue</enum>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>29</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
@@ -1167,12 +1258,26 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="pb_icon_color">
<widget class="QPushButton" name="pb_tool_icon_color">
<property name="text">
<string>Main window tool icons</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pb_gl_icon_color">
<property name="text">
<string>Gamelist icons</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pb_gl_tool_icon_color">
<property name="text">
<string>Gamelist tool icons</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -1183,12 +1288,9 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<width>0</width>
<height>0</height>
</size>
</property>
@@ -1260,8 +1362,4 @@
<include location="../resources.qrc"/>
</resources>
<connections/>
<buttongroups>
<buttongroup name="ppuBG"/>
<buttongroup name="spuBG"/>
</buttongroups>
</ui>
+74 -8
View File
@@ -17,9 +17,18 @@ namespace {
L"xinput9_1_0.dll"
};
inline u16 ConvertAxis(SHORT value)
inline u16 Clamp0To255(f32 input)
{
return static_cast<u16>((value + 32768l) >> 8);
if (input > 255.f)
return 255;
else if (input < 0.f)
return 0;
else return static_cast<u16>(input);
}
inline u16 ConvertAxis(float value)
{
return static_cast<u16>((value + 1.0)*(255.0 / 2.0));
}
}
@@ -45,7 +54,7 @@ void xinput_pad_handler::Init(const u32 max_connect)
{
xinputGetState = reinterpret_cast<PFN_XINPUTGETSTATE>(GetProcAddress(library, "XInputGetState"));
}
xinputSetState = reinterpret_cast<PFN_XINPUTSETSTATE>(GetProcAddress(library, "XInputSetState"));
if (xinputEnable && xinputGetState && xinputSetState)
@@ -137,6 +146,27 @@ void xinput_pad_handler::Close()
m_pads.clear();
}
std::tuple<u16, u16> xinput_pad_handler::ConvertToSquirclePoint(u16 inX, u16 inY)
{
// convert inX and Y to a (-1, 1) vector;
const f32 x = (inX - 127) / 127.f;
const f32 y = ((inY - 127) / 127.f);
// compute angle and len of given point to be used for squircle radius
const f32 angle = std::atan2(y, x);
const f32 r = std::sqrt(std::pow(x, 2.f) + std::pow(y, 2.f));
// now find len/point on the given squircle from our current angle and radius in polar coords
// https://thatsmaths.com/2016/07/14/squircles/
const f32 newLen = (1 + std::pow(std::sin(2 * angle), 2.f) / 8.f) * r;
// we now have len and angle, convert to cartisian
const int newX = Clamp0To255(((newLen * std::cos(angle)) + 1) * 127);
const int newY = Clamp0To255(((newLen * std::sin(angle)) + 1) * 127);
return std::tuple<u16, u16>(newX, newY);
}
DWORD xinput_pad_handler::ThreadProcedure()
{
// holds internal controller state change
@@ -152,7 +182,7 @@ DWORD xinput_pad_handler::ThreadProcedure()
{
auto & pad = m_pads[i];
result = (* xinputGetState)(i, &state);
result = (*xinputGetState)(i, &state);
switch (result)
{
case ERROR_DEVICE_NOT_CONNECTED:
@@ -181,10 +211,46 @@ DWORD xinput_pad_handler::ThreadProcedure()
pad.m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_pressed = state.Gamepad.bRightTrigger > 0;
pad.m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_value = state.Gamepad.bRightTrigger;
pad.m_sticks[0].m_value = ConvertAxis(state.Gamepad.sThumbLX);
pad.m_sticks[1].m_value = 255 - ConvertAxis(state.Gamepad.sThumbLY);
pad.m_sticks[2].m_value = ConvertAxis(state.Gamepad.sThumbRX);
pad.m_sticks[3].m_value = 255 - ConvertAxis(state.Gamepad.sThumbRY);
float LX, LY, RX, RY;
LX = state.Gamepad.sThumbLX;
LY = state.Gamepad.sThumbLY;
RX = state.Gamepad.sThumbRX;
RY = state.Gamepad.sThumbRY;
auto normalize_input = [](float& X, float& Y, float deadzone)
{
X /= 32767.0f;
Y /= 32767.0f;
deadzone /= 32767.0f;
float mag = sqrtf(X*X + Y*Y);
if (mag > deadzone)
{
float legalRange = 1.0f - deadzone;
float normalizedMag = std::min(1.0f, (mag - deadzone) / legalRange);
float scale = normalizedMag / mag;
X = X * scale;
Y = Y * scale;
}
else
{
X = 0;
Y = 0;
}
};
normalize_input(LX, LY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
normalize_input(RX, RY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
pad.m_sticks[0].m_value = ConvertAxis(LX);
pad.m_sticks[1].m_value = 255 - ConvertAxis(LY);
pad.m_sticks[2].m_value = ConvertAxis(RX);
pad.m_sticks[3].m_value = 255 - ConvertAxis(RY);
std::tie(pad.m_sticks[0].m_value, pad.m_sticks[1].m_value) = ConvertToSquirclePoint(pad.m_sticks[0].m_value, pad.m_sticks[1].m_value);
std::tie(pad.m_sticks[2].m_value, pad.m_sticks[3].m_value) = ConvertToSquirclePoint(pad.m_sticks[2].m_value, pad.m_sticks[3].m_value);
XINPUT_VIBRATION vibrate;
+1
View File
@@ -21,6 +21,7 @@ private:
typedef DWORD (WINAPI * PFN_XINPUTSETSTATE)(DWORD, XINPUT_VIBRATION *);
private:
std::tuple<u16, u16> ConvertToSquirclePoint(u16 inX, u16 inY);
DWORD ThreadProcedure();
static DWORD WINAPI ThreadProcProxy(LPVOID parameter);