Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d7e72797a8 | |||
| 0dfd2eadc1 | |||
| 291ec1eeb2 | |||
| b24eb621ae | |||
| 0fa148e65e | |||
| 3f6b24d33a | |||
| 9fee2ebeb5 | |||
| 99828a8f15 | |||
| 2526626646 | |||
| 582913dc31 | |||
| 94c1b74a17 | |||
| cbd1b28d0d | |||
| 05ffb50037 | |||
| 72e13ddeb2 | |||
| 78f09d7645 | |||
| f69121116a | |||
| 0136215ef1 | |||
| ab534ac55d | |||
| 67bbd59924 | |||
| f0b1a587aa | |||
| ae8f858c56 | |||
| 7426eb285f | |||
| f91f2e3e6d | |||
| e39ee10105 | |||
| 2ef2f0f63b | |||
| e875c91121 | |||
| 6610abcd5f | |||
| aed9b31294 | |||
| 6597eb27c7 | |||
| 035a39a9a8 | |||
| 9c547d5eef | |||
| 504fa5ffda | |||
| ad66dbfd0b | |||
| 0adb579736 | |||
| d4c83e5dd2 | |||
| 821a8c6e65 | |||
| ef60809219 | |||
| a21ed06d27 | |||
| 97f59405aa | |||
| b586bd3b10 | |||
| c617b17037 | |||
| 73bbfcb246 | |||
| cfa7d04c49 | |||
| fb191693d1 | |||
| 743a19027a | |||
| ced539579e | |||
| 225af34ce9 | |||
| eb80b7ec34 | |||
| 3df960bb52 | |||
| 25fcde9507 | |||
| d8abe75526 | |||
| 15fe8f1c51 | |||
| ce7b55871b | |||
| 9d8fa28dd9 | |||
| 3a3e9770f3 | |||
| 2d037fa130 |
@@ -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
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "GLGSRender.h"
|
||||
#include "GLTextureCache.h"
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, ®ion);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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 ® = 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 ® = vk::get_varying_register(i.name);
|
||||
OS << "layout(location=" << reg.reg_location << ") out vec4 " << i.name << ";\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (insert_back_diffuse && insert_front_diffuse)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 8.7 KiB |
@@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -73,6 +73,7 @@ public:
|
||||
void DoUpdate();
|
||||
void WriteRegs();
|
||||
void EnableButtons(bool enable);
|
||||
void ClearBreakpoints();
|
||||
|
||||
void OnUpdate();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -24,6 +24,7 @@ Q_SIGNALS:
|
||||
void GuiSettingsSyncRequest();
|
||||
void GuiStylesheetRequest(const QString& path);
|
||||
void GuiSettingsSaveRequest();
|
||||
void ToolBarRepaintRequest();
|
||||
private Q_SLOTS:
|
||||
void OnBackupCurrentConfig();
|
||||
void OnApplyConfig();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||