Compare commits

...

26 Commits

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

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

gl/dx12/vk: Handle null fragment program

- cleanup - use yield semantic instead of sleep(0) as yield is more cross-platform
 -- sleep(0) is a windows specific scheduler hint
2017-07-19 23:28:33 +03:00
kd-11 72e13ddeb2 spu: Implement concurrent instruction execution watchdog for wrch
- Delays threads by a predetermined amount to 'desync' spurs kernels.
  Largely reduces lock contention issues as well as making spurs kernels
  play nice with reservations
- Also reduces number of lost notifications (SPU_EVENT_LR)
2017-07-19 23:28:33 +03:00
kd-11 78f09d7645 asmjit: Minimal locking when reading, also only lock when actually writing to the db 2017-07-19 23:28:33 +03:00
kd-11 f69121116a rsx/vk: Optimize framebuffer lifetime management
- Significant gains due to avoiding aggressive create-delete cycles every frame
2017-07-19 23:28:33 +03:00
RipleyTom 0136215ef1 Minor sceNpBasicGetEvent Fix 2017-07-19 21:41:37 +03:00
Megamouse ab534ac55d main_window fixerinno
remove sinful toolbar sorcery
2017-07-19 21:16:13 +03:00
Megamouse 67bbd59924 debugger_frame fixerinno
delete zombie dialog
clear breakpoints properly
2017-07-19 21:16:13 +03:00
RipleyTom f0b1a587aa Implements deadzone for xinput 2017-07-19 20:35:12 +03:00
Megamouse ae8f858c56 settings_dialog: get rid of string duplication
use enum to dictate button id
2017-07-19 17:40:27 +03:00
Dangles91 7426eb285f Fixes for save_op_get_list_item
Only return data for dir and bind where requested. Some games like Akiba's Trip call this function but don't ask for any data back.
2017-07-18 15:22:49 +03:00
Nekotekina f91f2e3e6d Patch: add floating point support
Also count applied entries
2017-07-17 18:36:54 +03:00
Nekotekina e39ee10105 Patch system improved
SPU patch rewritten
PPU patch implemented
2017-07-17 16:49:46 +03:00
Nekotekina 2ef2f0f63b PPU LLVM: simplify module loading 2017-07-17 16:49:46 +03:00
Nekotekina e875c91121 Restore timeBeginPeriod 2017-07-17 16:49:46 +03:00
55 changed files with 1288 additions and 432 deletions
+1 -2
View File
@@ -87,11 +87,10 @@ before_script:
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs ;
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -appimage ;
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq ;
zip -r9 rpcs3.zip ./RPCS3*.AppImage;
if [ -z "$WITHOUT_LLVM" ]; then
export LLVM="-LLVM";
fi;
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}${LLVM}_linux64 --upload-file rpcs3.zip;
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}${LLVM}_linux64 --upload-file ./RPCS3*.AppImage;
fi;
script:
+37 -9
View File
@@ -12,6 +12,7 @@
#include "File.h"
#include "Log.h"
#include "mutex.h"
#include "sysinfo.h"
#include "VirtualMemory.h"
#ifdef _MSC_VER
@@ -343,22 +344,30 @@ public:
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
}
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
{
std::string name = m_path;
name.append(module->getName());
if (fs::file cached{name, fs::read})
if (fs::file cached{path, fs::read})
{
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
return buf;
}
return nullptr;
}
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
{
std::string path = m_path;
path.append(module->getName());
if (auto buf = load(path))
{
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
return buf;
}
else
{
return nullptr;
}
return nullptr;
}
};
@@ -369,6 +378,20 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, std::uintptr_t>
if (m_cpu.empty())
{
m_cpu = llvm::sys::getHostCPUName();
if (m_cpu == "sandybridge" ||
m_cpu == "ivybridge" ||
m_cpu == "haswell" ||
m_cpu == "broadwell" ||
m_cpu == "skylake" ||
m_cpu == "skylake-avx512" ||
m_cpu == "cannonlake")
{
if (!utils::has_avx())
{
m_cpu = "nehalem";
}
}
}
std::string result;
@@ -430,6 +453,11 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string&
}
}
void jit_compiler::add(const std::string& path)
{
m_engine->addObjectFile(std::move(llvm::object::ObjectFile::createObjectFile(*ObjectCache::load(path)).get()));
}
void jit_compiler::fin()
{
m_engine->finalizeObject();
+4 -1
View File
@@ -49,9 +49,12 @@ public:
return m_context;
}
// Add module
// Add module (path to obj cache dir)
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
// Add object (path to obj file)
void add(const std::string& path);
// Finalize
void fin();
+35 -4
View File
@@ -14,9 +14,13 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
case patch_type::le16: return "le16";
case patch_type::le32: return "le32";
case patch_type::le64: return "le64";
case patch_type::bef32: return "bef32";
case patch_type::bef64: return "bef64";
case patch_type::be16: return "be16";
case patch_type::be32: return "be32";
case patch_type::be64: return "be64";
case patch_type::lef32: return "lef32";
case patch_type::lef64: return "lef64";
}
return unknown;
@@ -39,23 +43,44 @@ void patch_engine::append(const std::string& patch)
u64 type64 = 0;
cfg::try_to_enum_value(&type64, &fmt_class_string<patch_type>::format, patch[0].Scalar());
struct patch info;
struct patch info{};
info.type = static_cast<patch_type>(type64);
info.offset = patch[1].as<u32>();
info.value = patch[2].as<u64>();
switch (info.type)
{
case patch_type::bef32:
case patch_type::lef32:
{
info.value_as<f32>() = patch[2].as<f32>();
break;
}
case patch_type::bef64:
case patch_type::lef64:
{
info.value_as<f64>() = patch[2].as<f64>();
break;
}
default:
{
info.value = patch[2].as<u64>();
break;
}
}
data.emplace_back(info);
}
}
}
}
void patch_engine::apply(const std::string& name, u8* dst) const
std::size_t patch_engine::apply(const std::string& name, u8* dst) const
{
const auto found = m_map.find(name);
if (found == m_map.cend())
{
return;
return 0;
}
// Apply modifications sequentially
@@ -76,11 +101,13 @@ void patch_engine::apply(const std::string& name, u8* dst) const
break;
}
case patch_type::le32:
case patch_type::lef32:
{
*reinterpret_cast<le_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
break;
}
case patch_type::le64:
case patch_type::lef64:
{
*reinterpret_cast<le_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
break;
@@ -91,15 +118,19 @@ void patch_engine::apply(const std::string& name, u8* dst) const
break;
}
case patch_type::be32:
case patch_type::bef32:
{
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
break;
}
case patch_type::be64:
case patch_type::bef64:
{
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
break;
}
}
}
return found->second.size();
}
+12 -2
View File
@@ -11,9 +11,13 @@ enum class patch_type
le16,
le32,
le64,
lef32,
lef64,
be16,
be32,
be64,
bef32,
bef64,
};
class patch_engine
@@ -23,6 +27,12 @@ class patch_engine
patch_type type;
u32 offset;
u64 value;
template <typename T>
T& value_as()
{
return *reinterpret_cast<T*>(reinterpret_cast<char*>(&value));
}
};
// Database
@@ -32,6 +42,6 @@ public:
// Load from file
void append(const std::string& path);
// Apply patch
void apply(const std::string& name, u8* dst) const;
// Apply patch (returns the number of entries applied)
std::size_t apply(const std::string& name, u8* dst) const;
};
+61
View File
@@ -0,0 +1,61 @@
#include "sysinfo.h"
#include "StrFmt.h"
#ifdef _WIN32
#include "windows.h"
#else
#include <unistd.h>
#endif
std::string utils::get_system_info()
{
std::string result;
std::string brand;
if (get_cpuid(0x80000000, 0)[0] >= 0x80000004)
{
for (u32 i = 0; i < 3; i++)
{
brand.append(reinterpret_cast<const char*>(get_cpuid(0x80000002 + i, 0).data()), 16);
}
}
else
{
brand = "Unknown CPU";
}
brand.erase(0, brand.find_first_not_of(' '));
brand.erase(brand.find_last_not_of(' ') + 1);
while (auto found = brand.find(" ") + 1)
{
brand.erase(brand.begin() + found);
}
#ifdef _WIN32
::SYSTEM_INFO sysInfo;
::GetNativeSystemInfo(&sysInfo);
::MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(memInfo);
::GlobalMemoryStatusEx(&memInfo);
const u32 num_proc = sysInfo.dwNumberOfProcessors;
const u64 mem_total = memInfo.ullTotalPhys;
#else
const u32 num_proc = ::sysconf(_SC_NPROCESSORS_ONLN);
const u64 mem_total = ::sysconf(_SC_PHYS_PAGES) * ::sysconf(_SC_PAGE_SIZE);
#endif
fmt::append(result, "%s | %d Threads | %.2f GiB RAM", brand, num_proc, mem_total / (1024.0f * 1024 * 1024));
if (has_avx())
{
result += " | AVX";
}
if (has_rtm())
{
result += " | TSX";
}
return result;
}
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include "types.h"
#include <string>
namespace utils
{
inline std::array<u32, 4> get_cpuid(u32 func, u32 subfunc)
{
int regs[4];
#ifdef _MSC_VER
__cpuidex(regs, func, subfunc);
#else
__asm__ volatile("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) : "a" (func), "c" (subfunc));
#endif
return {0u+regs[0], 0u+regs[1], 0u+regs[2], 0u+regs[3]};
}
inline bool has_ssse3()
{
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x200;
}
inline bool has_avx()
{
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000;
}
inline bool has_rtm()
{
// Check RTM and MPX extensions in order to filter out TSX on Haswell CPUs
return get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4800) == 0x4800;
}
inline bool transaction_enter()
{
while (true)
{
const auto status = _xbegin();
if (status == _XBEGIN_STARTED)
{
return true;
}
if (!(status & _XABORT_RETRY))
{
return false;
}
}
}
std::string get_system_info();
}
+2 -2
View File
@@ -1,4 +1,4 @@
version: '0.0.2-{build}'
version: '0.0.3-{build}'
os: Visual Studio 2015
platform: x64
@@ -32,7 +32,7 @@ install:
artifacts:
- path: bin
name: 'rpcs3-v0.0.2-$(Date)-$(COMMIT_SHA)_win64'
name: 'rpcs3-v0.0.3-$(Date)-$(COMMIT_SHA)_win64'
type: zip
cache:
+3 -5
View File
@@ -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
View File
@@ -1,9 +1,12 @@
#include "stdafx.h"
#include "Utilities/sysinfo.h"
#include "Emu/Memory/vm.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/lv2/sys_sync.h"
#include "MFC.h"
const bool s_use_rtm = utils::has_rtm();
template <>
void fmt_class_string<MFC>::format(std::string& out, u64 arg)
{
@@ -145,10 +148,25 @@ void mfc_thread::cpu_task()
vm::reservation_acquire(cmd.eal, 128);
// Store unconditionally
vm::writer_lock lock(0);
data = to_write;
vm::reservation_update(cmd.eal, 128);
vm::notify(cmd.eal, 128);
if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
data = to_write;
vm::reservation_update(cmd.eal, 128);
vm::notify(cmd.eal, 128);
_xend();
}
else
{
vm::writer_lock lock(0);
data = to_write;
vm::reservation_update(cmd.eal, 128);
vm::notify(cmd.eal, 128);
}
}
else if (cmd.cmd & MFC_LIST_MASK)
{
+17 -11
View File
@@ -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;
}
+2 -2
View File
@@ -511,9 +511,9 @@ s32 sceNpBasicGetEvent(vm::ptr<s32> event, vm::ptr<SceNpUserInfo> from, vm::ptr<
sceNp.warning("sceNpBasicGetEvent(event=*0x%x, from=*0x%x, data=*0x%x, size=*0x%x)", event, from, data, size);
// TODO: Check for other error and pass other events
*event = SCE_NP_BASIC_EVENT_OFFLINE;
//*event = SCE_NP_BASIC_EVENT_OFFLINE; // This event only indicates a contact is offline, not the current status of the connection
return CELL_OK;
return SCE_NP_BASIC_ERROR_NO_EVENT;
}
s32 sceNpCommerceCreateCtx()
+37 -2
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Utilities/VirtualMemory.h"
#include "Utilities/bin_patch.h"
#include "Crypto/sha1.h"
#include "Crypto/unself.h"
#include "Loader/ELF.h"
@@ -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);
+46 -4
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Utilities/VirtualMemory.h"
#include "Utilities/sysinfo.h"
#include "Crypto/sha1.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
@@ -53,6 +54,8 @@
#include <cfenv>
#include "Utilities/GSL.h"
const bool s_use_rtm = utils::has_rtm();
extern u64 get_system_time();
namespace vm { using namespace ps3; }
@@ -825,6 +828,26 @@ extern bool ppu_stwcx(ppu_thread& ppu, u32 addr, u32 reg_value)
return false;
}
if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
const bool result = ppu.rtime == vm::reservation_acquire(addr, sizeof(u32)) && data.compare_and_swap_test(static_cast<u32>(ppu.rdata), reg_value);
if (result)
{
vm::reservation_update(addr, sizeof(u32));
vm::notify(addr, sizeof(u32));
}
_xend();
ppu.raddr = 0;
return result;
}
vm::writer_lock lock(0);
const bool result = ppu.rtime == vm::reservation_acquire(addr, sizeof(u32)) && data.compare_and_swap_test(static_cast<u32>(ppu.rdata), reg_value);
@@ -849,6 +872,26 @@ extern bool ppu_stdcx(ppu_thread& ppu, u32 addr, u64 reg_value)
return false;
}
if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
const bool result = ppu.rtime == vm::reservation_acquire(addr, sizeof(u64)) && data.compare_and_swap_test(ppu.rdata, reg_value);
if (result)
{
vm::reservation_update(addr, sizeof(u64));
vm::notify(addr, sizeof(u64));
}
_xend();
ppu.raddr = 0;
return result;
}
vm::writer_lock lock(0);
const bool result = ppu.rtime == vm::reservation_acquire(addr, sizeof(u64)) && data.compare_and_swap_test(ppu.rdata, reg_value);
@@ -1142,7 +1185,8 @@ extern void ppu_initialize(const ppu_module& info)
if (fs::is_file(cache_path + obj_name))
{
semaphore_lock lock(jmutex);
ppu_initialize2(*jit, part, cache_path, obj_name);
jit->add(cache_path + obj_name);
LOG_SUCCESS(PPU, "LLVM: Loaded module %s", obj_name);
continue;
}
@@ -1173,7 +1217,7 @@ extern void ppu_initialize(const ppu_module& info)
// Proceed with original JIT instance
semaphore_lock lock(jmutex);
ppu_initialize2(*jit, part, cache_path, obj_name);
jit->add(cache_path + obj_name);
});
}
@@ -1286,8 +1330,6 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
std::shared_ptr<MsgDialogBase> dlg;
// Check cached file
if (!fs::is_file(cache_path + obj_name))
{
legacy::FunctionPassManager pm(module.get());
+27 -13
View File
@@ -43,23 +43,27 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
// Key for multimap
const u64 key = entry | u64{ ls[entry / 4] } << 32;
const be_t<u32>* base = ls + entry / 4;
const u32 block_sz = max_limit - entry;
{
reader_lock lock(m_mutex);
// Try to find existing function in the database
if (auto func = find(ls + entry / 4, key, max_limit - entry))
if (auto func = find(base, key, block_sz))
{
return func;
}
}
writer_lock lock(m_mutex);
// Double-check
if (auto func = find(ls + entry / 4, key, max_limit - entry))
{
return func;
writer_lock lock(m_mutex);
// Double-check
if (auto func = find(base, key, block_sz))
{
return func;
}
}
// Initialize block entries with the function entry point
@@ -81,11 +85,15 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
const auto type = s_spu_itype.decode(op.opcode);
// Find existing function
if (pos != entry && find(ls + pos / 4, pos | u64{ op.opcode } << 32, limit - pos))
{
limit = pos;
break;
reader_lock lock(m_mutex);
// Find existing function
if (pos != entry && find(ls + pos / 4, pos | u64{ op.opcode } << 32, limit - pos))
{
limit = pos;
break;
}
}
// Additional analysis at the beginning of the block
@@ -311,10 +319,16 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
// Set whether the function can reset stack
func->does_reset_stack = ila_sp_pos < limit;
// Add function to the database
m_db.emplace(key, func);
// Lock here just before we write to the db
// Its is unlikely that the second check will pass anyway so we delay this step since compiling functions is very fast
{
writer_lock lock(m_mutex);
LOG_SUCCESS(SPU, "Function detected [0x%05x-0x%05x] (size=0x%x)", func->addr, func->addr + func->size, func->size);
// Add function to the database
m_db.emplace(key, func);
}
LOG_NOTICE(SPU, "Function detected [0x%05x-0x%05x] (size=0x%x)", func->addr, func->addr + func->size, func->size);
return func;
}
+11 -2
View File
@@ -5,6 +5,7 @@
#include "SPUThread.h"
#include "SPURecompiler.h"
#include "SPUASMJITRecompiler.h"
#include <algorithm>
extern u64 get_system_time();
@@ -22,8 +23,15 @@ void spu_recompiler_base::enter(SPUThread& spu)
// Get SPU LS pointer
const auto _ls = vm::ps3::_ptr<u32>(spu.offset);
// Always validate (TODO)
const auto func = spu.spu_db->analyse(_ls, spu.pc);
// Search if cached data matches
auto func = spu.compiled_cache[spu.pc / 4];
// Check shared db if we dont have a match
if (!func || !std::equal(func->data.begin(), func->data.end(), _ls + spu.pc / 4, [](const be_t<u32>& l, const be_t<u32>& r) { return *(u32*)(u8*)&l == *(u32*)(u8*)&r; }))
{
func = spu.spu_db->analyse(_ls, spu.pc).get();
spu.compiled_cache[spu.pc / 4] = func;
}
// Reset callstack if necessary
if ((func->does_reset_stack && spu.recursion_level) || spu.recursion_level >= 128)
@@ -32,6 +40,7 @@ void spu_recompiler_base::enter(SPUThread& spu)
return;
}
// Compile if needed
if (!func->compiled)
{
if (!spu.spu_rec)
+129 -10
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Utilities/lockless.h"
#include "Utilities/sysinfo.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
@@ -22,6 +23,8 @@
#include <atomic>
#include <thread>
const bool s_use_rtm = utils::has_rtm();
#ifdef _MSC_VER
bool operator ==(const u128& lhs, const u128& rhs)
{
@@ -55,6 +58,65 @@ void fmt_class_string<spu_decoder_type>::format(std::string& out, u64 arg)
});
}
namespace spu
{
namespace scheduler
{
std::array<std::atomic<u8>, 65536> atomic_instruction_table = {};
constexpr u32 native_jiffy_duration_us = 2000000;
void acquire_pc_address(u32 pc, u32 timeout_ms = 3)
{
const u8 max_concurrent_instructions = (u8)g_cfg.core.preferred_spu_threads;
const u32 pc_offset = pc >> 2;
if (timeout_ms > 0)
{
while (timeout_ms--)
{
if (atomic_instruction_table[pc_offset].load(std::memory_order_consume) >= max_concurrent_instructions)
std::this_thread::sleep_for(1ms);
}
}
else
{
std::this_thread::yield();
}
atomic_instruction_table[pc_offset]++;
}
void release_pc_address(u32 pc)
{
const u32 pc_offset = pc >> 2;
atomic_instruction_table[pc_offset]--;
}
struct concurrent_execution_watchdog
{
u32 pc = 0;
bool active = false;
concurrent_execution_watchdog(SPUThread& spu)
:pc(spu.pc)
{
if (g_cfg.core.preferred_spu_threads > 0)
{
acquire_pc_address(pc, (u32)g_cfg.core.spu_delay_penalty);
active = true;
}
}
~concurrent_execution_watchdog()
{
if (active)
release_pc_address(pc);
}
};
}
}
void spu_int_ctrl_t::set(u64 ints)
{
// leave only enabled interrupts
@@ -483,6 +545,7 @@ void SPUThread::do_dma_transfer(const spu_mfc_cmd& args, bool from_mfc)
void SPUThread::process_mfc_cmd()
{
spu::scheduler::concurrent_execution_watchdog watchdog(*this);
LOG_TRACE(SPU, "DMAC: cmd=%s, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", ch_mfc_cmd.cmd, ch_mfc_cmd.lsa, ch_mfc_cmd.eal, ch_mfc_cmd.tag, ch_mfc_cmd.size);
const auto mfc = fxm::check_unlocked<mfc_thread>();
@@ -499,7 +562,7 @@ void SPUThread::process_mfc_cmd()
}
// TODO: investigate lost notifications
std::this_thread::sleep_for(0us);
std::this_thread::yield();
_mm_lfence();
}
};
@@ -544,9 +607,22 @@ void SPUThread::process_mfc_cmd()
thread_ctrl::wait_for(100);
}
}
else if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
rtime = vm::reservation_acquire(raddr, 128);
rdata = data;
_xend();
_ref<decltype(rdata)>(ch_mfc_cmd.lsa & 0x3ffff) = rdata;
return ch_atomic_stat.set_value(MFC_GETLLAR_SUCCESS);
}
else
{
// Fast path
rdata = data;
_mm_lfence();
}
@@ -577,15 +653,36 @@ void SPUThread::process_mfc_cmd()
if (raddr == ch_mfc_cmd.eal && rtime == vm::reservation_acquire(raddr, 128) && rdata == data)
{
// TODO: vm::check_addr
vm::writer_lock lock;
if (rtime == vm::reservation_acquire(raddr, 128) && rdata == data)
if (s_use_rtm && utils::transaction_enter())
{
data = to_write;
result = true;
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
vm::reservation_update(raddr, 128);
vm::notify(raddr, 128);
if (rtime == vm::reservation_acquire(raddr, 128) && rdata == data)
{
data = to_write;
result = true;
vm::reservation_update(raddr, 128);
vm::notify(raddr, 128);
}
_xend();
}
else
{
vm::writer_lock lock;
if (rtime == vm::reservation_acquire(raddr, 128) && rdata == data)
{
data = to_write;
result = true;
vm::reservation_update(raddr, 128);
vm::notify(raddr, 128);
}
}
}
@@ -621,6 +718,23 @@ void SPUThread::process_mfc_cmd()
// Store unconditionally
// TODO: vm::check_addr
if (s_use_rtm && utils::transaction_enter())
{
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
data = to_write;
vm::reservation_update(ch_mfc_cmd.eal, 128);
vm::notify(ch_mfc_cmd.eal, 128);
_xend();
ch_atomic_stat.set_value(MFC_PUTLLUC_SUCCESS);
return;
}
vm::writer_lock lock(0);
data = to_write;
vm::reservation_update(ch_mfc_cmd.eal, 128);
@@ -897,7 +1011,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
if (ctr > 10000)
{
ctr = 0;
std::this_thread::sleep_for(0us);
std::this_thread::yield();
}
else
{
@@ -978,6 +1092,11 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
case SPU_RdDec:
{
out = ch_dec_value - (u32)(get_timebased_time() - ch_dec_start_timestamp);
//Polling: We might as well hint to the scheduler to slot in another thread since this one is counting down
if (g_cfg.core.spu_loop_detection && out > spu::scheduler::native_jiffy_duration_us)
std::this_thread::yield();
return true;
}
+1
View File
@@ -582,6 +582,7 @@ public:
std::exception_ptr pending_exception;
std::array<struct spu_function_t*, 65536> compiled_cache{};
std::shared_ptr<class SPUDatabase> spu_db;
std::shared_ptr<class spu_recompiler_base> spu_rec;
u32 recursion_level = 0;
+39 -28
View File
@@ -34,24 +34,13 @@ void sys_spu_image::load(const fs::file& stream)
const u32 addr = this->segs.addr() + 4096;
sha1_context ctx;
u8 output[20];
sha1_starts(&ctx);
sha1_update(&ctx, reinterpret_cast<const u8*>(&obj.header), sizeof(obj.header));
for (const auto& shdr : obj.shdrs)
{
sha1_update(&ctx, reinterpret_cast<const u8*>(&shdr), sizeof(spu_exec_object::shdr_t));
LOG_NOTICE(SPU, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", shdr.sh_type, shdr.sh_addr, shdr.sh_size, shdr.sh_flags);
}
for (const auto& prog : obj.progs)
{
sha1_update(&ctx, reinterpret_cast<const u8*>(&prog), sizeof(spu_exec_object::phdr_t));
sha1_update(&ctx, reinterpret_cast<const u8*>(prog.bin.data()), prog.bin.size());
LOG_NOTICE(SPU, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
if (prog.p_type == SYS_SPU_SEGMENT_TYPE_COPY)
@@ -85,22 +74,6 @@ void sys_spu_image::load(const fs::file& stream)
LOG_ERROR(SPU, "Unknown program type (0x%x)", prog.p_type);
}
}
sha1_finish(&ctx, output);
// Format patch name
std::string hash("spu-");
for (u8 x : output) fmt::append(hash, "%02x", x);
LOG_NOTICE(LOADER, "Loaded SPU image: %s", hash);
// Apply the patch
fxm::check_unlocked<patch_engine>()->apply(hash, vm::g_base_addr + addr);
if (!Emu.GetTitleID().empty())
{
// Alternative patch
fxm::check_unlocked<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr + addr);
}
}
void sys_spu_image::free()
@@ -113,15 +86,29 @@ void sys_spu_image::free()
void sys_spu_image::deploy(u32 loc)
{
// Segment info dump
std::string dump;
// Executable hash
sha1_context sha;
sha1_starts(&sha);
u8 sha1_hash[20];
for (int i = 0; i < nsegs; i++)
{
auto& seg = segs[i];
LOG_NOTICE(SPU, "*** Deploy: t=0x%x, ls=0x%x, size=0x%x, addr=0x%x", seg.type, seg.ls, seg.size, seg.addr);
fmt::append(dump, "\n\t[%d] t=0x%x, ls=0x%x, size=0x%x, addr=0x%x", i, seg.type, seg.ls, seg.size, seg.addr);
// Hash big-endian values
sha1_update(&sha, (uchar*)&seg.type, sizeof(seg.type));
sha1_update(&sha, (uchar*)&seg.size, sizeof(seg.size));
if (seg.type == SYS_SPU_SEGMENT_TYPE_COPY)
{
std::memcpy(vm::base(loc + seg.ls), vm::base(seg.addr), seg.size);
sha1_update(&sha, (uchar*)&seg.ls, sizeof(seg.ls));
sha1_update(&sha, vm::g_base_addr + seg.addr, seg.size);
}
else if (seg.type == SYS_SPU_SEGMENT_TYPE_FILL)
{
@@ -131,8 +118,32 @@ void sys_spu_image::deploy(u32 loc)
}
std::fill_n(vm::_ptr<u32>(loc + seg.ls), seg.size / 4, seg.addr);
sha1_update(&sha, (uchar*)&seg.ls, sizeof(seg.ls));
sha1_update(&sha, (uchar*)&seg.addr, sizeof(seg.addr));
}
}
sha1_finish(&sha, sha1_hash);
// Format patch name
std::string hash("SPU-0000000000000000000000000000000000000000");
for (u32 i = 0; i < sizeof(sha1_hash); i++)
{
constexpr auto pal = "0123456789abcdef";
hash[4 + i * 2] = pal[sha1_hash[i] >> 4];
hash[5 + i * 2] = pal[sha1_hash[i] & 15];
}
// Apply the patch
auto applied = fxm::check_unlocked<patch_engine>()->apply(hash, vm::g_base_addr + loc);
if (!Emu.GetTitleID().empty())
{
// Alternative patch
applied += fxm::check_unlocked<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr + loc);
}
LOG_NOTICE(LOADER, "Loaded SPU image: %s (<- %u)%s", hash, applied, dump);
}
error_code sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu)
+2 -2
View File
@@ -27,9 +27,9 @@
namespace vm
{
static u8* memory_reserve_4GiB(std::uintptr_t addr = 0)
static u8* memory_reserve_4GiB(std::uintptr_t _addr = 0)
{
for (u64 addr = 0x100000000;; addr += 0x100000000)
for (u64 addr = _addr + 0x100000000;; addr += 0x100000000)
{
if (auto ptr = utils::memory_reserve(0x100000000, (void*)addr))
{
+29 -5
View File
@@ -1,10 +1,13 @@
#include "stdafx.h"
#include "Utilities/sysinfo.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "ARMv7Thread.h"
#include "ARMv7Interpreter.h"
const bool s_use_rtm = utils::has_rtm();
using namespace arm_code::arm_encoding_alias;
#define ARG(arg, ...) const u32 arg = args::arg::extract(__VA_ARGS__);
@@ -2091,13 +2094,34 @@ void arm_interpreter::STREX(ARMv7Thread& cpu, const u32 op, const u32 cond)
return;
}
vm::writer_lock lock(0);
bool result;
const bool result = cpu.rtime == vm::reservation_acquire(addr, cpu.rtime) && data.compare_and_swap_test(cpu.rdata, value);
if (result)
if (s_use_rtm && utils::transaction_enter())
{
vm::reservation_update(addr, sizeof(u32));
if (!vm::reader_lock{vm::try_to_lock})
{
_xabort(0);
}
result = cpu.rtime == vm::reservation_acquire(addr, sizeof(u32)) && data.compare_and_swap_test(cpu.rdata, value);
if (result)
{
vm::reservation_update(addr, sizeof(u32));
}
_xend();
}
else
{
vm::writer_lock lock(0);
result = cpu.rtime == vm::reservation_acquire(addr, sizeof(u32)) && data.compare_and_swap_test(cpu.rdata, value);
if (result)
{
vm::reservation_update(addr, sizeof(u32));
}
}
cpu.raddr = 0;
+79 -5
View File
@@ -93,7 +93,12 @@ namespace rsx
auto It = m_render_targets_storage.find(address);
// TODO: Fix corner cases
// This doesn't take overlapping surface(s) into account.
surface_storage_type old_surface_storage;
surface_storage_type new_surface_storage;
surface_type old_surface = nullptr;
surface_type new_surface = nullptr;
if (It != m_render_targets_storage.end())
{
surface_storage_type &rtt = It->second;
@@ -104,10 +109,43 @@ namespace rsx
}
old_surface = Traits::get(rtt);
invalidated_resources.push_back(std::move(rtt));
old_surface_storage = std::move(rtt);
m_render_targets_storage.erase(address);
}
//Search invalidated resources for a suitable surface
for (auto It = invalidated_resources.begin(); It != invalidated_resources.end(); It++)
{
auto &rtt = *It;
if (Traits::rtt_has_format_width_height(rtt, color_format, width, height, true))
{
new_surface_storage = std::move(rtt);
if (old_surface)
//Exchange this surface with the invalidated one
rtt = std::move(old_surface_storage);
else
//rtt is now empty - erase it
invalidated_resources.erase(It);
new_surface = Traits::get(new_surface_storage);
Traits::invalidate_rtt_surface_contents(command_list, new_surface, old_surface, true);
Traits::prepare_rtt_for_drawing(command_list, new_surface);
break;
}
}
if (old_surface != nullptr && new_surface == nullptr)
//This was already determined to be invalid and is excluded from testing above
invalidated_resources.push_back(std::move(old_surface_storage));
if (new_surface != nullptr)
{
//New surface was found among existing surfaces
m_render_targets_storage[address] = std::move(new_surface_storage);
return new_surface;
}
m_render_targets_storage[address] = Traits::create_new_surface(address, color_format, width, height, old_surface, std::forward<Args>(extra_params)...);
return Traits::get(m_render_targets_storage[address]);
}
@@ -119,7 +157,11 @@ namespace rsx
surface_depth_format depth_format, size_t width, size_t height,
Args&&... extra_params)
{
surface_storage_type old_surface_storage;
surface_storage_type new_surface_storage;
surface_type old_surface = nullptr;
surface_type new_surface = nullptr;
auto It = m_depth_stencil_storage.find(address);
if (It != m_depth_stencil_storage.end())
{
@@ -131,10 +173,42 @@ namespace rsx
}
old_surface = Traits::get(ds);
invalidated_resources.push_back(std::move(ds));
old_surface_storage = std::move(ds);
m_depth_stencil_storage.erase(address);
}
//Search invalidated resources for a suitable surface
for (auto It = invalidated_resources.begin(); It != invalidated_resources.end(); It++)
{
auto &ds = *It;
if (Traits::ds_has_format_width_height(ds, depth_format, width, height, true))
{
new_surface_storage = std::move(ds);
if (old_surface)
//Exchange this surface with the invalidated one
ds = std::move(old_surface_storage);
else
invalidated_resources.erase(It);
new_surface = Traits::get(new_surface_storage);
Traits::prepare_ds_for_drawing(command_list, new_surface);
Traits::invalidate_depth_surface_contents(command_list, new_surface, old_surface, true);
break;
}
}
if (old_surface != nullptr && new_surface == nullptr)
//This was already determined to be invalid and is excluded from testing above
invalidated_resources.push_back(std::move(old_surface_storage));
if (new_surface != nullptr)
{
//New surface was found among existing surfaces
m_depth_stencil_storage[address] = std::move(new_surface_storage);
return new_surface;
}
m_depth_stencil_storage[address] = Traits::create_new_surface(address, depth_format, width, height, old_surface, std::forward<Args>(extra_params)...);
return Traits::get(m_depth_stencil_storage[address]);
}
@@ -358,10 +432,10 @@ namespace rsx
void invalidate_surface_cache_data(command_list_type command_list)
{
for (auto &rtt : m_render_targets_storage)
Traits::invalidate_rtt_surface_contents(command_list, Traits::get(std::get<1>(rtt)));
Traits::invalidate_rtt_surface_contents(command_list, Traits::get(std::get<1>(rtt)), nullptr, false);
for (auto &ds : m_depth_stencil_storage)
Traits::invalidate_depth_surface_contents(command_list, Traits::get(std::get<1>(ds)));
Traits::invalidate_depth_surface_contents(command_list, Traits::get(std::get<1>(ds)), nullptr, true);
}
};
}
}
+3 -4
View File
@@ -223,9 +223,8 @@ namespace
const std::vector<std::pair<u32, u32>>& vertex_ranges, d3d12_data_heap& m_buffer_data)
{
size_t index_count = std::accumulate(
vertex_ranges.begin(), vertex_ranges.end(), 0, [](size_t acc, const auto& pair) {
return acc + get_index_count(
rsx::method_registers.current_draw_clause.primitive, pair.second);
vertex_ranges.begin(), vertex_ranges.end(), 0ll, [](size_t acc, const auto& pair) {
return acc + get_index_count(rsx::method_registers.current_draw_clause.primitive, pair.second);
});
// Alloc
@@ -236,7 +235,7 @@ namespace
void* mapped_buffer =
m_buffer_data.map<void>(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size));
size_t vertex_count = 0;
u32 vertex_count = 0;
for (const auto& pair : vertex_ranges)
vertex_count += pair.second;
+11 -5
View File
@@ -324,6 +324,17 @@ void D3D12GSRender::end()
{
std::chrono::time_point<steady_clock> start_duration = steady_clock::now();
std::chrono::time_point<steady_clock> program_load_start = steady_clock::now();
load_program();
std::chrono::time_point<steady_clock> program_load_end = steady_clock::now();
m_timers.program_load_duration += std::chrono::duration_cast<std::chrono::microseconds>(program_load_end - program_load_start).count();
if (!m_fragment_program.valid)
{
rsx::thread::end();
return;
}
std::chrono::time_point<steady_clock> rtt_duration_start = steady_clock::now();
prepare_render_targets(get_current_resource_storage().command_list.Get());
@@ -344,11 +355,6 @@ void D3D12GSRender::end()
std::chrono::time_point<steady_clock> vertex_index_duration_end = steady_clock::now();
m_timers.vertex_index_duration += std::chrono::duration_cast<std::chrono::microseconds>(vertex_index_duration_end - vertex_index_duration_start).count();
std::chrono::time_point<steady_clock> program_load_start = steady_clock::now();
load_program();
std::chrono::time_point<steady_clock> program_load_end = steady_clock::now();
m_timers.program_load_duration += std::chrono::duration_cast<std::chrono::microseconds>(program_load_end - program_load_start).count();
get_current_resource_storage().command_list->SetGraphicsRootSignature(m_shared_root_signature.Get());
get_current_resource_storage().command_list->OMSetStencilRef(rsx::method_registers.stencil_func_ref());
@@ -56,6 +56,9 @@ void D3D12GSRender::load_program()
m_vertex_program = get_current_vertex_program();
m_fragment_program = get_current_fragment_program(rtt_lookup_func);
if (!m_fragment_program.valid)
return;
D3D12PipelineProperties prop = {};
prop.Topology = get_primitive_topology_type(rsx::method_registers.current_draw_clause.primitive);
+6 -4
View File
@@ -116,28 +116,30 @@ struct render_target_traits
command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(ds, D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_GENERIC_READ));
}
static
void invalidate_rtt_surface_contents(
gsl::not_null<ID3D12GraphicsCommandList*>,
ID3D12Resource*)
ID3D12Resource*, ID3D12Resource*, bool)
{}
static
void invalidate_depth_surface_contents(
gsl::not_null<ID3D12GraphicsCommandList*>,
ID3D12Resource*)
ID3D12Resource*, ID3D12Resource*, bool)
{
//TODO
}
static
bool rtt_has_format_width_height(const ComPtr<ID3D12Resource> &rtt, surface_color_format surface_color_format, size_t width, size_t height)
bool rtt_has_format_width_height(const ComPtr<ID3D12Resource> &rtt, surface_color_format surface_color_format, size_t width, size_t height, bool=false)
{
DXGI_FORMAT dxgi_format = get_color_surface_format(surface_color_format);
return rtt->GetDesc().Format == dxgi_format && rtt->GetDesc().Width == width && rtt->GetDesc().Height == height;
}
static
bool ds_has_format_width_height(const ComPtr<ID3D12Resource> &rtt, surface_depth_format, size_t width, size_t height)
bool ds_has_format_width_height(const ComPtr<ID3D12Resource> &rtt, surface_depth_format, size_t width, size_t height, bool=false)
{
//TODO: Check format
return rtt->GetDesc().Width == width && rtt->GetDesc().Height == height;
+57 -22
View File
@@ -322,17 +322,15 @@ namespace
void GLGSRender::end()
{
if (skip_frame || !framebuffer_status_valid)
std::chrono::time_point<steady_clock> program_start = steady_clock::now();
//Load program here since it is dependent on vertex state
if (skip_frame || !framebuffer_status_valid || !load_program())
{
rsx::thread::end();
return;
}
std::chrono::time_point<steady_clock> program_start = steady_clock::now();
//Load program here since it is dependent on vertex state
load_program();
std::chrono::time_point<steady_clock> program_stop = steady_clock::now();
m_begin_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(program_stop - program_start).count();
@@ -364,44 +362,80 @@ void GLGSRender::end()
surface->old_contents = nullptr;
};
//Check if we have any 'recycled' surfaces in memory and if so, clear them
std::vector<int> buffers_to_clear;
bool clear_all_color = true;
bool clear_depth = false;
for (int index = 0; index < 4; index++)
{
if (std::get<0>(m_rtts.m_bound_render_targets[index]) != 0)
{
if (std::get<1>(m_rtts.m_bound_render_targets[index])->cleared())
clear_all_color = false;
else
buffers_to_clear.push_back(index);
}
}
gl::render_target *ds = std::get<1>(m_rtts.m_bound_depth_stencil);
if (ds && !ds->cleared())
{
//Temporarily disable pixel tests
glDisable(GL_SCISSOR_TEST);
glDepthMask(GL_TRUE);
glClearDepth(1.0);
glClearStencil(255);
clear_depth = true;
}
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//Temporarily disable pixel tests
glDisable(GL_SCISSOR_TEST);
if (g_cfg.video.strict_rendering_mode)
if (clear_depth || buffers_to_clear.size() > 0)
{
GLenum mask = 0;
if (clear_depth)
{
//Copy previous data if any
if (ds->old_contents != nullptr)
copy_rtt_contents(ds);
glDepthMask(GL_TRUE);
glClearDepth(1.0);
glClearStencil(255);
mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
}
glDepthMask(rsx::method_registers.depth_write_enabled());
glEnable(GL_SCISSOR_TEST);
if (clear_all_color)
mask |= GL_COLOR_BUFFER_BIT;
glClear(mask);
if (buffers_to_clear.size() > 0 && !clear_all_color)
{
GLfloat colors[] = { 0.f, 0.f, 0.f, 0.f };
//It is impossible for the render target to be typa A or B here (clear all would have been flagged)
for (auto &i: buffers_to_clear)
glClearBufferfv(draw_fbo.id(), i, colors);
}
if (clear_depth)
glDepthMask(rsx::method_registers.depth_write_enabled());
ds->set_cleared();
}
if (g_cfg.video.strict_rendering_mode)
{
if (ds->old_contents != nullptr)
copy_rtt_contents(ds);
for (auto &rtt : m_rtts.m_bound_render_targets)
{
if (std::get<0>(rtt) != 0)
{
auto surface = std::get<1>(rtt);
if (!surface->cleared() && surface->old_contents != nullptr)
if (surface->old_contents != nullptr)
copy_rtt_contents(surface);
}
}
}
glEnable(GL_SCISSOR_TEST);
std::chrono::time_point<steady_clock> textures_start = steady_clock::now();
//Setup textures
@@ -463,7 +497,6 @@ void GLGSRender::end()
}
else
{
//LOG_ERROR(RSX, "No work is needed for this draw call! Muhahahahahahaha");
skip_upload = true;
}
@@ -841,8 +874,10 @@ bool GLGSRender::load_program()
return std::make_tuple(true, surface->get_native_pitch());
};
RSXVertexProgram vertex_program = get_current_vertex_program();
RSXFragmentProgram fragment_program = get_current_fragment_program(rtt_lookup_func);
if (!fragment_program.valid) return false;
RSXVertexProgram vertex_program = get_current_vertex_program();
u32 unnormalized_rtts = 0;
+2
View File
@@ -173,6 +173,8 @@ OPENGL_PROC(PFNGLMULTIDRAWARRAYSPROC, MultiDrawArrays);
OPENGL_PROC(PFNGLGETTEXTUREIMAGEEXTPROC, GetTextureImageEXT);
OPENGL_PROC(PFNGLGETTEXTUREIMAGEPROC, GetTextureImage);
OPENGL_PROC(PFNGLCLEARBUFFERFVPROC, ClearBufferfv);
//Sampler Objects
OPENGL_PROC(PFNGLGENSAMPLERSPROC, GenSamplers);
OPENGL_PROC(PFNGLDELETESAMPLERSPROC, DeleteSamplers);
+11 -4
View File
@@ -192,6 +192,7 @@ struct gl_render_target_traits
if (old_surface != nullptr && old_surface->get_compatible_internal_format() == internal_fmt)
result->old_contents = old_surface;
result->set_cleared();
return result;
}
@@ -240,19 +241,25 @@ struct gl_render_target_traits
static void prepare_ds_for_drawing(void *, gl::render_target*) {}
static void prepare_ds_for_sampling(void *, gl::render_target*) {}
static void invalidate_rtt_surface_contents(void *, gl::render_target*) {}
static void invalidate_depth_surface_contents(void *, gl::render_target *ds) { ds->set_cleared(false); }
static void invalidate_rtt_surface_contents(void *, gl::render_target *rtt, gl::render_target* /*old*/, bool forced) { if (forced) rtt->set_cleared(false); }
static void invalidate_depth_surface_contents(void *, gl::render_target *ds, gl::render_target* /*old*/, bool) { ds->set_cleared(false); }
static
bool rtt_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_color_format format, size_t width, size_t height)
bool rtt_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_color_format format, size_t width, size_t height, bool check_refs=false)
{
if (check_refs) //TODO
return false;
auto internal_fmt = rsx::internals::sized_internal_format(format);
return rtt->get_compatible_internal_format() == internal_fmt && rtt->width() == width && rtt->height() == height;
}
static
bool ds_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_depth_format, size_t width, size_t height)
bool ds_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_depth_format, size_t width, size_t height, bool check_refs=false)
{
if (check_refs) //TODO
return false;
// TODO: check format
return rtt->width() == width && rtt->height() == height;
}
+3
View File
@@ -239,6 +239,8 @@ struct RSXFragmentProgram
u8 textures_alpha_kill[16];
u32 textures_zfunc[16];
bool valid;
rsx::texture_dimension_extended get_texture_dimension(u8 id) const
{
return (rsx::texture_dimension_extended)((texture_dimensions >> (id * 2)) & 0x3);
@@ -263,6 +265,7 @@ struct RSXFragmentProgram
, ctrl(0)
, unnormalized_coords(0)
, texture_dimensions(0)
, valid(false)
{
}
};
+19 -13
View File
@@ -974,9 +974,17 @@ namespace rsx
RSXFragmentProgram thread::get_current_fragment_program(std::function<std::tuple<bool, u16>(u32, fragment_texture&, bool)> get_surface_info) const
{
RSXFragmentProgram result = {};
u32 shader_program = rsx::method_registers.shader_program_address();
result.offset = shader_program & ~0x3;
result.addr = vm::base(rsx::get_address(result.offset, (shader_program & 0x3) - 1));
const u32 shader_program = rsx::method_registers.shader_program_address();
if (shader_program == 0)
return result;
const u32 program_location = (shader_program & 0x3) - 1;
const u32 program_offset = (shader_program & ~0x3);
result.offset = program_offset;
result.addr = vm::base(rsx::get_address(program_offset, program_location));
result.valid = true;
result.ctrl = rsx::method_registers.shader_control();
result.unnormalized_coords = 0;
result.front_back_color_enabled = !rsx::method_registers.two_side_light_en();
@@ -1174,26 +1182,26 @@ namespace rsx
if (packet.post_upload_func)
packet.post_upload_func(packet.dst_span.data(), packet.type, (u8)packet.vector_width, task.vertex_count);
_mm_sfence();
task.remaining_packets--;
current_job += step;
_mm_sfence();
}
_mm_mfence();
while (task.remaining_packets > 0 && !Emu.IsStopped())
{
std::this_thread::yield();
_mm_lfence();
std::this_thread::sleep_for(0us);
}
_mm_sfence();
task.ready_threads++;
_mm_sfence();
}
else
std::this_thread::sleep_for(0us);
//thread_ctrl::wait();
//busy_wait();
{
std::this_thread::yield();
}
}
});
}
@@ -1201,8 +1209,7 @@ namespace rsx
while (m_vertex_streaming_task.ready_threads != 0 && !Emu.IsStopped())
{
_mm_lfence();
busy_wait();
_mm_pause();
}
m_vertex_streaming_task.vertex_count = vertex_count;
@@ -1214,8 +1221,7 @@ namespace rsx
{
while (m_vertex_streaming_task.remaining_packets > 0 && !Emu.IsStopped())
{
_mm_lfence();
busy_wait();
_mm_pause();
}
m_vertex_streaming_task.packets.resize(0);
+226 -63
View File
@@ -677,6 +677,7 @@ VKGSRender::~VKGSRender()
m_buffer_view_to_clean.clear();
m_sampler_to_clean.clear();
m_framebuffer_to_clean.clear();
m_draw_fbo.reset();
//Render passes
for (auto &render_pass : m_render_passes)
@@ -874,17 +875,17 @@ void VKGSRender::begin_render_pass()
size_t idx = vk::get_render_pass_location(
vk::get_compatible_surface_format(rsx::method_registers.surface_color()).first,
vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, rsx::method_registers.surface_depth_fmt()),
(u8)vk::get_draw_buffers(rsx::method_registers.surface_color_target()).size());
(u8)m_draw_buffers_count);
VkRenderPass current_render_pass = m_render_passes[idx];
VkRenderPassBeginInfo rp_begin = {};
rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rp_begin.renderPass = current_render_pass;
rp_begin.framebuffer = m_framebuffer_to_clean.back()->value;
rp_begin.framebuffer = m_draw_fbo->value;
rp_begin.renderArea.offset.x = 0;
rp_begin.renderArea.offset.y = 0;
rp_begin.renderArea.extent.width = m_framebuffer_to_clean.back()->width();
rp_begin.renderArea.extent.height = m_framebuffer_to_clean.back()->height();
rp_begin.renderArea.extent.width = m_draw_fbo->width();
rp_begin.renderArea.extent.height = m_draw_fbo->height();
vkCmdBeginRenderPass(*m_current_command_buffer, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
render_pass_open = true;
@@ -941,10 +942,15 @@ void VKGSRender::end()
}
//Load program here since it is dependent on vertex state
load_program(is_instanced);
if (!load_program(is_instanced))
{
LOG_ERROR(RSX, "No valid program bound to pipeline. Skipping draw");
rsx::thread::end();
return;
}
std::chrono::time_point<steady_clock> program_stop = steady_clock::now();
m_setup_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(program_stop - program_start).count();
//m_setup_time += std::chrono::duration_cast<std::chrono::microseconds>(program_stop - program_start).count();
if (is_instanced)
{
@@ -999,14 +1005,14 @@ void VKGSRender::end()
{
auto surface = std::get<1>(rtt);
if (surface->dirty && surface->old_contents != nullptr)
if (surface->old_contents != nullptr)
copy_rtt_contents(surface);
}
}
if (auto ds = std::get<1>(m_rtts.m_bound_depth_stencil))
{
if (ds->dirty && ds->old_contents != nullptr)
if (ds->old_contents != nullptr)
copy_rtt_contents(ds);
}
}
@@ -1114,6 +1120,11 @@ void VKGSRender::end()
vkCmdBindPipeline(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline);
vkCmdBindDescriptorSets(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets, 0, nullptr);
//Clear any 'dirty' surfaces - possible is a recycled cache surface is used
std::vector<VkClearAttachment> buffers_to_clear;
buffers_to_clear.reserve(4);
const auto targets = vk::get_draw_buffers(rsx::method_registers.surface_color_target());
if (auto ds = std::get<1>(m_rtts.m_bound_depth_stencil))
{
if (ds->dirty)
@@ -1123,28 +1134,41 @@ void VKGSRender::end()
depth_clear_value.depthStencil.depth = 1.f;
depth_clear_value.depthStencil.stencil = 255;
VkClearRect clear_rect = { 0, 0, m_framebuffer_to_clean.back()->width(), m_framebuffer_to_clean.back()->height(), 0, 1 };
VkClearAttachment clear_desc = { ds->attachment_aspect_flag, 0, depth_clear_value };
vkCmdClearAttachments(*m_current_command_buffer, 1, &clear_desc, 1, &clear_rect);
buffers_to_clear.push_back(clear_desc);
ds->dirty = false;
}
}
std::optional<std::tuple<VkDeviceSize, VkIndexType> > index_info = std::get<2>(upload_info);
if (m_attrib_ring_info.mapped)
for (int index = 0; index < targets.size(); ++index)
{
wait_for_vertex_upload_task();
m_attrib_ring_info.unmap();
if (std::get<0>(m_rtts.m_bound_render_targets[index]) != 0 && std::get<1>(m_rtts.m_bound_render_targets[index])->dirty)
{
const u32 real_index = (index == 1 && targets.size() == 1) ? 0 : static_cast<u32>(index);
buffers_to_clear.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, real_index, {} });
std::get<1>(m_rtts.m_bound_render_targets[index])->dirty = false;
}
}
if (buffers_to_clear.size() > 0)
{
VkClearRect clear_rect = { 0, 0, m_draw_fbo->width(), m_draw_fbo->height(), 0, 1 };
vkCmdClearAttachments(*m_current_command_buffer, static_cast<u32>(buffers_to_clear.size()), buffers_to_clear.data(), 1, &clear_rect);
}
std::optional<std::tuple<VkDeviceSize, VkIndexType> > index_info = std::get<2>(upload_info);
std::chrono::time_point<steady_clock> vertex_end = steady_clock::now();
m_vertex_upload_time += std::chrono::duration_cast<std::chrono::microseconds>(vertex_end - textures_end).count();
if (!index_info)
{
vkCmdDraw(*m_current_command_buffer, std::get<1>(upload_info), 1, 0, 0);
const auto vertex_count = std::get<1>(upload_info);
vkCmdDraw(*m_current_command_buffer, vertex_count, 1, 0, 0);
m_last_vertex_count = vertex_count;
m_last_draw_indexed = false;
}
else
@@ -1175,6 +1199,22 @@ void VKGSRender::end()
copy_render_targets_to_dma_location();
m_draw_calls++;
if (g_cfg.video.overlay)
{
if (m_last_vertex_count < 1024)
m_uploads_small++;
else if (m_last_vertex_count < 2048)
m_uploads_1k++;
else if (m_last_vertex_count < 4096)
m_uploads_2k++;
else if (m_last_vertex_count < 8192)
m_uploads_4k++;
else if (m_last_vertex_count < 16384)
m_uploads_8k++;
else
m_uploads_16k++;
}
rsx::thread::end();
}
@@ -1251,8 +1291,6 @@ void VKGSRender::clear_surface(u32 mask)
u32 depth_stencil_mask = 0;
std::vector<VkClearAttachment> clear_descriptors;
std::vector<VkClearRect> clear_regions;
VkClearValue depth_stencil_clear_values, color_clear_values;
u16 scissor_x = rsx::method_registers.scissor_origin_x();
@@ -1260,8 +1298,8 @@ void VKGSRender::clear_surface(u32 mask)
u16 scissor_y = rsx::method_registers.scissor_origin_y();
u16 scissor_h = rsx::method_registers.scissor_height();
const u32 fb_width = m_framebuffer_to_clean.back()->width();
const u32 fb_height = m_framebuffer_to_clean.back()->height();
const u32 fb_width = m_draw_fbo->width();
const u32 fb_height = m_draw_fbo->height();
//clip region
std::tie(scissor_x, scissor_y, scissor_w, scissor_h) = rsx::clip_region<u16>(fb_width, fb_height, scissor_x, scissor_y, scissor_w, scissor_h, true);
@@ -1309,8 +1347,8 @@ void VKGSRender::clear_surface(u32 mask)
for (int index = 0; index < targets.size(); ++index)
{
clear_descriptors.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, (uint32_t)index, color_clear_values });
clear_regions.push_back(region);
const u32 real_index = (index == 1 && targets.size() == 1) ? 0 : static_cast<u32>(index);
clear_descriptors.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, real_index, color_clear_values });
}
for (auto &rtt : m_rtts.m_bound_render_targets)
@@ -1324,13 +1362,10 @@ void VKGSRender::clear_surface(u32 mask)
}
if (mask & 0x3)
{
clear_descriptors.push_back({ (VkImageAspectFlags)depth_stencil_mask, 0, depth_stencil_clear_values });
clear_regions.push_back(region);
}
begin_render_pass();
vkCmdClearAttachments(*m_current_command_buffer, (u32)clear_descriptors.size(), clear_descriptors.data(), (u32)clear_regions.size(), clear_regions.data());
vkCmdClearAttachments(*m_current_command_buffer, (u32)clear_descriptors.size(), clear_descriptors.data(), 1, &region);
if (mask & 0x3)
{
@@ -1392,6 +1427,12 @@ void VKGSRender::copy_render_targets_to_dma_location()
void VKGSRender::flush_command_queue(bool hard_sync)
{
if (m_attrib_ring_info.mapped)
{
wait_for_vertex_upload_task();
m_attrib_ring_info.unmap();
}
close_render_pass();
close_and_submit_command_buffer({}, m_current_command_buffer->submit_fence);
@@ -1472,7 +1513,7 @@ void VKGSRender::process_swap_request()
//Feed back damaged resources to the main texture cache for management...
//m_texture_cache.merge_dirty_textures(m_rtts.invalidated_resources);
m_rtts.invalidated_resources.clear();
m_rtts.free_invalidated();
m_texture_cache.flush();
if (g_cfg.video.invalidate_surface_cache_every_frame)
@@ -1480,7 +1521,13 @@ void VKGSRender::process_swap_request()
m_buffer_view_to_clean.clear();
m_sampler_to_clean.clear();
m_framebuffer_to_clean.clear();
m_framebuffer_to_clean.remove_if([](std::unique_ptr<vk::framebuffer_holder>& fbo)
{
if (fbo->deref_count >= 2) return true;
fbo->deref_count++;
return false;
});
if (g_cfg.video.overlay)
{
@@ -1545,8 +1592,10 @@ bool VKGSRender::load_program(bool fast_update)
return std::make_tuple(true, surface->native_pitch);
};
vertex_program = get_current_vertex_program();
fragment_program = get_current_fragment_program(rtt_lookup_func);
if (!fragment_program.valid) return false;
vertex_program = get_current_vertex_program();
vk::pipeline_props properties = {};
@@ -1691,7 +1740,7 @@ bool VKGSRender::load_program(bool fast_update)
size_t idx = vk::get_render_pass_location(
vk::get_compatible_surface_format(rsx::method_registers.surface_color()).first,
vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, rsx::method_registers.surface_depth_fmt()),
(u8)vk::get_draw_buffers(rsx::method_registers.surface_color_target()).size());
(u8)m_draw_buffers_count);
properties.render_pass = m_render_passes[idx];
@@ -1853,6 +1902,7 @@ void VKGSRender::prepare_rtts()
{
LOG_ERROR(RSX, "Invalid framebuffer setup, w=%d, h=%d", clip_width, clip_height);
framebuffer_status_valid = false;
return;
}
framebuffer_status_valid = true;
@@ -1863,6 +1913,35 @@ void VKGSRender::prepare_rtts()
const u32 surface_pitchs[] = { rsx::method_registers.surface_a_pitch(), rsx::method_registers.surface_b_pitch(),
rsx::method_registers.surface_c_pitch(), rsx::method_registers.surface_d_pitch() };
if (m_draw_fbo)
{
const u32 fb_width = m_draw_fbo->width();
const u32 fb_height = m_draw_fbo->height();
bool really_changed = false;
if (fb_width == clip_width && fb_height == clip_height)
{
for (u8 i = 0; i < rsx::limits::color_buffers_count; ++i)
{
if (m_surface_info[i].address != surface_addresses[i])
{
really_changed = true;
break;
}
}
if (!really_changed)
{
if (zeta_address == m_depth_surface_info.address)
{
//Nothing has changed, we're still using the same framebuffer
return;
}
}
}
}
m_rtts.prepare_render_target(&*m_current_command_buffer,
rsx::method_registers.surface_color(), rsx::method_registers.surface_depth_fmt(),
clip_width, clip_height,
@@ -1886,20 +1965,16 @@ void VKGSRender::prepare_rtts()
//Bind created rtts as current fbo...
std::vector<u8> draw_buffers = vk::get_draw_buffers(rsx::method_registers.surface_color_target());
std::vector<std::unique_ptr<vk::image_view>> fbo_images;
//Search old framebuffers for this same configuration
bool framebuffer_found = false;
std::vector<vk::image*> bound_images;
bound_images.reserve(5);
for (u8 index : draw_buffers)
{
vk::image *raw = std::get<1>(m_rtts.m_bound_render_targets[index]);
VkImageSubresourceRange subres = {};
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subres.baseArrayLayer = 0;
subres.baseMipLevel = 0;
subres.layerCount = 1;
subres.levelCount = 1;
fbo_images.push_back(std::make_unique<vk::image_view>(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres));
bound_images.push_back(std::get<1>(m_rtts.m_bound_render_targets[index]));
m_surface_info[index].address = surface_addresses[index];
m_surface_info[index].pitch = surface_pitchs[index];
@@ -1912,20 +1987,9 @@ void VKGSRender::prepare_rtts()
}
}
m_draw_buffers_count = static_cast<u32>(fbo_images.size());
if (std::get<1>(m_rtts.m_bound_depth_stencil) != nullptr)
if (std::get<0>(m_rtts.m_bound_depth_stencil) != 0)
{
vk::image *raw = (std::get<1>(m_rtts.m_bound_depth_stencil));
VkImageSubresourceRange subres = {};
subres.aspectMask = (rsx::method_registers.surface_depth_fmt() == rsx::surface_depth_format::z24s8) ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) : VK_IMAGE_ASPECT_DEPTH_BIT;
subres.baseArrayLayer = 0;
subres.baseMipLevel = 0;
subres.layerCount = 1;
subres.levelCount = 1;
fbo_images.push_back(std::make_unique<vk::image_view>(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres));
bound_images.push_back(std::get<1>(m_rtts.m_bound_depth_stencil));
m_depth_surface_info.address = zeta_address;
m_depth_surface_info.pitch = rsx::method_registers.surface_z_pitch();
@@ -1934,6 +1998,8 @@ void VKGSRender::prepare_rtts()
m_depth_surface_info.pitch = 0;
}
m_draw_buffers_count = static_cast<u32>(draw_buffers.size());
if (g_cfg.video.write_color_buffers)
{
for (u8 index : draw_buffers)
@@ -1942,7 +2008,7 @@ void VKGSRender::prepare_rtts()
const u32 range = m_surface_info[index].pitch * m_surface_info[index].height;
m_texture_cache.lock_memory_region(std::get<1>(m_rtts.m_bound_render_targets[index]), m_surface_info[index].address, range,
m_surface_info[index].width, m_surface_info[index].height);
m_surface_info[index].width, m_surface_info[index].height);
}
}
@@ -1959,10 +2025,59 @@ void VKGSRender::prepare_rtts()
}
}
size_t idx = vk::get_render_pass_location(vk::get_compatible_surface_format(rsx::method_registers.surface_color()).first, vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, rsx::method_registers.surface_depth_fmt()), (u8)draw_buffers.size());
VkRenderPass current_render_pass = m_render_passes[idx];
for (auto &fbo : m_framebuffer_to_clean)
{
if (fbo->matches(bound_images, clip_width, clip_height))
{
m_draw_fbo.swap(fbo);
m_draw_fbo->reset_refs();
framebuffer_found = true;
//LOG_ERROR(RSX, "Matching framebuffer exists, using that instead");
break;
}
}
m_framebuffer_to_clean.push_back(std::make_unique<vk::framebuffer>(*m_device, current_render_pass, clip_width, clip_height, std::move(fbo_images)));
if (!framebuffer_found)
{
std::vector<std::unique_ptr<vk::image_view>> fbo_images;
fbo_images.reserve(5);
for (u8 index : draw_buffers)
{
vk::image *raw = std::get<1>(m_rtts.m_bound_render_targets[index]);
VkImageSubresourceRange subres = {};
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subres.baseArrayLayer = 0;
subres.baseMipLevel = 0;
subres.layerCount = 1;
subres.levelCount = 1;
fbo_images.push_back(std::make_unique<vk::image_view>(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres));
}
if (std::get<1>(m_rtts.m_bound_depth_stencil) != nullptr)
{
vk::image *raw = (std::get<1>(m_rtts.m_bound_depth_stencil));
VkImageSubresourceRange subres = {};
subres.aspectMask = (rsx::method_registers.surface_depth_fmt() == rsx::surface_depth_format::z24s8) ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) : VK_IMAGE_ASPECT_DEPTH_BIT;
subres.baseArrayLayer = 0;
subres.baseMipLevel = 0;
subres.layerCount = 1;
subres.levelCount = 1;
fbo_images.push_back(std::make_unique<vk::image_view>(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres));
}
size_t idx = vk::get_render_pass_location(vk::get_compatible_surface_format(rsx::method_registers.surface_color()).first, vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, rsx::method_registers.surface_depth_fmt()), (u8)draw_buffers.size());
VkRenderPass current_render_pass = m_render_passes[idx];
if (m_draw_fbo)
m_framebuffer_to_clean.push_back(std::move(m_draw_fbo));
m_draw_fbo.reset(new vk::framebuffer_holder(*m_device, current_render_pass, clip_width, clip_height, std::move(fbo_images)));
}
}
@@ -1981,6 +2096,13 @@ void VKGSRender::flip(int buffer)
m_setup_time = 0;
m_vertex_upload_time = 0;
m_textures_upload_time = 0;
m_uploads_small = 0;
m_uploads_1k = 0;
m_uploads_2k = 0;
m_uploads_4k = 0;
m_uploads_8k = 0;
m_uploads_16k = 0;
}
return;
@@ -2060,7 +2182,7 @@ void VKGSRender::flip(int buffer)
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_present_image), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, range);
}
std::unique_ptr<vk::framebuffer> direct_fbo;
std::unique_ptr<vk::framebuffer_holder> direct_fbo;
std::vector<std::unique_ptr<vk::image_view>> swap_image_view;
if (g_cfg.video.overlay)
{
@@ -2082,9 +2204,24 @@ void VKGSRender::flip(int buffer)
size_t idx = vk::get_render_pass_location(m_swap_chain->get_surface_format(), VK_FORMAT_UNDEFINED, 1);
VkRenderPass single_target_pass = m_render_passes[idx];
swap_image_view.push_back(std::make_unique<vk::image_view>(*m_device, target_image, VK_IMAGE_VIEW_TYPE_2D, m_swap_chain->get_surface_format(), vk::default_component_map(), subres));
direct_fbo.reset(new vk::framebuffer(*m_device, single_target_pass, m_client_width, m_client_height, std::move(swap_image_view)));
for (auto It = m_framebuffer_to_clean.begin(); It != m_framebuffer_to_clean.end(); It++)
{
auto &fbo = *It;
if (fbo->attachments[0]->info.image == target_image)
{
direct_fbo.swap(fbo);
direct_fbo->reset_refs();
m_framebuffer_to_clean.erase(It);
break;
}
}
if (!direct_fbo)
{
swap_image_view.push_back(std::make_unique<vk::image_view>(*m_device, target_image, VK_IMAGE_VIEW_TYPE_2D, m_swap_chain->get_surface_format(), vk::default_component_map(), subres));
direct_fbo.reset(new vk::framebuffer_holder(*m_device, single_target_pass, m_client_width, m_client_height, std::move(swap_image_view)));
}
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 0, direct_fbo->width(), direct_fbo->height(), "draw calls: " + std::to_string(m_draw_calls) + ", instanced repeats: " + std::to_string(m_instanced_draws));
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 18, direct_fbo->width(), direct_fbo->height(), "draw call setup: " + std::to_string(m_setup_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 36, direct_fbo->width(), direct_fbo->height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
@@ -2092,10 +2229,29 @@ void VKGSRender::flip(int buffer)
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 72, direct_fbo->width(), direct_fbo->height(), "draw call execution: " + std::to_string(m_draw_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 90, direct_fbo->width(), direct_fbo->height(), "submit and flip: " + std::to_string(m_flip_time) + "us");
//Vertex upload statistics
u32 _small, _1k, _2k, _4k, _8k, _16k;
if (m_draw_calls > 0)
{
_small = m_uploads_small * 100 / m_draw_calls;
_1k = m_uploads_1k * 100 / m_draw_calls;
_2k = m_uploads_2k * 100 / m_draw_calls;
_4k = m_uploads_4k * 100 / m_draw_calls;
_8k = m_uploads_8k * 100 / m_draw_calls;
_16k = m_uploads_16k * 100 / m_draw_calls;
}
else
{
_small = _1k = _2k = _4k = _8k = _16k = 0;
}
std::string message = fmt::format("Vertex sizes: < 1k: %d%%, 1k+: %d%%, 2k+: %d%%, 4k+: %d%%, 8k+: %d%%, 16k+: %d%%", _small, _1k, _2k, _4k, _8k, _16k);
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 108, direct_fbo->width(), direct_fbo->height(), message);
vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subres);
m_framebuffer_to_clean.push_back(std::move(direct_fbo));
}
m_framebuffer_to_clean.push_back(std::move(direct_fbo));
queue_swap_request();
}
else
@@ -2193,4 +2349,11 @@ void VKGSRender::flip(int buffer)
m_setup_time = 0;
m_vertex_upload_time = 0;
m_textures_upload_time = 0;
m_uploads_small = 0;
m_uploads_1k = 0;
m_uploads_2k = 0;
m_uploads_4k = 0;
m_uploads_8k = 0;
m_uploads_16k = 0;
}
+12 -1
View File
@@ -149,15 +149,26 @@ private:
vk::descriptor_pool descriptor_pool;
std::vector<std::unique_ptr<vk::buffer_view> > m_buffer_view_to_clean;
std::vector<std::unique_ptr<vk::framebuffer> > m_framebuffer_to_clean;
std::vector<std::unique_ptr<vk::sampler> > m_sampler_to_clean;
std::list<std::unique_ptr<vk::framebuffer_holder> > m_framebuffer_to_clean;
std::unique_ptr<vk::framebuffer_holder> m_draw_fbo;
u32 m_client_width = 0;
u32 m_client_height = 0;
// Draw call stats
u32 m_draw_calls = 0;
u32 m_instanced_draws = 0;
// Vertex buffer usage stats
u32 m_uploads_small = 0;
u32 m_uploads_1k = 0;
u32 m_uploads_2k = 0;
u32 m_uploads_4k = 0;
u32 m_uploads_8k = 0;
u32 m_uploads_16k = 0;
// Timers
s64 m_setup_time = 0;
s64 m_vertex_upload_time = 0;
s64 m_textures_upload_time = 0;
+22 -4
View File
@@ -650,17 +650,17 @@ namespace vk
{
VkFramebuffer value;
VkFramebufferCreateInfo info = {};
std::vector<std::unique_ptr<vk::image_view>> attachements;
std::vector<std::unique_ptr<vk::image_view>> attachments;
u32 m_width = 0;
u32 m_height = 0;
public:
framebuffer(VkDevice dev, VkRenderPass pass, u32 width, u32 height, std::vector<std::unique_ptr<vk::image_view>> &&atts)
: m_device(dev), attachements(std::move(atts))
: m_device(dev), attachments(std::move(atts))
{
std::vector<VkImageView> image_view_array(attachements.size());
std::vector<VkImageView> image_view_array(attachments.size());
size_t i = 0;
for (const auto &att : attachements)
for (const auto &att : attachments)
{
image_view_array[i++] = att->value;
}
@@ -694,6 +694,24 @@ namespace vk
return m_height;
}
bool matches(std::vector<vk::image*> fbo_images, u32 width, u32 height)
{
if (m_width != width || m_height != height)
return false;
if (fbo_images.size() != attachments.size())
return false;
for (int n = 0; n < fbo_images.size(); ++n)
{
if (attachments[n]->info.image != fbo_images[n]->value ||
attachments[n]->info.format != fbo_images[n]->info.format)
return false;
}
return true;
}
framebuffer(const framebuffer&) = delete;
framebuffer(framebuffer&&) = delete;
+65 -10
View File
@@ -7,9 +7,16 @@
#include "../Common/TextureUtils.h"
#include "VKFormats.h"
struct ref_counted
{
u8 deref_count = 0;
void reset_refs() { deref_count = 0; }
};
namespace vk
{
struct render_target : public image
struct render_target : public image, public ref_counted
{
bool dirty = false;
u16 native_pitch = 0;
@@ -34,6 +41,17 @@ namespace vk
mipmaps, layers, samples, initial_layout, tiling, usage, image_flags)
{}
};
struct framebuffer_holder: public vk::framebuffer, public ref_counted
{
framebuffer_holder(VkDevice dev,
VkRenderPass pass,
u32 width, u32 height,
std::vector<std::unique_ptr<vk::image_view>> &&atts)
: framebuffer(dev, pass, width, height, std::move(atts))
{}
};
}
namespace rsx
@@ -147,6 +165,9 @@ namespace rsx
{
VkImageSubresourceRange range = vk::get_image_subresource_range(0, 0, 1, 1, surface->attachment_aspect_flag);
change_image_layout(*pcmd, surface, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, range);
//Reset deref count
surface->deref_count = 0;
}
static void prepare_rtt_for_sampling(vk::command_buffer* pcmd, vk::render_target *surface)
@@ -159,6 +180,9 @@ namespace rsx
{
VkImageSubresourceRange range = vk::get_image_subresource_range(0, 0, 1, 1, surface->attachment_aspect_flag);
change_image_layout(*pcmd, surface, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, range);
//Reset deref count
surface->deref_count = 0;
}
static void prepare_ds_for_sampling(vk::command_buffer* pcmd, vk::render_target *surface)
@@ -167,15 +191,26 @@ namespace rsx
change_image_layout(*pcmd, surface, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range);
}
static void invalidate_rtt_surface_contents(vk::command_buffer*, vk::render_target*) {}
static void invalidate_rtt_surface_contents(vk::command_buffer* pcmd, vk::render_target *rtt, vk::render_target *old_surface, bool forced)
{
if (forced)
{
rtt->old_contents = old_surface;
rtt->dirty = true;
}
}
static void invalidate_depth_surface_contents(vk::command_buffer* /*pcmd*/, vk::render_target *ds)
static void invalidate_depth_surface_contents(vk::command_buffer* /*pcmd*/, vk::render_target *ds, vk::render_target *old_surface, bool /*forced*/)
{
ds->dirty = true;
ds->old_contents = old_surface;
}
static bool rtt_has_format_width_height(const std::unique_ptr<vk::render_target> &rtt, surface_color_format format, size_t width, size_t height)
static bool rtt_has_format_width_height(const std::unique_ptr<vk::render_target> &rtt, surface_color_format format, size_t width, size_t height, bool check_refs=false)
{
if (check_refs && rtt->deref_count == 0) //Surface may still have read refs from data 'copy'
return false;
VkFormat fmt = vk::get_compatible_surface_format(format).first;
if (rtt->info.format == fmt &&
@@ -186,15 +221,24 @@ namespace rsx
return false;
}
static bool ds_has_format_width_height(const std::unique_ptr<vk::render_target> &ds, surface_depth_format, size_t width, size_t height)
static bool ds_has_format_width_height(const std::unique_ptr<vk::render_target> &ds, surface_depth_format format, size_t width, size_t height, bool check_refs=false)
{
// TODO: check format
//VkFormat fmt = vk::get_compatible_depth_surface_format(format);
if (check_refs && ds->deref_count == 0) //Surface may still have read refs from data 'copy'
return false;
if (//tex.get_format() == fmt &&
ds->info.extent.width == width &&
if (ds->info.extent.width == width &&
ds->info.extent.height == height)
return true;
{
//Check format
switch (ds->info.format)
{
case VK_FORMAT_D16_UNORM:
return format == surface_depth_format::z16;
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return format == surface_depth_format::z24s8;
}
}
return false;
}
@@ -237,5 +281,16 @@ namespace rsx
m_depth_stencil_storage.clear();
invalidated_resources.clear();
}
void free_invalidated()
{
invalidated_resources.remove_if([](std::unique_ptr<vk::render_target> &rtt)
{
if (rtt->deref_count >= 2) return true;
rtt->deref_count++;
return false;
});
}
};
}
+1 -1
View File
@@ -477,7 +477,7 @@ namespace
{
const auto &vbo = vertex_buffers[i];
if (vbo.which() == 0 && vertex_count > 128 && vertex_buffers.size() > 2 && rsxthr->vertex_upload_task_ready())
if (vbo.which() == 0 && vertex_count >= (u32)g_cfg.video.mt_vertex_upload_threshold && vertex_buffers.size() > 1 && rsxthr->vertex_upload_task_ready())
{
//vertex array buffer. We can thread this thing heavily
const auto& v = vbo.get<rsx::vertex_array_buffer>();
+22 -11
View File
@@ -146,8 +146,9 @@ void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std
static const vertex_reg_info reg_table[] =
{
{ "gl_Position", false, "dst_reg0", "", false },
{ "back_diff_color", true, "dst_reg1", "", false },
{ "back_spec_color", true, "dst_reg2", "", false },
//Technically these two are for both back and front
{ "back_diff_color", true, "dst_reg1", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_FRONTDIFFUSE },
{ "back_spec_color", true, "dst_reg2", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_FRONTSPECULAR },
{ "front_diff_color", true, "dst_reg3", "", false },
{ "front_spec_color", true, "dst_reg4", "", false },
{ "fog_c", true, "dst_reg5", ".xxxx", true, "", "", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_FOG },
@@ -159,15 +160,15 @@ static const vertex_reg_info reg_table[] =
{ "gl_ClipDistance[3]", false, "dst_reg6", ".y * userClipFactor[0].w", false, "userClipEnabled[0].w > 0", "0.5", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_UC3 },
{ "gl_ClipDistance[4]", false, "dst_reg6", ".z * userClipFactor[1].x", false, "userClipEnabled[1].x > 0", "0.5", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_UC4 },
{ "gl_ClipDistance[5]", false, "dst_reg6", ".w * userClipFactor[1].y", false, "userClipEnabled[1].y > 0", "0.5", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_UC5 },
{ "tc0", true, "dst_reg7", "", false },
{ "tc1", true, "dst_reg8", "", false },
{ "tc2", true, "dst_reg9", "", false },
{ "tc3", true, "dst_reg10", "", false },
{ "tc4", true, "dst_reg11", "", false },
{ "tc5", true, "dst_reg12", "", false },
{ "tc6", true, "dst_reg13", "", false },
{ "tc7", true, "dst_reg14", "", false },
{ "tc8", true, "dst_reg15", "", false },
{ "tc0", true, "dst_reg7", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX0 },
{ "tc1", true, "dst_reg8", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX1 },
{ "tc2", true, "dst_reg9", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX2 },
{ "tc3", true, "dst_reg10", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX3 },
{ "tc4", true, "dst_reg11", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX4 },
{ "tc5", true, "dst_reg12", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX5 },
{ "tc6", true, "dst_reg13", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX6 },
{ "tc7", true, "dst_reg14", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX7 },
{ "tc8", true, "dst_reg15", "", false, "", "", "", false, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX8 },
{ "tc9", true, "dst_reg6", "", false, "", "", "", true, CELL_GCM_ATTRIB_OUTPUT_MASK_TEX9 } // In this line, dst_reg6 is correct since dst_reg goes from 0 to 15.
};
@@ -195,6 +196,16 @@ void VKVertexDecompilerThread::insertOutputs(std::stringstream & OS, const std::
const vk::varying_register_t &reg = vk::get_varying_register(i.name);
OS << "layout(location=" << reg.reg_location << ") out vec4 " << i.name << ";\n";
}
else
{
//Force some outputs to be declared even if unused so we can set default values
//NOTE: Registers that can be skept will not have their check_mask_value set
if (i.need_declare && (rsx_vertex_program.output_mask & i.check_mask_value) > 0)
{
const vk::varying_register_t &reg = vk::get_varying_register(i.name);
OS << "layout(location=" << reg.reg_location << ") out vec4 " << i.name << ";\n";
}
}
}
if (insert_back_diffuse && insert_front_diffuse)
+8 -4
View File
@@ -272,6 +272,9 @@ struct cfg_root : cfg::node
cfg::_bool lower_spu_priority{this, "Lower SPU thread priority"};
cfg::_bool spu_debug{this, "SPU Debug"};
cfg::_int<32, 16384> max_spu_immediate_write_size{this, "Maximum immediate DMA write size", 16384}; // Maximum size that an SPU thread can write directly without posting to MFC
cfg::_int<0, 6> preferred_spu_threads{this, "Preferred SPU Threads", 0}; //Numnber of hardware threads dedicated to heavy simultaneous spu tasks
cfg::_int<0, 16> spu_delay_penalty{this, "SPU delay penalty", 3}; //Number of milliseconds to block a thread if a virtual 'core' isn't free
cfg::_bool spu_loop_detection{this, "SPU loop detection", false}; //Try to detect wait loops and trigger thread yield
cfg::_enum<lib_loading_type> lib_loading{this, "Lib Loader", lib_loading_type::automatic};
cfg::_bool hook_functions{this, "Hook static functions"};
@@ -320,12 +323,13 @@ struct cfg_root : cfg::node
cfg::_bool invalidate_surface_cache_every_frame{this, "Invalidate Cache Every Frame", true};
cfg::_bool strict_rendering_mode{this, "Strict Rendering Mode"};
cfg::_bool batch_instanced_geometry{this, "Batch Instanced Geometry", false};
cfg::_int<1, 16> vertex_upload_threads{ this, "Vertex Upload Threads", 1 };
cfg::_bool batch_instanced_geometry{this, "Batch Instanced Geometry", false}; //Avoid re-uploading geometry if the same draw command is repeated
cfg::_int<1, 16> vertex_upload_threads{ this, "Vertex Upload Threads", 1 }; //Max number of threads to use for parallel vertex processing
cfg::_int<32, 65536> mt_vertex_upload_threshold{ this, "Multithreaded Vertex Upload Threshold", 4096}; //Minimum vertex count to parallelize
cfg::_bool frame_skip_enabled{this, "Enable Frame Skip"};
cfg::_int<1, 8> consequtive_frames_to_draw{this, "Consequtive Frames Drawn", 1};
cfg::_int<1, 8> consequtive_frames_to_skip{this, "Consequtive Frames Skept", 1};
cfg::_int<1, 8> consequtive_frames_to_draw{this, "Consecutive Frames Drawn", 1};
cfg::_int<1, 8> consequtive_frames_to_skip{this, "Consecutive Frames Skept", 1};
struct node_d3d12 : cfg::node
{
+2 -2
View File
@@ -98,13 +98,13 @@ struct elf_phdr<en_t, u32>
template<template<typename T> class en_t, typename sz_t>
struct elf_prog final : elf_phdr<en_t, sz_t>
{
std::vector<char> bin;
std::vector<uchar> bin;
using base = elf_phdr<en_t, sz_t>;
elf_prog() = default;
elf_prog(u32 type, u32 flags, sz_t vaddr, sz_t memsz, sz_t align, std::vector<char>&& bin)
elf_prog(u32 type, u32 flags, sz_t vaddr, sz_t memsz, sz_t align, std::vector<uchar>&& bin)
: bin(std::move(bin))
{
base::p_type = type;
+8
View File
@@ -102,6 +102,13 @@
<ClCompile Include="..\Utilities\StrFmt.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Utilities\sysinfo.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Utilities\Thread.cpp" />
<ClCompile Include="..\Utilities\version.cpp" />
<ClCompile Include="..\Utilities\VirtualMemory.cpp" />
@@ -426,6 +433,7 @@
<ClInclude Include="..\Utilities\rXml.h" />
<ClInclude Include="..\Utilities\StrFmt.h" />
<ClInclude Include="..\Utilities\StrUtil.h" />
<ClInclude Include="..\Utilities\sysinfo.h" />
<ClInclude Include="..\Utilities\Thread.h" />
<ClInclude Include="..\Utilities\Timer.h" />
<ClInclude Include="..\Utilities\types.h" />
+6
View File
@@ -923,6 +923,9 @@
<ClCompile Include="Emu\Cell\lv2\sys_ss.cpp">
<Filter>Emu\Cell\lv2</Filter>
</ClCompile>
<ClCompile Include="..\Utilities\sysinfo.cpp">
<Filter>Utilities</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">
@@ -1777,5 +1780,8 @@
<ClInclude Include="..\Utilities\CRC.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="..\Utilities\sysinfo.h">
<Filter>Utilities</Filter>
</ClInclude>
</ItemGroup>
</Project>
+7
View File
@@ -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
-1
View File
@@ -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" />
-3
View File
@@ -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>
+1 -1
View File
@@ -4,5 +4,5 @@
namespace rpcs3
{
const extern utils::version version{ 0, 0, 2, utils::version_type::alpha, 1, RPCS3_GIT_VERSION };
const extern utils::version version{ 0, 0, 3, utils::version_type::alpha, 1, RPCS3_GIT_VERSION };
}
+7 -5
View File
@@ -190,11 +190,6 @@ void debugger_frame::UpdateUI()
}
}
}
if (Emu.IsStopped())
{
g_breakpoints.clear();
}
}
void debugger_frame::UpdateUnitList()
@@ -388,6 +383,8 @@ void debugger_frame::Show_Val()
}
m_list->ShowAddr(CentrePc(pc));
}
diag->deleteLater();
}
void debugger_frame::Show_PC()
@@ -424,6 +421,11 @@ void debugger_frame::EnableButtons(bool enable)
m_btn_run->setEnabled(enable);
}
void debugger_frame::ClearBreakpoints()
{
g_breakpoints.clear();
}
debugger_list::debugger_list(debugger_frame* parent) : QListWidget(parent)
{
m_pc = 0;
+1
View File
@@ -73,6 +73,7 @@ public:
void DoUpdate();
void WriteRegs();
void EnableButtons(bool enable);
void ClearBreakpoints();
void OnUpdate();
+2 -2
View File
@@ -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);
+7 -9
View File
@@ -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(); });
+3
View File
@@ -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>
+46 -29
View File
@@ -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);
+2 -27
View File
@@ -58,9 +58,6 @@
<property name="text">
<string>Interpreter (precise)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">ppuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -68,9 +65,6 @@
<property name="text">
<string>Interpreter (fast)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">ppuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -78,9 +72,6 @@
<property name="text">
<string>Recompiler (LLVM)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">ppuBG</string>
</attribute>
</widget>
</item>
</layout>
@@ -97,9 +88,6 @@
<property name="text">
<string>Interpreter (precise)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">spuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -107,9 +95,6 @@
<property name="text">
<string>Interpreter (fast)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">spuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -117,9 +102,6 @@
<property name="text">
<string>Recompiler (ASMJIT)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">spuBG</string>
</attribute>
</widget>
</item>
<item>
@@ -130,9 +112,6 @@
<property name="text">
<string>Recompiler (LLVM)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">spuBG</string>
</attribute>
</widget>
</item>
</layout>
@@ -232,7 +211,7 @@
</sizepolicy>
</property>
<property name="text">
<string>Load automatic and manual libraries</string>
<string>Load automatic and manual selection</string>
</property>
</widget>
</item>
@@ -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>
-86
View File
@@ -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 &regs[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);
};
};
+74 -8
View File
@@ -17,9 +17,18 @@ namespace {
L"xinput9_1_0.dll"
};
inline u16 ConvertAxis(SHORT value)
inline u16 Clamp0To255(f32 input)
{
return static_cast<u16>((value + 32768l) >> 8);
if (input > 255.f)
return 255;
else if (input < 0.f)
return 0;
else return static_cast<u16>(input);
}
inline u16 ConvertAxis(float value)
{
return static_cast<u16>((value + 1.0)*(255.0 / 2.0));
}
}
@@ -45,7 +54,7 @@ void xinput_pad_handler::Init(const u32 max_connect)
{
xinputGetState = reinterpret_cast<PFN_XINPUTGETSTATE>(GetProcAddress(library, "XInputGetState"));
}
xinputSetState = reinterpret_cast<PFN_XINPUTSETSTATE>(GetProcAddress(library, "XInputSetState"));
if (xinputEnable && xinputGetState && xinputSetState)
@@ -137,6 +146,27 @@ void xinput_pad_handler::Close()
m_pads.clear();
}
std::tuple<u16, u16> xinput_pad_handler::ConvertToSquirclePoint(u16 inX, u16 inY)
{
// convert inX and Y to a (-1, 1) vector;
const f32 x = (inX - 127) / 127.f;
const f32 y = ((inY - 127) / 127.f);
// compute angle and len of given point to be used for squircle radius
const f32 angle = std::atan2(y, x);
const f32 r = std::sqrt(std::pow(x, 2.f) + std::pow(y, 2.f));
// now find len/point on the given squircle from our current angle and radius in polar coords
// https://thatsmaths.com/2016/07/14/squircles/
const f32 newLen = (1 + std::pow(std::sin(2 * angle), 2.f) / 8.f) * r;
// we now have len and angle, convert to cartisian
const int newX = Clamp0To255(((newLen * std::cos(angle)) + 1) * 127);
const int newY = Clamp0To255(((newLen * std::sin(angle)) + 1) * 127);
return std::tuple<u16, u16>(newX, newY);
}
DWORD xinput_pad_handler::ThreadProcedure()
{
// holds internal controller state change
@@ -152,7 +182,7 @@ DWORD xinput_pad_handler::ThreadProcedure()
{
auto & pad = m_pads[i];
result = (* xinputGetState)(i, &state);
result = (*xinputGetState)(i, &state);
switch (result)
{
case ERROR_DEVICE_NOT_CONNECTED:
@@ -181,10 +211,46 @@ DWORD xinput_pad_handler::ThreadProcedure()
pad.m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_pressed = state.Gamepad.bRightTrigger > 0;
pad.m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_value = state.Gamepad.bRightTrigger;
pad.m_sticks[0].m_value = ConvertAxis(state.Gamepad.sThumbLX);
pad.m_sticks[1].m_value = 255 - ConvertAxis(state.Gamepad.sThumbLY);
pad.m_sticks[2].m_value = ConvertAxis(state.Gamepad.sThumbRX);
pad.m_sticks[3].m_value = 255 - ConvertAxis(state.Gamepad.sThumbRY);
float LX, LY, RX, RY;
LX = state.Gamepad.sThumbLX;
LY = state.Gamepad.sThumbLY;
RX = state.Gamepad.sThumbRX;
RY = state.Gamepad.sThumbRY;
auto normalize_input = [](float& X, float& Y, float deadzone)
{
X /= 32767.0f;
Y /= 32767.0f;
deadzone /= 32767.0f;
float mag = sqrtf(X*X + Y*Y);
if (mag > deadzone)
{
float legalRange = 1.0f - deadzone;
float normalizedMag = std::min(1.0f, (mag - deadzone) / legalRange);
float scale = normalizedMag / mag;
X = X * scale;
Y = Y * scale;
}
else
{
X = 0;
Y = 0;
}
};
normalize_input(LX, LY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
normalize_input(RX, RY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
pad.m_sticks[0].m_value = ConvertAxis(LX);
pad.m_sticks[1].m_value = 255 - ConvertAxis(LY);
pad.m_sticks[2].m_value = ConvertAxis(RX);
pad.m_sticks[3].m_value = 255 - ConvertAxis(RY);
std::tie(pad.m_sticks[0].m_value, pad.m_sticks[1].m_value) = ConvertToSquirclePoint(pad.m_sticks[0].m_value, pad.m_sticks[1].m_value);
std::tie(pad.m_sticks[2].m_value, pad.m_sticks[3].m_value) = ConvertToSquirclePoint(pad.m_sticks[2].m_value, pad.m_sticks[3].m_value);
XINPUT_VIBRATION vibrate;
+1
View File
@@ -21,6 +21,7 @@ private:
typedef DWORD (WINAPI * PFN_XINPUTSETSTATE)(DWORD, XINPUT_VIBRATION *);
private:
std::tuple<u16, u16> ConvertToSquirclePoint(u16 inX, u16 inY);
DWORD ThreadProcedure();
static DWORD WINAPI ThreadProcProxy(LPVOID parameter);