Compare commits
26 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 |
+1
-2
@@ -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:
|
||||
|
||||
+37
-9
@@ -12,6 +12,7 @@
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
#include "mutex.h"
|
||||
#include "sysinfo.h"
|
||||
#include "VirtualMemory.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -343,22 +344,30 @@ public:
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||
}
|
||||
|
||||
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
|
||||
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
|
||||
{
|
||||
std::string name = m_path;
|
||||
name.append(module->getName());
|
||||
|
||||
if (fs::file cached{name, fs::read})
|
||||
if (fs::file cached{path, fs::read})
|
||||
{
|
||||
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
|
||||
return buf;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
|
||||
{
|
||||
std::string path = m_path;
|
||||
path.append(module->getName());
|
||||
|
||||
if (auto buf = load(path))
|
||||
{
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
|
||||
return buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -369,6 +378,20 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, std::uintptr_t>
|
||||
if (m_cpu.empty())
|
||||
{
|
||||
m_cpu = llvm::sys::getHostCPUName();
|
||||
|
||||
if (m_cpu == "sandybridge" ||
|
||||
m_cpu == "ivybridge" ||
|
||||
m_cpu == "haswell" ||
|
||||
m_cpu == "broadwell" ||
|
||||
m_cpu == "skylake" ||
|
||||
m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cannonlake")
|
||||
{
|
||||
if (!utils::has_avx())
|
||||
{
|
||||
m_cpu = "nehalem";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string result;
|
||||
@@ -430,6 +453,11 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string&
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(const std::string& path)
|
||||
{
|
||||
m_engine->addObjectFile(std::move(llvm::object::ObjectFile::createObjectFile(*ObjectCache::load(path)).get()));
|
||||
}
|
||||
|
||||
void jit_compiler::fin()
|
||||
{
|
||||
m_engine->finalizeObject();
|
||||
|
||||
+4
-1
@@ -49,9 +49,12 @@ public:
|
||||
return m_context;
|
||||
}
|
||||
|
||||
// Add module
|
||||
// Add module (path to obj cache dir)
|
||||
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
|
||||
|
||||
// Add object (path to obj file)
|
||||
void add(const std::string& path);
|
||||
|
||||
// Finalize
|
||||
void fin();
|
||||
|
||||
|
||||
+35
-4
@@ -14,9 +14,13 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||
case patch_type::le16: return "le16";
|
||||
case patch_type::le32: return "le32";
|
||||
case patch_type::le64: return "le64";
|
||||
case patch_type::bef32: return "bef32";
|
||||
case patch_type::bef64: return "bef64";
|
||||
case patch_type::be16: return "be16";
|
||||
case patch_type::be32: return "be32";
|
||||
case patch_type::be64: return "be64";
|
||||
case patch_type::lef32: return "lef32";
|
||||
case patch_type::lef64: return "lef64";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
@@ -39,23 +43,44 @@ void patch_engine::append(const std::string& patch)
|
||||
u64 type64 = 0;
|
||||
cfg::try_to_enum_value(&type64, &fmt_class_string<patch_type>::format, patch[0].Scalar());
|
||||
|
||||
struct patch info;
|
||||
struct patch info{};
|
||||
info.type = static_cast<patch_type>(type64);
|
||||
info.offset = patch[1].as<u32>();
|
||||
info.value = patch[2].as<u64>();
|
||||
|
||||
switch (info.type)
|
||||
{
|
||||
case patch_type::bef32:
|
||||
case patch_type::lef32:
|
||||
{
|
||||
info.value_as<f32>() = patch[2].as<f32>();
|
||||
break;
|
||||
}
|
||||
case patch_type::bef64:
|
||||
case patch_type::lef64:
|
||||
{
|
||||
info.value_as<f64>() = patch[2].as<f64>();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
info.value = patch[2].as<u64>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data.emplace_back(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void patch_engine::apply(const std::string& name, u8* dst) const
|
||||
std::size_t patch_engine::apply(const std::string& name, u8* dst) const
|
||||
{
|
||||
const auto found = m_map.find(name);
|
||||
|
||||
if (found == m_map.cend())
|
||||
{
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply modifications sequentially
|
||||
@@ -76,11 +101,13 @@ void patch_engine::apply(const std::string& name, u8* dst) const
|
||||
break;
|
||||
}
|
||||
case patch_type::le32:
|
||||
case patch_type::lef32:
|
||||
{
|
||||
*reinterpret_cast<le_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::le64:
|
||||
case patch_type::lef64:
|
||||
{
|
||||
*reinterpret_cast<le_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
||||
break;
|
||||
@@ -91,15 +118,19 @@ void patch_engine::apply(const std::string& name, u8* dst) const
|
||||
break;
|
||||
}
|
||||
case patch_type::be32:
|
||||
case patch_type::bef32:
|
||||
{
|
||||
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::be64:
|
||||
case patch_type::bef64:
|
||||
{
|
||||
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found->second.size();
|
||||
}
|
||||
|
||||
+12
-2
@@ -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();
|
||||
}
|
||||
+2
-2
@@ -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:
|
||||
|
||||
@@ -85,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)
|
||||
@@ -98,7 +95,7 @@ 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
|
||||
@@ -283,6 +280,7 @@ endif()
|
||||
# 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})
|
||||
@@ -294,7 +292,7 @@ foreach (TMP_PATH ${RPCS3_SRC})
|
||||
endforeach(TMP_PATH)
|
||||
|
||||
# Remove the Qt moc files as part of clean, they are compiled when generating automoc
|
||||
file(GLOB_RECURSE TMP_MOC "${RPCS3_SRC_DIR}/moc_*.cpp" "${RPCS3_SRC_DIR}/rpcs3_automoc.cpp")
|
||||
file(GLOB_RECURSE TMP_MOC "${RPCS3_SRC_DIR}/moc_*.cpp" "${RPCS3_SRC_DIR}/rpcs3_automoc.cpp" "${RPCS3_SRC_DIR}/qrc_resources.cpp")
|
||||
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${TMP_MOC}")
|
||||
|
||||
if (WIN32)
|
||||
|
||||
+22
-4
@@ -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)
|
||||
{
|
||||
|
||||
@@ -743,20 +743,26 @@ s32 static NEVER_INLINE save_op_get_list_item(vm::cptr<char> dirName, vm::ptr<Ce
|
||||
strcpy_trunc(sysFileParam->detail, psf.at("DETAIL").as_string());
|
||||
}
|
||||
|
||||
fs::stat_t dir_info{};
|
||||
if (!fs::stat(save_path, dir_info))
|
||||
if (dir)
|
||||
{
|
||||
return CELL_SAVEDATA_ERROR_INTERNAL;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
//TODO: Set bind in accordance to any problems
|
||||
*bind = 0;
|
||||
if (bind)
|
||||
{
|
||||
//TODO: Set bind in accordance to any problems
|
||||
*bind = 0;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
@@ -954,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)
|
||||
{
|
||||
@@ -964,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)
|
||||
@@ -973,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)
|
||||
@@ -1004,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)
|
||||
|
||||
+129
-10
@@ -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;
|
||||
}
|
||||
|
||||
@@ -239,6 +239,8 @@ struct RSXFragmentProgram
|
||||
u8 textures_alpha_kill[16];
|
||||
u32 textures_zfunc[16];
|
||||
|
||||
bool valid;
|
||||
|
||||
rsx::texture_dimension_extended get_texture_dimension(u8 id) const
|
||||
{
|
||||
return (rsx::texture_dimension_extended)((texture_dimensions >> (id * 2)) & 0x3);
|
||||
@@ -263,6 +265,7 @@ struct RSXFragmentProgram
|
||||
, ctrl(0)
|
||||
, unnormalized_coords(0)
|
||||
, texture_dimensions(0)
|
||||
, valid(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
+19
-13
@@ -974,9 +974,17 @@ namespace rsx
|
||||
RSXFragmentProgram thread::get_current_fragment_program(std::function<std::tuple<bool, u16>(u32, fragment_texture&, bool)> get_surface_info) const
|
||||
{
|
||||
RSXFragmentProgram result = {};
|
||||
u32 shader_program = rsx::method_registers.shader_program_address();
|
||||
result.offset = shader_program & ~0x3;
|
||||
result.addr = vm::base(rsx::get_address(result.offset, (shader_program & 0x3) - 1));
|
||||
|
||||
const u32 shader_program = rsx::method_registers.shader_program_address();
|
||||
if (shader_program == 0)
|
||||
return result;
|
||||
|
||||
const u32 program_location = (shader_program & 0x3) - 1;
|
||||
const u32 program_offset = (shader_program & ~0x3);
|
||||
|
||||
result.offset = program_offset;
|
||||
result.addr = vm::base(rsx::get_address(program_offset, program_location));
|
||||
result.valid = true;
|
||||
result.ctrl = rsx::method_registers.shader_control();
|
||||
result.unnormalized_coords = 0;
|
||||
result.front_back_color_enabled = !rsx::method_registers.two_side_light_en();
|
||||
@@ -1174,26 +1182,26 @@ namespace rsx
|
||||
if (packet.post_upload_func)
|
||||
packet.post_upload_func(packet.dst_span.data(), packet.type, (u8)packet.vector_width, task.vertex_count);
|
||||
|
||||
_mm_sfence();
|
||||
task.remaining_packets--;
|
||||
current_job += step;
|
||||
_mm_sfence();
|
||||
}
|
||||
|
||||
_mm_mfence();
|
||||
|
||||
while (task.remaining_packets > 0 && !Emu.IsStopped())
|
||||
{
|
||||
std::this_thread::yield();
|
||||
_mm_lfence();
|
||||
std::this_thread::sleep_for(0us);
|
||||
}
|
||||
|
||||
_mm_sfence();
|
||||
task.ready_threads++;
|
||||
_mm_sfence();
|
||||
}
|
||||
else
|
||||
std::this_thread::sleep_for(0us);
|
||||
//thread_ctrl::wait();
|
||||
//busy_wait();
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1201,8 +1209,7 @@ namespace rsx
|
||||
|
||||
while (m_vertex_streaming_task.ready_threads != 0 && !Emu.IsStopped())
|
||||
{
|
||||
_mm_lfence();
|
||||
busy_wait();
|
||||
_mm_pause();
|
||||
}
|
||||
|
||||
m_vertex_streaming_task.vertex_count = vertex_count;
|
||||
@@ -1214,8 +1221,7 @@ namespace rsx
|
||||
{
|
||||
while (m_vertex_streaming_task.remaining_packets > 0 && !Emu.IsStopped())
|
||||
{
|
||||
_mm_lfence();
|
||||
busy_wait();
|
||||
_mm_pause();
|
||||
}
|
||||
|
||||
m_vertex_streaming_task.packets.resize(0);
|
||||
|
||||
+226
-63
@@ -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)
|
||||
|
||||
+8
-4
@@ -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
|
||||
{
|
||||
|
||||
+2
-2
@@ -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>
|
||||
@@ -19,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
|
||||
|
||||
@@ -1201,7 +1201,6 @@
|
||||
<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\system_info.h" />
|
||||
<ClInclude Include="rpcs3qt\game_list.h" />
|
||||
<ClInclude Include="rpcs3qt\game_list_grid_delegate.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
|
||||
@@ -514,9 +514,6 @@
|
||||
<ClInclude Include="QTGeneratedFiles\ui_welcome_dialog.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\system_info.h">
|
||||
<Filter>Gui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\game_list.h">
|
||||
<Filter>Gui</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <stdafx.h>
|
||||
#include "rpcs3_version.h"
|
||||
#include "system_info.h"
|
||||
#include "Utilities/sysinfo.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QActionGroup>
|
||||
@@ -40,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%s\n", rpcs3::version.to_string(), System_Info::getCPU().first);
|
||||
last->msg = fmt::format("RPCS3 v%s\n%s\n", rpcs3::version.to_string(), utils::get_system_info());
|
||||
|
||||
// Self-registration
|
||||
logs::listener::add(this);
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
#include "rpcs3_version.h"
|
||||
#include "system_info.h"
|
||||
#include "Utilities/sysinfo.h"
|
||||
|
||||
#include "ui_main_window.h"
|
||||
|
||||
@@ -112,13 +112,13 @@ void main_window::Init()
|
||||
RequestGlobalStylesheetChange(guiSettings->GetCurrentStylesheetPath());
|
||||
ConfigureGuiFromSettings(true);
|
||||
|
||||
if (!System_Info::getCPU().second)
|
||||
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"
|
||||
"\n"
|
||||
"No games will run and RPCS3 will crash if you try.");
|
||||
"Your CPU does not support SSSE3 (with three S, not two).\n");
|
||||
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,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
|
||||
@@ -1189,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(); });
|
||||
|
||||
@@ -148,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>
|
||||
|
||||
@@ -112,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])); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,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])); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,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])); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,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);
|
||||
|
||||
@@ -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>
|
||||
@@ -1213,7 +1192,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
<string>Heigth</string>
|
||||
<string>Height</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_48">
|
||||
<item>
|
||||
@@ -1383,8 +1362,4 @@
|
||||
<include location="../resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="ppuBG"/>
|
||||
<buttongroup name="spuBG"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <regex>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#include <bitset>
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#endif
|
||||
|
||||
class System_Info
|
||||
{
|
||||
class CPUID {
|
||||
uint32_t regs[4];
|
||||
|
||||
public:
|
||||
explicit CPUID(uint32_t func, uint32_t subfunc) {
|
||||
#ifdef _WIN32
|
||||
__cpuidex((int *)regs, func, subfunc);
|
||||
#else
|
||||
asm volatile
|
||||
("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
|
||||
: "a" (func), "c" (subfunc));
|
||||
// ECX is set to zero for CPUID function 4
|
||||
#endif
|
||||
}
|
||||
|
||||
const uint32_t &EAX() const { return regs[0]; }
|
||||
const uint32_t &EBX() const { return regs[1]; }
|
||||
const uint32_t &ECX() const { return regs[2]; }
|
||||
const uint32_t &EDX() const { return regs[3]; }
|
||||
const uint32_t *data() const { return ®s[0]; }
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Retrieves various information about the system's hardware
|
||||
@return a pair consisting of the compiled information as string value and a bool for SSSE3 compatibility
|
||||
*/
|
||||
static const std::pair<std::string, bool> getCPU()
|
||||
{
|
||||
int nIds_ = 0;
|
||||
int nExIds_ = 0;
|
||||
char brand[0x40];
|
||||
std::bitset<32> cpu_capabilities = 0;
|
||||
|
||||
nIds_ = CPUID(0, 0).EAX();
|
||||
// load bitset with flags for function 0x00000001
|
||||
if (nIds_ >= 1)
|
||||
{
|
||||
cpu_capabilities = CPUID(1, 0).ECX();
|
||||
}
|
||||
|
||||
nExIds_ = CPUID(0x80000000, 0).EAX();
|
||||
memset(brand, 0, sizeof(brand));
|
||||
if (nExIds_ >= 0x80000004)
|
||||
{
|
||||
memcpy(brand, CPUID(0x80000002, 0).data(), 16);
|
||||
memcpy(brand + 16, CPUID(0x80000003, 0).data(), 16);
|
||||
memcpy(brand + 32, CPUID(0x80000004, 0).data(), 16);
|
||||
}
|
||||
|
||||
bool supports_ssse3 = cpu_capabilities[9];
|
||||
|
||||
std::string s_sysInfo = fmt::format("%s | SSSE3 %s", std::regex_replace(brand, std::regex("^ +"), ""), supports_ssse3 ? "Supported" : "Not Supported");
|
||||
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetNativeSystemInfo(&sysInfo);
|
||||
MEMORYSTATUSEX memInfo;
|
||||
memInfo.dwLength = sizeof(memInfo);
|
||||
GlobalMemoryStatusEx(&memInfo);
|
||||
s_sysInfo += fmt::format(" | %d Threads | %.2f GB RAM", sysInfo.dwNumberOfProcessors, (float)memInfo.ullTotalPhys / std::pow(1024.0f, 3));
|
||||
#else
|
||||
long mem_total = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE);
|
||||
s_sysInfo += fmt::format(" | %d Threads | %.2f GB RAM", sysconf(_SC_NPROCESSORS_ONLN), (float)mem_total / std::pow(1024.0f, 3));
|
||||
#endif
|
||||
|
||||
return std::pair<std::string, bool>(s_sysInfo, supports_ssse3);
|
||||
};
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user