Merge branch 'master' into vsh_fix

This commit is contained in:
Elad 2025-01-22 09:39:21 +02:00 committed by GitHub
commit c3b469a9ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
170 changed files with 3889 additions and 1616 deletions

View file

@ -44,7 +44,7 @@ add_subdirectory(libpng EXCLUDE_FROM_ALL)
# pugixml # pugixml
if (USE_SYSTEM_PUGIXML) if (USE_SYSTEM_PUGIXML)
pkg_check_modules(PUGIXML REQUIRED IMPORTED_TARGET pugixml>=1.11) pkg_check_modules(PUGIXML REQUIRED IMPORTED_TARGET pugixml>=1.15)
add_library(pugixml INTERFACE) add_library(pugixml INTERFACE)
target_link_libraries(pugixml INTERFACE PkgConfig::PUGIXML) target_link_libraries(pugixml INTERFACE PkgConfig::PUGIXML)
else() else()

@ -1 +1 @@
Subproject commit f5e92d76973a7a53f517579bc95d61483bf108c0 Subproject commit 51f5bd68b9b806d2c92b4318164d28b49357da31

2
3rdparty/pugixml vendored

@ -1 +1 @@
Subproject commit db78afc2b7d8f043b4bc6b185635d949ea2ed2a8 Subproject commit ee86beb30e4973f5feffe3ce63bfa4fbadf72f38

View file

@ -22,6 +22,5 @@ else()
add_subdirectory(wolfssl EXCLUDE_FROM_ALL) add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP) target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_NO_OPTIONS_H)
target_compile_definitions(wolfssl PUBLIC FP_MAX_BITS=8192)
endif() endif()

View file

@ -21,7 +21,7 @@ using namespace std::literals::string_literals;
#include <cwchar> #include <cwchar>
#include <Windows.h> #include <Windows.h>
static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source) static std::unique_ptr<wchar_t[]> to_wchar(std::string_view source)
{ {
// String size + null terminator // String size + null terminator
const usz buf_size = source.size() + 1; const usz buf_size = source.size() + 1;
@ -44,7 +44,7 @@ static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
std::memcpy(buffer.get() + 32768 + 4, L"UNC\\", 4 * sizeof(wchar_t)); std::memcpy(buffer.get() + 32768 + 4, L"UNC\\", 4 * sizeof(wchar_t));
} }
ensure(MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get() + 32768 + (unc ? 8 : 4), size)); // "to_wchar" ensure(MultiByteToWideChar(CP_UTF8, 0, source.data(), size, buffer.get() + 32768 + (unc ? 8 : 4), size)); // "to_wchar"
// Canonicalize wide path (replace '/', ".", "..", \\ repetitions, etc) // Canonicalize wide path (replace '/', ".", "..", \\ repetitions, etc)
ensure(GetFullPathNameW(buffer.get() + 32768, 32768, buffer.get(), nullptr) - 1 < 32768 - 1); // "to_wchar" ensure(GetFullPathNameW(buffer.get() + 32768, 32768, buffer.get(), nullptr) - 1 < 32768 - 1); // "to_wchar"
@ -2021,7 +2021,7 @@ std::string fs::get_executable_dir()
return s_exe_dir; return s_exe_dir;
} }
const std::string& fs::get_config_dir() const std::string& fs::get_config_dir([[maybe_unused]] bool get_config_subdirectory)
{ {
// Use magic static // Use magic static
static const std::string s_dir = [] static const std::string s_dir = []
@ -2103,6 +2103,14 @@ const std::string& fs::get_config_dir()
return dir; return dir;
}(); }();
#ifdef _WIN32
if (get_config_subdirectory)
{
static const std::string subdir = s_dir + "config/";
return subdir;
}
#endif
return s_dir; return s_dir;
} }
@ -2144,6 +2152,16 @@ const std::string& fs::get_cache_dir()
return s_dir; return s_dir;
} }
const std::string& fs::get_log_dir()
{
#ifdef _WIN32
static const std::string s_dir = fs::get_config_dir() + "log/";
return s_dir;
#else
return fs::get_cache_dir();
#endif
}
const std::string& fs::get_temp_dir() const std::string& fs::get_temp_dir()
{ {
static const std::string s_dir = [] static const std::string s_dir = []

View file

@ -599,12 +599,15 @@ namespace fs
// Get executable containing directory // Get executable containing directory
std::string get_executable_dir(); std::string get_executable_dir();
// Get configuration directory // Get configuration directory. Set get_config_subdirectory to true to get the nested config dir on windows.
const std::string& get_config_dir(); const std::string& get_config_dir(bool get_config_subdirectory = false);
// Get common cache directory // Get common cache directory
const std::string& get_cache_dir(); const std::string& get_cache_dir();
// Get common log directory
const std::string& get_log_dir();
// Temporary directory // Temporary directory
const std::string& get_temp_dir(); const std::string& get_temp_dir();

View file

@ -33,7 +33,8 @@ enum class thread_state : u32
aborting = 1, // The thread has been joined in the destructor or explicitly aborted aborting = 1, // The thread has been joined in the destructor or explicitly aborted
errored = 2, // Set after the emergency_exit call errored = 2, // Set after the emergency_exit call
finished = 3, // Final state, always set at the end of thread execution finished = 3, // Final state, always set at the end of thread execution
mask = 3 mask = 3,
destroying_context = 7, // Special value assigned to destroy data explicitly before the destructor
}; };
template <class Context> template <class Context>
@ -702,14 +703,17 @@ public:
thread::m_sync.notify_all(); thread::m_sync.notify_all();
} }
if (s == thread_state::finished) if (s == thread_state::finished || s == thread_state::destroying_context)
{ {
// This participates in emulation stopping, use destruction-alike semantics // This participates in emulation stopping, use destruction-alike semantics
thread::join(true); thread::join(true);
}
if (s == thread_state::destroying_context)
{
if constexpr (std::is_assignable_v<Context&, thread_state>) if constexpr (std::is_assignable_v<Context&, thread_state>)
{ {
static_cast<Context&>(*this) = thread_state::finished; static_cast<Context&>(*this) = thread_state::destroying_context;
} }
} }

View file

@ -125,19 +125,15 @@ patch_engine::patch_engine()
std::string patch_engine::get_patch_config_path() std::string patch_engine::get_patch_config_path()
{ {
#ifdef _WIN32 const std::string config_dir = fs::get_config_dir(true);
const std::string config_dir = fs::get_config_dir() + "config/";
const std::string patch_path = config_dir + "patch_config.yml"; const std::string patch_path = config_dir + "patch_config.yml";
#ifdef _WIN32
if (!fs::create_path(config_dir)) if (!fs::create_path(config_dir))
{ {
patch_log.error("Could not create path: %s (%s)", patch_path, fs::g_tls_error); patch_log.error("Could not create path: %s (%s)", patch_path, fs::g_tls_error);
} }
return patch_path;
#else
return fs::get_config_dir() + "patch_config.yml";
#endif #endif
return patch_path;
} }
std::string patch_engine::get_patches_path() std::string patch_engine::get_patches_path()
@ -1449,6 +1445,8 @@ static usz apply_modification(std::vector<u32>& applied, patch_engine::patch_inf
void patch_engine::apply(std::vector<u32>& applied_total, const std::string& name, std::function<u8*(u32, u32)> mem_translate, u32 filesz, u32 min_addr) void patch_engine::apply(std::vector<u32>& applied_total, const std::string& name, std::function<u8*(u32, u32)> mem_translate, u32 filesz, u32 min_addr)
{ {
// applied_total may be non-empty, do not clear it
if (!m_map.contains(name)) if (!m_map.contains(name))
{ {
return; return;
@ -1597,6 +1595,9 @@ void patch_engine::apply(std::vector<u32>& applied_total, const std::string& nam
} }
} }
} }
// Ensure consistent order
std::sort(applied_total.begin(), applied_total.end());
} }
void patch_engine::unload(const std::string& name) void patch_engine::unload(const std::string& name)

View file

@ -14,6 +14,7 @@ enum class cheat_type : u8
signed_16_cheat, signed_16_cheat,
signed_32_cheat, signed_32_cheat,
signed_64_cheat, signed_64_cheat,
float_32_cheat,
max max
}; };

View file

@ -49,12 +49,11 @@ std::string rXmlNode::GetName()
return {}; return {};
} }
std::string rXmlNode::GetAttribute(const std::string& name) std::string rXmlNode::GetAttribute(std::string_view name)
{ {
if (handle) if (handle)
{ {
const auto pred = [&name](const pugi::xml_attribute& attr) { return (name == attr.name()); }; if (const pugi::xml_attribute attr = handle.attribute(name))
if (const pugi::xml_attribute attr = handle.find_attribute(pred))
{ {
if (const pugi::char_t* value = attr.value()) if (const pugi::char_t* value = attr.value())
{ {
@ -86,7 +85,7 @@ rXmlDocument::rXmlDocument()
{ {
} }
pugi::xml_parse_result rXmlDocument::Read(const std::string& data) pugi::xml_parse_result rXmlDocument::Read(std::string_view data)
{ {
if (handle) if (handle)
{ {

View file

@ -23,7 +23,7 @@ struct rXmlNode
std::shared_ptr<rXmlNode> GetChildren(); std::shared_ptr<rXmlNode> GetChildren();
std::shared_ptr<rXmlNode> GetNext(); std::shared_ptr<rXmlNode> GetNext();
std::string GetName(); std::string GetName();
std::string GetAttribute(const std::string& name); std::string GetAttribute(std::string_view name);
std::string GetNodeContent(); std::string GetNodeContent();
pugi::xml_node handle{}; pugi::xml_node handle{};
@ -34,7 +34,7 @@ struct rXmlDocument
rXmlDocument(); rXmlDocument();
rXmlDocument(const rXmlDocument& other) = delete; rXmlDocument(const rXmlDocument& other) = delete;
rXmlDocument &operator=(const rXmlDocument& other) = delete; rXmlDocument &operator=(const rXmlDocument& other) = delete;
pugi::xml_parse_result Read(const std::string& data); pugi::xml_parse_result Read(std::string_view data);
virtual std::shared_ptr<rXmlNode> GetRoot(); virtual std::shared_ptr<rXmlNode> GetRoot();
pugi::xml_document handle{}; pugi::xml_document handle{};

View file

@ -164,7 +164,7 @@ elseif(WIN32)
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/GuiConfigs COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/GuiConfigs
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/git COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/git
COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt
--no-translations --no-quick --no-system-d3d-compiler --no-quick-import --no-translations --no-system-d3d-compiler --no-quick-import
--plugindir "$<IF:$<CXX_COMPILER_ID:MSVC>,$<TARGET_FILE_DIR:rpcs3>/plugins,$<TARGET_FILE_DIR:rpcs3>/share/qt6/plugins>" --plugindir "$<IF:$<CXX_COMPILER_ID:MSVC>,$<TARGET_FILE_DIR:rpcs3>/plugins,$<TARGET_FILE_DIR:rpcs3>/share/qt6/plugins>"
--verbose 0 "$<TARGET_FILE:rpcs3>") --verbose 0 "$<TARGET_FILE:rpcs3>")
endif() endif()

View file

@ -172,6 +172,7 @@ target_link_libraries(rpcs3_emu
# Cell # Cell
target_sources(rpcs3_emu PRIVATE target_sources(rpcs3_emu PRIVATE
Cell/ErrorCodes.cpp
Cell/MFC.cpp Cell/MFC.cpp
Cell/PPUAnalyser.cpp Cell/PPUAnalyser.cpp
Cell/PPUDisAsm.cpp Cell/PPUDisAsm.cpp
@ -557,6 +558,7 @@ target_sources(rpcs3_emu PRIVATE
RSX/Program/CgBinaryFragmentProgram.cpp RSX/Program/CgBinaryFragmentProgram.cpp
RSX/Program/CgBinaryVertexProgram.cpp RSX/Program/CgBinaryVertexProgram.cpp
RSX/Program/FragmentProgramDecompiler.cpp RSX/Program/FragmentProgramDecompiler.cpp
RSX/Program/FragmentProgramRegister.cpp
RSX/Program/GLSLCommon.cpp RSX/Program/GLSLCommon.cpp
RSX/Program/ProgramStateCache.cpp RSX/Program/ProgramStateCache.cpp
RSX/Program/program_util.cpp RSX/Program/program_util.cpp
@ -593,6 +595,7 @@ if(TARGET 3rdparty_vulkan)
RSX/VK/VKAsyncScheduler.cpp RSX/VK/VKAsyncScheduler.cpp
RSX/VK/VKCommandStream.cpp RSX/VK/VKCommandStream.cpp
RSX/VK/VKCommonDecompiler.cpp RSX/VK/VKCommonDecompiler.cpp
RSX/VK/VKCommonPipelineLayout.cpp
RSX/VK/VKCompute.cpp RSX/VK/VKCompute.cpp
RSX/VK/VKDMA.cpp RSX/VK/VKDMA.cpp
RSX/VK/VKDraw.cpp RSX/VK/VKDraw.cpp

View file

@ -0,0 +1,91 @@
#include "stdafx.h"
#include "ErrorCodes.h"
#include "PPUThread.h"
#include "SPUThread.h"
LOG_CHANNEL(sys_log, "SYS");
bool g_log_all_errors = false;
s32 error_code::error_report(s32 result, const logs::message* channel, const char* fmt, const fmt_type_info* sup, const u64* args)
{
static thread_local std::string g_tls_error_str;
static thread_local std::unordered_map<std::string, usz> g_tls_error_stats;
if (!channel)
{
channel = &sys_log.error;
}
if (!sup && !args)
{
if (!fmt)
{
// Report and clean error state
for (auto&& pair : g_tls_error_stats)
{
if (pair.second > 3)
{
channel->operator()("Stat: %s [x%u]", pair.first, pair.second);
}
}
g_tls_error_stats.clear();
return 0;
}
}
ensure(fmt);
const char* func = "Unknown function";
if (auto ppu = get_current_cpu_thread<ppu_thread>())
{
if (auto current = ppu->current_function)
{
func = current;
}
}
else if (auto spu = get_current_cpu_thread<spu_thread>())
{
if (auto current = spu->current_func; current && spu->start_time)
{
func = current;
}
}
// Format log message (use preallocated buffer)
g_tls_error_str.clear();
fmt::append(g_tls_error_str, "'%s' failed with 0x%08x", func, result);
// Add spacer between error and fmt if necessary
if (fmt[0] != ' ')
g_tls_error_str += " : ";
fmt::raw_append(g_tls_error_str, fmt, sup, args);
// Update stats and check log threshold
if (g_log_all_errors) [[unlikely]]
{
if (!g_tls_error_stats.empty())
{
// Report and clean error state
error_report(0, nullptr, nullptr, nullptr, nullptr);
}
channel->operator()("%s", g_tls_error_str);
}
else
{
const auto stat = ++g_tls_error_stats[g_tls_error_str];
if (stat <= 3)
{
channel->operator()("%s [%u]", g_tls_error_str, stat);
}
}
return result;
}

View file

@ -270,35 +270,75 @@ void AtracXdecContext::exec(ppu_thread& ppu)
decoder.init_avcodec(); decoder.init_avcodec();
} }
switch (savestate)
{
case atracxdec_state::initial: break;
case atracxdec_state::waiting_for_cmd: goto label1_wait_for_cmd_state;
case atracxdec_state::checking_run_thread_1: goto label2_check_run_thread_1_state;
case atracxdec_state::executing_cmd: goto label3_execute_cmd_state;
case atracxdec_state::waiting_for_output: goto label4_wait_for_output_state;
case atracxdec_state::checking_run_thread_2: goto label5_check_run_thread_2_state;
case atracxdec_state::decoding: goto label6_decode_state;
}
for (;;cmd_counter++) for (;;cmd_counter++)
{ {
cellAtracXdec.trace("Command counter: %llu, waiting for next command...", cmd_counter); cellAtracXdec.trace("Command counter: %llu, waiting for next command...", cmd_counter);
if (!skip_getting_command) for (;;)
{ {
lv2_obj::sleep(ppu); savestate = atracxdec_state::initial;
std::lock_guard lock{queue_mutex};
while (cmd_queue.empty() && !ppu.is_stopped()) ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{ {
lv2_obj::sleep(ppu); return;
queue_not_empty.wait(queue_mutex, 20000);
} }
if (ppu.is_stopped()) if (!cmd_queue.empty())
{
break;
}
savestate = atracxdec_state::waiting_for_cmd;
label1_wait_for_cmd_state:
ensure(sys_cond_wait(ppu, queue_not_empty, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{ {
ppu.state += cpu_flag::again;
return; return;
} }
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
}
cmd_queue.pop(cmd); cmd_queue.pop(cmd);
if (!run_thread) ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
savestate = atracxdec_state::checking_run_thread_1;
label2_check_run_thread_1_state:
ensure(sys_mutex_lock(ppu, run_thread_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{ {
return; return;
} }
if (!run_thread)
{
ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK);
return;
} }
ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK);
savestate = atracxdec_state::executing_cmd;
label3_execute_cmd_state:
cellAtracXdec.trace("Command type: %d", static_cast<u32>(cmd.type.get())); cellAtracXdec.trace("Command type: %d", static_cast<u32>(cmd.type.get()));
switch (cmd.type) switch (cmd.type)
@ -327,8 +367,6 @@ void AtracXdecContext::exec(ppu_thread& ppu)
} }
case AtracXdecCmdType::end_seq: case AtracXdecCmdType::end_seq:
{ {
skip_getting_command = true;
// Block savestate creation during callbacks // Block savestate creation during callbacks
std::unique_lock savestate_lock{g_fxo->get<hle_locks_t>(), std::try_to_lock}; std::unique_lock savestate_lock{g_fxo->get<hle_locks_t>(), std::try_to_lock};
@ -338,41 +376,59 @@ void AtracXdecContext::exec(ppu_thread& ppu)
return; return;
} }
skip_getting_command = false;
// Doesn't do anything else // Doesn't do anything else
notify_seq_done.cbFunc(ppu, notify_seq_done.cbArg); notify_seq_done.cbFunc(ppu, notify_seq_done.cbArg);
break; break;
} }
case AtracXdecCmdType::decode_au: case AtracXdecCmdType::decode_au:
{ {
skip_getting_command = true;
ensure(!!cmd.au_start_addr); // Not checked on LLE ensure(!!cmd.au_start_addr); // Not checked on LLE
cellAtracXdec.trace("Waiting for output to be consumed..."); cellAtracXdec.trace("Waiting for output to be consumed...");
lv2_obj::sleep(ppu); ensure(sys_mutex_lock(ppu, output_mutex, 0) == CELL_OK);
std::unique_lock output_mutex_lock{output_mutex};
while (output_locked && !ppu.is_stopped()) if (ppu.state & cpu_flag::again)
{ {
lv2_obj::sleep(ppu); return;
output_consumed.wait(output_mutex, 20000);
} }
if (ppu.is_stopped()) while (output_locked)
{
savestate = atracxdec_state::waiting_for_output;
label4_wait_for_output_state:
ensure(sys_cond_wait(ppu, output_consumed, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
return;
}
}
cellAtracXdec.trace("Output consumed");
savestate = atracxdec_state::checking_run_thread_2;
label5_check_run_thread_2_state:
ensure(sys_mutex_lock(ppu, run_thread_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{ {
ppu.state += cpu_flag::again;
return; return;
} }
if (!run_thread) if (!run_thread)
{ {
ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK);
ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK);
return; return;
} }
cellAtracXdec.trace("Output consumed"); ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK);
savestate = atracxdec_state::decoding;
label6_decode_state:
u32 error = CELL_OK; u32 error = CELL_OK;
@ -578,14 +634,12 @@ void AtracXdecContext::exec(ppu_thread& ppu)
return; return;
} }
skip_getting_command = false;
// au_done and pcm_out callbacks are always called after a decode command, even if an error occurred // au_done and pcm_out callbacks are always called after a decode command, even if an error occurred
// The output always has to be consumed as well // The output always has to be consumed as well
notify_au_done.cbFunc(ppu, cmd.pcm_handle, notify_au_done.cbArg); notify_au_done.cbFunc(ppu, cmd.pcm_handle, notify_au_done.cbArg);
output_locked = true; output_locked = true;
output_mutex_lock.unlock(); ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK);
const u32 output_size = decoded_samples_num * (decoder.bw_pcm & 0x7fu) * decoder.nch_out; const u32 output_size = decoded_samples_num * (decoder.bw_pcm & 0x7fu) * decoder.nch_out;
@ -614,29 +668,46 @@ void AtracXdecContext::exec(ppu_thread& ppu)
template <AtracXdecCmdType type> template <AtracXdecCmdType type>
error_code AtracXdecContext::send_command(ppu_thread& ppu, auto&&... args) error_code AtracXdecContext::send_command(ppu_thread& ppu, auto&&... args)
{ {
ppu.state += cpu_flag::wait; auto& savestate = *ppu.optional_savestate_state;
const bool signal = savestate.try_read<bool>().second;
savestate.clear();
if (!signal)
{ {
std::lock_guard lock{queue_mutex}; ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
return {};
}
if constexpr (type == AtracXdecCmdType::close) if constexpr (type == AtracXdecCmdType::close)
{ {
// Close command is only sent if the queue is empty on LLE // Close command is only sent if the queue is empty on LLE
if (!cmd_queue.empty()) if (!cmd_queue.empty())
{ {
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
return {}; return {};
} }
} }
if (cmd_queue.full()) if (cmd_queue.full())
{ {
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
return CELL_ADEC_ERROR_ATX_BUSY; return CELL_ADEC_ERROR_ATX_BUSY;
} }
cmd_queue.emplace(std::forward<AtracXdecCmdType>(type), std::forward<decltype(args)>(args)...); cmd_queue.emplace(std::forward<AtracXdecCmdType>(type), std::forward<decltype(args)>(args)...);
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
} }
queue_not_empty.notify_one(); ensure(sys_cond_signal(ppu, queue_not_empty) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
savestate(true);
}
return CELL_OK; return CELL_OK;
} }
@ -699,6 +770,29 @@ error_code _CellAdecCoreOpOpenExt_atracx(ppu_thread& ppu, vm::ptr<AtracXdecConte
write_to_ptr(handle.get_ptr(), AtracXdecContext(notifyAuDone, notifyAuDoneArg, notifyPcmOut, notifyPcmOutArg, notifyError, notifyErrorArg, notifySeqDone, notifySeqDoneArg, write_to_ptr(handle.get_ptr(), AtracXdecContext(notifyAuDone, notifyAuDoneArg, notifyPcmOut, notifyPcmOutArg, notifyError, notifyErrorArg, notifySeqDone, notifySeqDoneArg,
vm::bptr<u8>::make(handle.addr() + utils::align(static_cast<u32>(sizeof(AtracXdecContext)), 0x80) + ATXDEC_SPURS_STRUCTS_SIZE))); vm::bptr<u8>::make(handle.addr() + utils::align(static_cast<u32>(sizeof(AtracXdecContext)), 0x80) + ATXDEC_SPURS_STRUCTS_SIZE)));
const vm::var<sys_mutex_attribute_t> mutex_attr{{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_atd001"_u64 } }};
const vm::var<sys_cond_attribute_t> cond_attr{{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_atd002"_u64 } }};
ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::queue_mutex), mutex_attr) == CELL_OK);
ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::queue_not_empty), handle->queue_mutex, cond_attr) == CELL_OK);
mutex_attr->name_u64 = "_atd003"_u64;
cond_attr->name_u64 = "_atd004"_u64;
ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::run_thread_mutex), mutex_attr) == CELL_OK);
ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::run_thread_cond), handle->run_thread_mutex, cond_attr) == CELL_OK);
mutex_attr->name_u64 = "_atd005"_u64;
cond_attr->name_u64 = "_atd006"_u64;
ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::output_mutex), mutex_attr) == CELL_OK);
ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::output_consumed), handle->output_mutex, cond_attr) == CELL_OK);
ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK);
handle->output_locked = false;
ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK);
ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK);
const vm::var<char[]> _name = vm::make_str("HLE ATRAC3plus decoder"); const vm::var<char[]> _name = vm::make_str("HLE ATRAC3plus decoder");
const auto entry = g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(atracXdecEntry)); const auto entry = g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(atracXdecEntry));
ppu_execute<&sys_ppu_thread_create>(ppu, handle.ptr(&AtracXdecContext::thread_id), entry, handle.addr(), +res->ppuThreadPriority, +res->ppuThreadStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, +_name); ppu_execute<&sys_ppu_thread_create>(ppu, handle.ptr(&AtracXdecContext::thread_id), entry, handle.addr(), +res->ppuThreadPriority, +res->ppuThreadStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, +_name);
@ -725,29 +819,32 @@ error_code _CellAdecCoreOpClose_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext
return {}; return {};
} }
ppu.state += cpu_flag::wait;
cellAtracXdec.notice("_CellAdecCoreOpClose_atracx(handle=*0x%x)", handle); cellAtracXdec.notice("_CellAdecCoreOpClose_atracx(handle=*0x%x)", handle);
ensure(!!handle); // Not checked on LLE ensure(!!handle); // Not checked on LLE
ensure(sys_mutex_lock(ppu, handle->run_thread_mutex, 0) == CELL_OK);
handle->run_thread = false; handle->run_thread = false;
ensure(sys_mutex_unlock(ppu, handle->run_thread_mutex) == CELL_OK);
handle->send_command<AtracXdecCmdType::close>(ppu); handle->send_command<AtracXdecCmdType::close>(ppu);
{ ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK);
std::lock_guard lock{handle->output_mutex};
handle->output_locked = false; handle->output_locked = false;
} ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK);
ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK);
handle->output_consumed.notify_one(); vm::var<u64> thread_ret;
ensure(sys_ppu_thread_join(ppu, static_cast<u32>(handle->thread_id), +thread_ret) == CELL_OK);
if (vm::var<u64> ret; sys_ppu_thread_join(ppu, static_cast<u32>(handle->thread_id), +ret) != CELL_OK) error_code ret = sys_cond_destroy(ppu, handle->queue_not_empty);
{ ret = ret ? ret : sys_cond_destroy(ppu, handle->run_thread_cond);
// Other thread already closed the decoder ret = ret ? ret : sys_cond_destroy(ppu, handle->output_consumed);
return CELL_ADEC_ERROR_FATAL; ret = ret ? ret : sys_mutex_destroy(ppu, handle->queue_mutex);
} ret = ret ? ret : sys_mutex_destroy(ppu, handle->run_thread_mutex);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->output_mutex);
return CELL_OK; return ret != CELL_OK ? static_cast<error_code>(CELL_ADEC_ERROR_FATAL) : CELL_OK;
} }
error_code _CellAdecCoreOpStartSeq_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle, vm::cptr<CellAdecParamAtracX> atracxParam) error_code _CellAdecCoreOpStartSeq_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle, vm::cptr<CellAdecParamAtracX> atracxParam)
@ -808,15 +905,35 @@ error_code _CellAdecCoreOpRealign_atracx(vm::ptr<AtracXdecContext> handle, vm::p
error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle, s32 pcmHandle, vm::cptr<void> outBuffer) error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle, s32 pcmHandle, vm::cptr<void> outBuffer)
{ {
ppu.state += cpu_flag::wait;
cellAtracXdec.trace("_CellAdecCoreOpReleasePcm_atracx(handle=*0x%x, pcmHandle=%d, outBuffer=*0x%x)", handle, pcmHandle, outBuffer); cellAtracXdec.trace("_CellAdecCoreOpReleasePcm_atracx(handle=*0x%x, pcmHandle=%d, outBuffer=*0x%x)", handle, pcmHandle, outBuffer);
ensure(!!handle); // Not checked on LLE ensure(!!handle); // Not checked on LLE
std::lock_guard lock{handle->output_mutex}; auto& savestate = *ppu.optional_savestate_state;
const bool signal = savestate.try_read<bool>().second;
savestate.clear();
if (!signal)
{
ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
return {};
}
handle->output_locked = false; handle->output_locked = false;
handle->output_consumed.notify_one(); }
ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
savestate(true);
return {};
}
ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK);
return CELL_OK; return CELL_OK;
} }

View file

@ -20,7 +20,6 @@ constexpr int averror_eof = AVERROR_EOF; // Workaround for old-style-cast error
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#include "Utilities/cond.h"
#include "cellPamf.h" #include "cellPamf.h"
#include "cellAdec.h" #include "cellAdec.h"
@ -215,16 +214,28 @@ struct AtracXdecDecoder
CHECK_SIZE(AtracXdecDecoder, 0xa8); CHECK_SIZE(AtracXdecDecoder, 0xa8);
// HLE exclusive, for savestates
enum class atracxdec_state : u8
{
initial,
waiting_for_cmd,
checking_run_thread_1,
executing_cmd,
waiting_for_output,
checking_run_thread_2,
decoding
};
struct AtracXdecContext struct AtracXdecContext
{ {
be_t<u64> thread_id; // sys_ppu_thread_t be_t<u64> thread_id; // sys_ppu_thread_t
shared_mutex queue_mutex; // sys_mutex_t be_t<u32> queue_mutex; // sys_mutex_t
cond_variable queue_not_empty; // sys_cond_t be_t<u32> queue_not_empty; // sys_cond_t
AdecCmdQueue<AtracXdecCmd> cmd_queue; AdecCmdQueue<AtracXdecCmd> cmd_queue;
shared_mutex output_mutex; // sys_mutex_t be_t<u32> output_mutex; // sys_mutex_t
cond_variable output_consumed; // sys_cond_t be_t<u32> output_consumed; // sys_cond_t
be_t<u32> output_locked = false; be_t<u32> output_locked = false;
be_t<u32> run_thread_mutex; // sys_mutex_t be_t<u32> run_thread_mutex; // sys_mutex_t
@ -241,7 +252,7 @@ struct AtracXdecContext
// HLE exclusive // HLE exclusive
u64 cmd_counter = 0; // For debugging u64 cmd_counter = 0; // For debugging
AtracXdecCmd cmd; // For savestates; if savestate was created while processing a decode command, we need to save the current command AtracXdecCmd cmd; // For savestates; if savestate was created while processing a decode command, we need to save the current command
b8 skip_getting_command = false; // For savestates; skips getting a new command from the queue atracxdec_state savestate{}; // For savestates
b8 skip_next_frame; // Needed to emulate behavior of LLE SPU program, it doesn't output the first frame after a sequence reset or error b8 skip_next_frame; // Needed to emulate behavior of LLE SPU program, it doesn't output the first frame after a sequence reset or error
u8 spurs_stuff[58]; // 120 bytes on LLE, pointers to CellSpurs, CellSpursTaskset, etc. u8 spurs_stuff[58]; // 120 bytes on LLE, pointers to CellSpurs, CellSpursTaskset, etc.

View file

@ -148,9 +148,44 @@ void camera_context::save(utils::serial& ar)
return; return;
} }
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera); const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera);
ar(notify_data_map, start_timestamp_us, read_mode, is_streaming, is_attached, is_open, info, attr, frame_num); ar(notify_data_map, start_timestamp_us, read_mode, is_streaming, is_attached, is_open, info, attr, frame_num);
if (ar.is_writing() || version >= 2)
{
ar(is_attached_dirty);
}
if (!ar.is_writing())
{
if (is_open)
{
if (!open_camera())
{
cellCamera.error("Failed to open camera while loading savestate");
}
else if (is_streaming && !start_camera())
{
cellCamera.error("Failed to start camera while loading savestate");
}
}
}
}
gem_camera_shared::gem_camera_shared(utils::serial& ar)
{
save(ar);
}
void gem_camera_shared::save(utils::serial& ar)
{
const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera);
if (ar.is_writing() || version >= 2)
{
ar(frame_timestamp_us, width, height, size, format);
}
} }
static bool check_dev_num(s32 dev_num) static bool check_dev_num(s32 dev_num)
@ -435,7 +470,6 @@ error_code cellCameraInit()
g_camera.attr[CELL_CAMERA_USBLOAD] = { 4 }; g_camera.attr[CELL_CAMERA_USBLOAD] = { 4 };
break; break;
} }
case fake_camera_type::eyetoy2: case fake_camera_type::eyetoy2:
{ {
g_camera.attr[CELL_CAMERA_SATURATION] = { 64 }; g_camera.attr[CELL_CAMERA_SATURATION] = { 64 };
@ -455,7 +489,6 @@ error_code cellCameraInit()
g_camera.attr[CELL_CAMERA_AGCHIGH] = { 64 }; g_camera.attr[CELL_CAMERA_AGCHIGH] = { 64 };
break; break;
} }
case fake_camera_type::uvc1_1: case fake_camera_type::uvc1_1:
{ {
g_camera.attr[CELL_CAMERA_DEVICEID] = { 0x5ca, 0x18d0 }; // KBCR-S01MU g_camera.attr[CELL_CAMERA_DEVICEID] = { 0x5ca, 0x18d0 }; // KBCR-S01MU
@ -463,14 +496,14 @@ error_code cellCameraInit()
g_camera.attr[CELL_CAMERA_NUMFRAME] = { 1 }; // Amount of supported resolutions g_camera.attr[CELL_CAMERA_NUMFRAME] = { 1 }; // Amount of supported resolutions
break; break;
} }
default: default:
cellCamera.todo("Trying to init cellCamera with un-researched camera type."); cellCamera.todo("Trying to init cellCamera with un-researched camera type.");
break;
} }
// TODO: Some other default attributes? Need to check the actual behaviour on a real PS3. // TODO: Some other default attributes? Need to check the actual behaviour on a real PS3.
g_camera.is_attached = true; g_camera.is_attached = g_cfg.io.camera != camera_handler::null;
g_camera.init = 1; g_camera.init = 1;
return CELL_OK; return CELL_OK;
} }
@ -816,8 +849,8 @@ s32 cellCameraIsAttached(s32 dev_num)
// normally should be attached immediately after event queue is registered, but just to be sure // normally should be attached immediately after event queue is registered, but just to be sure
if (!is_attached) if (!is_attached)
{ {
g_camera.send_attach_state(true); g_camera.is_attached = is_attached = true;
is_attached = g_camera.is_attached; g_camera.is_attached_dirty = true;
} }
} }
@ -1606,9 +1639,15 @@ void camera_context::operator()()
{ {
while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped())
{ {
// send ATTACH event
if (init && is_attached_dirty && !Emu.IsPaused())
{
send_attach_state(is_attached);
}
const s32 fps = info.framerate; const s32 fps = info.framerate;
if (!fps || Emu.IsPaused() || g_cfg.io.camera == camera_handler::null) if (!init || !fps || Emu.IsPaused() || g_cfg.io.camera == camera_handler::null)
{ {
thread_ctrl::wait_for(1000); // hack thread_ctrl::wait_for(1000); // hack
continue; continue;
@ -1783,6 +1822,7 @@ void camera_context::reset_state()
read_mode = CELL_CAMERA_READ_FUNCCALL; read_mode = CELL_CAMERA_READ_FUNCCALL;
is_streaming = false; is_streaming = false;
is_attached = false; is_attached = false;
is_attached_dirty = false;
is_open = false; is_open = false;
info.framerate = 0; info.framerate = 0;
std::memset(&attr, 0, sizeof(attr)); std::memset(&attr, 0, sizeof(attr));
@ -1828,6 +1868,7 @@ void camera_context::send_attach_state(bool attached)
// We're not expected to send any events for attaching/detaching // We're not expected to send any events for attaching/detaching
is_attached = attached; is_attached = attached;
is_attached_dirty = false;
} }
void camera_context::set_attr(s32 attrib, u32 arg1, u32 arg2) void camera_context::set_attr(s32 attrib, u32 arg1, u32 arg2)
@ -1862,15 +1903,13 @@ void camera_context::set_attr(s32 attrib, u32 arg1, u32 arg2)
void camera_context::add_queue(u64 key, u64 source, u64 flag) void camera_context::add_queue(u64 key, u64 source, u64 flag)
{ {
std::lock_guard lock(mutex);
{ {
std::lock_guard lock_data_map(mutex_notify_data_map); std::lock_guard lock_data_map(mutex_notify_data_map);
notify_data_map[key] = { source, flag }; notify_data_map[key] = { source, flag };
} }
// send ATTACH event - HACKY is_attached_dirty = true;
send_attach_state(is_attached);
} }
void camera_context::remove_queue(u64 key) void camera_context::remove_queue(u64 key)
@ -1897,7 +1936,8 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state
{ {
if (is_attached) if (is_attached)
{ {
send_attach_state(false); is_attached = false;
is_attached_dirty = true;
} }
if (handler) if (handler)
{ {
@ -1938,7 +1978,8 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state
if (!is_attached) if (!is_attached)
{ {
cellCamera.warning("Camera handler not attached. Sending attach event...", static_cast<int>(state)); cellCamera.warning("Camera handler not attached. Sending attach event...", static_cast<int>(state));
send_attach_state(true); is_attached = true;
is_attached_dirty = true;
} }
break; break;
} }

View file

@ -427,6 +427,7 @@ public:
atomic_t<u8> read_mode{CELL_CAMERA_READ_FUNCCALL}; atomic_t<u8> read_mode{CELL_CAMERA_READ_FUNCCALL};
atomic_t<bool> is_streaming{false}; atomic_t<bool> is_streaming{false};
atomic_t<bool> is_attached{false}; atomic_t<bool> is_attached{false};
atomic_t<bool> is_attached_dirty{false};
atomic_t<bool> is_open{false}; atomic_t<bool> is_open{false};
CellCameraInfoEx info{}; CellCameraInfoEx info{};
@ -471,6 +472,13 @@ using camera_thread = named_thread<camera_context>;
/// Shared data between cellGem and cellCamera /// Shared data between cellGem and cellCamera
struct gem_camera_shared struct gem_camera_shared
{ {
gem_camera_shared() {}
gem_camera_shared(utils::serial& ar);
void save(utils::serial& ar);
SAVESTATE_INIT_POS(7);
atomic_t<u64> frame_timestamp_us{}; // latest read timestamp from cellCamera (cellCameraRead(Ex)) atomic_t<u64> frame_timestamp_us{}; // latest read timestamp from cellCamera (cellCameraRead(Ex))
atomic_t<s32> width{640}; atomic_t<s32> width{640};
atomic_t<s32> height{480}; atomic_t<s32> height{480};

File diff suppressed because it is too large Load diff

View file

@ -196,7 +196,7 @@ bool cellPad_NotifyStateChange(usz index, u64 /*state*/, bool locked, bool is_bl
return true; return true;
} }
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[index]; const auto& pad = pads[index];
@ -268,7 +268,7 @@ error_code cellPadInit(ppu_thread& ppu, u32 max_connect)
config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF); config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF);
config.reported_info = {}; config.reported_info = {};
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
for (usz i = 0; i < config.get_max_connect(); ++i) for (usz i = 0; i < config.get_max_connect(); ++i)
@ -336,7 +336,7 @@ error_code cellPadClearBuf(u32 port_no)
if (port_no >= config.get_max_connect()) if (port_no >= config.get_max_connect())
return CELL_PAD_ERROR_NO_DEVICE; return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -351,7 +351,7 @@ error_code cellPadClearBuf(u32 port_no)
void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
{ {
auto& config = g_fxo->get<pad_info>(); auto& config = g_fxo->get<pad_info>();
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pad = handler->GetPads()[port_no]; const auto& pad = handler->GetPads()[port_no];
const PadInfo& rinfo = handler->GetInfo(); const PadInfo& rinfo = handler->GetInfo();
@ -709,7 +709,7 @@ error_code cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
if (port_no >= config.get_max_connect()) if (port_no >= config.get_max_connect())
return CELL_PAD_ERROR_NO_DEVICE; return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -740,7 +740,7 @@ error_code cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
if (!info) if (!info)
return CELL_PAD_ERROR_INVALID_PARAMETER; return CELL_PAD_ERROR_INVALID_PARAMETER;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const PadInfo& rinfo = handler->GetInfo(); const PadInfo& rinfo = handler->GetInfo();
std::memset(info.get_ptr(), 0, sizeof(CellPadPeriphInfo)); std::memset(info.get_ptr(), 0, sizeof(CellPadPeriphInfo));
@ -795,7 +795,7 @@ error_code cellPadPeriphGetData(u32 port_no, vm::ptr<CellPadPeriphData> data)
if (port_no >= config.get_max_connect()) if (port_no >= config.get_max_connect())
return CELL_PAD_ERROR_NO_DEVICE; return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -827,7 +827,7 @@ error_code cellPadGetRawData(u32 port_no, vm::ptr<CellPadData> data)
if (port_no >= config.get_max_connect()) if (port_no >= config.get_max_connect())
return CELL_PAD_ERROR_NO_DEVICE; return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -891,7 +891,7 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr<CellPadActParam> param)
if (port_no >= config.get_max_connect()) if (port_no >= config.get_max_connect())
return CELL_PAD_ERROR_NO_DEVICE; return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -923,7 +923,7 @@ error_code cellPadGetInfo(vm::ptr<CellPadInfo> info)
std::memset(info.get_ptr(), 0, sizeof(CellPadInfo)); std::memset(info.get_ptr(), 0, sizeof(CellPadInfo));
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const PadInfo& rinfo = handler->GetInfo(); const PadInfo& rinfo = handler->GetInfo();
info->max_connect = config.max_connect; info->max_connect = config.max_connect;
info->system_info = rinfo.system_info; info->system_info = rinfo.system_info;
@ -968,7 +968,7 @@ error_code cellPadGetInfo2(vm::ptr<CellPadInfo2> info)
std::memset(info.get_ptr(), 0, sizeof(CellPadInfo2)); std::memset(info.get_ptr(), 0, sizeof(CellPadInfo2));
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const PadInfo& rinfo = handler->GetInfo(); const PadInfo& rinfo = handler->GetInfo();
info->max_connect = config.get_max_connect(); // Here it is forcibly clamped info->max_connect = config.get_max_connect(); // Here it is forcibly clamped
info->system_info = rinfo.system_info; info->system_info = rinfo.system_info;
@ -1018,7 +1018,7 @@ error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr<CellPadCapabilityInfo>
if (port_no >= config.get_max_connect()) if (port_no >= config.get_max_connect())
return CELL_PAD_ERROR_NO_DEVICE; return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -1074,7 +1074,7 @@ error_code cellPadInfoPressMode(u32 port_no)
if (port_no >= config.get_max_connect()) if (port_no >= config.get_max_connect())
return CELL_PAD_ERROR_NO_DEVICE; return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -1101,7 +1101,7 @@ error_code cellPadInfoSensorMode(u32 port_no)
if (port_no >= config.get_max_connect()) if (port_no >= config.get_max_connect())
return CELL_PAD_ERROR_NO_DEVICE; return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -1129,7 +1129,7 @@ error_code cellPadSetPressMode(u32 port_no, u32 mode)
if (port_no >= CELL_PAD_MAX_PORT_NUM) if (port_no >= CELL_PAD_MAX_PORT_NUM)
return CELL_OK; return CELL_OK;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -1163,7 +1163,7 @@ error_code cellPadSetSensorMode(u32 port_no, u32 mode)
if (port_no >= CELL_PAD_MAX_PORT_NUM) if (port_no >= CELL_PAD_MAX_PORT_NUM)
return CELL_OK; return CELL_OK;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = pads[port_no]; const auto& pad = pads[port_no];
@ -1190,7 +1190,7 @@ error_code cellPadLddRegisterController()
if (!config.max_connect) if (!config.max_connect)
return CELL_PAD_ERROR_UNINITIALIZED; return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const s32 handle = handler->AddLddPad(); const s32 handle = handler->AddLddPad();
@ -1215,7 +1215,7 @@ error_code cellPadLddDataInsert(s32 handle, vm::ptr<CellPadData> data)
if (!config.max_connect) if (!config.max_connect)
return CELL_PAD_ERROR_UNINITIALIZED; return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
auto& pads = handler->GetPads(); auto& pads = handler->GetPads();
if (handle < 0 || static_cast<u32>(handle) >= pads.size() || !data) // data == NULL stalls on decr if (handle < 0 || static_cast<u32>(handle) >= pads.size() || !data) // data == NULL stalls on decr
@ -1240,7 +1240,7 @@ error_code cellPadLddGetPortNo(s32 handle)
if (!config.max_connect) if (!config.max_connect)
return CELL_PAD_ERROR_UNINITIALIZED; return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
auto& pads = handler->GetPads(); auto& pads = handler->GetPads();
if (handle < 0 || static_cast<u32>(handle) >= pads.size()) if (handle < 0 || static_cast<u32>(handle) >= pads.size())
@ -1264,7 +1264,7 @@ error_code cellPadLddUnregisterController(s32 handle)
if (!config.max_connect) if (!config.max_connect)
return CELL_PAD_ERROR_UNINITIALIZED; return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
if (handle < 0 || static_cast<u32>(handle) >= pads.size()) if (handle < 0 || static_cast<u32>(handle) >= pads.size())

View file

@ -221,7 +221,7 @@ error_code sys_prx_get_module_info(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<s
opt->info = info; opt->info = info;
// Call the syscall // Call the syscall
return _sys_prx_get_module_info(ppu, id, 0, opt); return _sys_prx_get_module_info(ppu, id, flags, opt);
} }
error_code sys_prx_get_module_id_by_name(ppu_thread& ppu, vm::cptr<char> name, u64 flags, vm::ptr<sys_prx_get_module_id_by_name_option_t> pOpt) error_code sys_prx_get_module_id_by_name(ppu_thread& ppu, vm::cptr<char> name, u64 flags, vm::ptr<sys_prx_get_module_id_by_name_option_t> pOpt)

View file

@ -96,6 +96,7 @@ struct ppu_module : public Type
std::vector<ppu_segment> segs{}; std::vector<ppu_segment> segs{};
std::vector<ppu_segment> secs{}; std::vector<ppu_segment> secs{};
std::vector<ppu_function> funcs{}; std::vector<ppu_function> funcs{};
std::vector<u32> applied_patches;
std::deque<std::shared_ptr<void>> allocations; std::deque<std::shared_ptr<void>> allocations;
std::map<u32, u32> addr_to_seg_index; std::map<u32, u32> addr_to_seg_index;
@ -185,7 +186,6 @@ struct main_ppu_module : public ppu_module<T>
{ {
u32 elf_entry{}; u32 elf_entry{};
u32 seg0_code_end{}; u32 seg0_code_end{};
std::vector<u32> applied_patches;
// Disable inherited savestate ordering // Disable inherited savestate ordering
void save(utils::serial&) = delete; void save(utils::serial&) = delete;

View file

@ -1816,6 +1816,9 @@ shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_load, c
prx->module_info_version[1] = lib_info->version[1]; prx->module_info_version[1] = lib_info->version[1];
prx->module_info_attributes = lib_info->attributes; prx->module_info_attributes = lib_info->attributes;
prx->imports_start = lib_info->imports_start;
prx->imports_end = lib_info->imports_end;
prx->exports_start = lib_info->exports_start; prx->exports_start = lib_info->exports_start;
prx->exports_end = lib_info->exports_end; prx->exports_end = lib_info->exports_end;
@ -1947,6 +1950,7 @@ shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_load, c
ppu_check_patch_spu_images(*prx, seg); ppu_check_patch_spu_images(*prx, seg);
} }
prx->applied_patches = applied;
prx->analyse(toc, 0, end, applied, exported_funcs); prx->analyse(toc, 0, end, applied, exported_funcs);
if (!ar && !virtual_load) if (!ar && !virtual_load)

View file

@ -4898,6 +4898,30 @@ bool ppu_initialize(const ppu_module<lv2_obj>& info, bool check_only, u64 file_s
sha1_update(&ctx, ensure(info.get_ptr<const u8>(func.addr)), func.size); sha1_update(&ctx, ensure(info.get_ptr<const u8>(func.addr)), func.size);
} }
if (!workload.empty() && fpos >= info.funcs.size())
{
// Hash the entire function grouped addresses for the integrity of the symbol resolver function
// Potentially occuring during patches
// Avoid doing it for files with a single module such as most PRX
std::vector<be_t<u32>> addrs;
for (const ppu_function& func : info.funcs)
{
if (func.size == 0)
{
continue;
}
addrs.emplace_back(func.addr - reloc);
}
// Hash its size too
addrs.emplace_back(::size32(addrs));
sha1_update(&ctx, reinterpret_cast<const u8*>(addrs.data()), addrs.size() * sizeof(be_t<u32>));
}
if (false) if (false)
{ {
const be_t<u64> forced_upd = 3; const be_t<u64> forced_upd = 3;

View file

@ -5481,7 +5481,7 @@ bool spu_thread::reservation_check(u32 addr, u32 hash, atomic_t<u64, 64>* range_
usz spu_thread::register_cache_line_waiter(u32 addr) usz spu_thread::register_cache_line_waiter(u32 addr)
{ {
const u64 value = u64{compute_rdata_hash32(rdata)} << 32 | raddr; const u64 value = u64{compute_rdata_hash32(rdata)} << 32 | addr;
for (usz i = 0; i < std::size(g_spu_waiters_by_value); i++) for (usz i = 0; i < std::size(g_spu_waiters_by_value); i++)
{ {

View file

@ -122,7 +122,7 @@ void lv2_config::remove_service_event(u32 id)
lv2_config_service_event& lv2_config_service_event::operator=(thread_state s) noexcept lv2_config_service_event& lv2_config_service_event::operator=(thread_state s) noexcept
{ {
if (s == thread_state::finished) if (s == thread_state::destroying_context && !m_destroyed.exchange(true))
{ {
if (auto global = g_fxo->try_get<lv2_config>()) if (auto global = g_fxo->try_get<lv2_config>())
{ {
@ -133,6 +133,23 @@ lv2_config_service_event& lv2_config_service_event::operator=(thread_state s) no
return *this; return *this;
} }
lv2_config_service_event::~lv2_config_service_event() noexcept
{
operator=(thread_state::destroying_context);
}
lv2_config::~lv2_config() noexcept
{
for (auto& [key, event] : events)
{
if (event)
{
// Avoid collision with lv2_config_service_event destructor
event->m_destroyed = true;
}
}
}
// LV2 Config Service Listener // LV2 Config Service Listener
bool lv2_config_service_listener::check_service(const lv2_config_service& service) const bool lv2_config_service_listener::check_service(const lv2_config_service& service) const
{ {

View file

@ -161,6 +161,8 @@ public:
return null_ptr; return null_ptr;
} }
~lv2_config() noexcept;
}; };
/* /*
@ -276,7 +278,7 @@ public:
// Utilities // Utilities
usz get_size() const { return sizeof(sys_config_service_event_t)-1 + data.size(); } usz get_size() const { return sizeof(sys_config_service_event_t)-1 + data.size(); }
shared_ptr<lv2_config_service> get_shared_ptr () const { return idm::get_unlocked<lv2_config_service>(idm_id); } shared_ptr<lv2_config_service> get_shared_ptr () const { return stx::make_shared_from_this<lv2_config_service>(this); }
u32 get_id() const { return idm_id; } u32 get_id() const { return idm_id; }
}; };
@ -342,7 +344,7 @@ public:
// Utilities // Utilities
u32 get_id() const { return idm_id; } u32 get_id() const { return idm_id; }
shared_ptr<lv2_config_service_listener> get_shared_ptr() const { return idm::get_unlocked<lv2_config_service_listener>(idm_id); } shared_ptr<lv2_config_service_listener> get_shared_ptr() const { return stx::make_shared_from_this<lv2_config_service_listener>(this); }
}; };
/* /*
@ -360,6 +362,10 @@ class lv2_config_service_event
return g_fxo->get<service_event_id>().next_id++; return g_fxo->get<service_event_id>().next_id++;
} }
atomic_t<bool> m_destroyed = false;
friend class lv2_config;
public: public:
const u32 id; const u32 id;
@ -391,8 +397,7 @@ public:
// Destructor // Destructor
lv2_config_service_event& operator=(thread_state s) noexcept; lv2_config_service_event& operator=(thread_state s) noexcept;
~lv2_config_service_event() noexcept;
~lv2_config_service_event() noexcept = default;
// Notify queue that this event exists // Notify queue that this event exists
bool notify() const; bool notify() const;

View file

@ -420,10 +420,8 @@ error_code sys_event_queue_tryreceive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sy
while (count < size && !queue->events.empty()) while (count < size && !queue->events.empty())
{ {
auto& dest = events[count++]; auto& dest = events[count++];
const auto event = queue->events.front(); std::tie(dest.source, dest.data1, dest.data2, dest.data3) = queue->events.front();
queue->events.pop_front(); queue->events.pop_front();
std::tie(dest.source, dest.data1, dest.data2, dest.data3) = event;
} }
lock.unlock(); lock.unlock();

View file

@ -178,7 +178,7 @@ void lv2_socket::queue_wake(ppu_thread* ppu)
lv2_socket& lv2_socket::operator=(thread_state s) noexcept lv2_socket& lv2_socket::operator=(thread_state s) noexcept
{ {
if (s == thread_state::finished) if (s == thread_state::destroying_context)
{ {
close(); close();
} }

View file

@ -11,7 +11,6 @@ struct lv2_overlay final : ppu_module<lv2_obj>
u32 entry{}; u32 entry{};
u32 seg0_code_end{}; u32 seg0_code_end{};
std::vector<u32> applied_patches;
lv2_overlay() = default; lv2_overlay() = default;
lv2_overlay(utils::serial&){} lv2_overlay(utils::serial&){}

View file

@ -271,7 +271,7 @@ error_code _sys_process_get_paramsfo(vm::ptr<char> buffer)
{ {
sys_process.warning("_sys_process_get_paramsfo(buffer=0x%x)", buffer); sys_process.warning("_sys_process_get_paramsfo(buffer=0x%x)", buffer);
if (!Emu.GetTitleID().length()) if (Emu.GetTitleID().empty())
{ {
return CELL_ENOENT; return CELL_ENOENT;
} }
@ -498,6 +498,9 @@ void lv2_exitspawn(ppu_thread& ppu, std::vector<std::string>& argv, std::vector<
}; };
signal_system_cache_can_stay(); signal_system_cache_can_stay();
// Make sure we keep the game window opened
Emu.SetContinuousMode(true);
Emu.Kill(false); Emu.Kill(false);
}); });

View file

@ -1034,7 +1034,7 @@ error_code _sys_prx_get_module_info(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<
return CELL_EFAULT; return CELL_EFAULT;
} }
if (pOpt->info->size != pOpt->info.size()) if (pOpt->info->size != pOpt->info.size() && pOpt->info_v2->size != pOpt->info_v2.size())
{ {
return CELL_EINVAL; return CELL_EINVAL;
} }
@ -1072,6 +1072,14 @@ error_code _sys_prx_get_module_info(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<
pOpt->info->segments_num = i; pOpt->info->segments_num = i;
} }
if (pOpt->info_v2->size == pOpt->info_v2.size())
{
pOpt->info_v2->exports_addr = prx->exports_start;
pOpt->info_v2->exports_size = prx->exports_end - prx->exports_start;
pOpt->info_v2->imports_addr = prx->imports_start;
pOpt->info_v2->imports_size = prx->imports_end - prx->imports_start;
}
return CELL_OK; return CELL_OK;
} }
@ -1079,11 +1087,30 @@ error_code _sys_prx_get_module_id_by_name(ppu_thread& ppu, vm::cptr<char> name,
{ {
ppu.state += cpu_flag::wait; ppu.state += cpu_flag::wait;
sys_prx.todo("_sys_prx_get_module_id_by_name(name=%s, flags=%d, pOpt=*0x%x)", name, flags, pOpt); sys_prx.warning("_sys_prx_get_module_id_by_name(name=%s, flags=%d, pOpt=*0x%x)", name, flags, pOpt);
//if (realName == "?") ... std::string module_name;
if (!vm::read_string(name.addr(), 28, module_name))
{
return CELL_EINVAL;
}
return not_an_error(CELL_PRX_ERROR_UNKNOWN_MODULE); const auto [prx, id] = idm::select<lv2_obj, lv2_prx>([&](u32 id, lv2_prx& prx) -> u32
{
if (strncmp(module_name.c_str(), prx.module_info_name, sizeof(prx.module_info_name)) == 0)
{
return id;
}
return 0;
});
if (!id)
{
return CELL_PRX_ERROR_UNKNOWN_MODULE;
}
return not_an_error(id);
} }
error_code _sys_prx_get_module_id_by_address(ppu_thread& ppu, u32 addr) error_code _sys_prx_get_module_id_by_address(ppu_thread& ppu, u32 addr)

View file

@ -76,10 +76,22 @@ struct sys_prx_module_info_t
be_t<u32> segments_num; // 0x44 be_t<u32> segments_num; // 0x44
}; };
struct sys_prx_module_info_v2_t : sys_prx_module_info_t
{
be_t<u32> exports_addr; // 0x48
be_t<u32> exports_size; // 0x4C
be_t<u32> imports_addr; // 0x50
be_t<u32> imports_size; // 0x54
};
struct sys_prx_module_info_option_t struct sys_prx_module_info_option_t
{ {
be_t<u64> size; // 0x10 be_t<u64> size; // 0x10
union
{
vm::bptr<sys_prx_module_info_t> info; vm::bptr<sys_prx_module_info_t> info;
vm::bptr<sys_prx_module_info_v2_t> info_v2;
};
}; };
struct sys_prx_start_module_option_t struct sys_prx_start_module_option_t
@ -192,6 +204,9 @@ struct lv2_prx final : ppu_module<lv2_obj>
u8 module_info_version[2]{}; u8 module_info_version[2]{};
be_t<u16> module_info_attributes{}; be_t<u16> module_info_attributes{};
u32 imports_start = umax;
u32 imports_end = 0;
u32 exports_start = umax; u32 exports_start = umax;
u32 exports_end = 0; u32 exports_end = 0;

View file

@ -25,7 +25,7 @@ void cfg_ipc::load()
void cfg_ipc::save() const void cfg_ipc::save() const
{ {
#ifdef _WIN32 #ifdef _WIN32
const std::string path_to_cfg = fs::get_config_dir() + "config/"; const std::string path_to_cfg = fs::get_config_dir(true);
if (!fs::create_path(path_to_cfg)) if (!fs::create_path(path_to_cfg))
{ {
IPC.error("Could not create path: %s", path_to_cfg); IPC.error("Could not create path: %s", path_to_cfg);
@ -42,11 +42,7 @@ void cfg_ipc::save() const
std::string cfg_ipc::get_path() std::string cfg_ipc::get_path()
{ {
#ifdef _WIN32 return fs::get_config_dir(true) + "ipc.yml";
return fs::get_config_dir() + "config/ipc.yml";
#else
return fs::get_config_dir() + "ipc.yml";
#endif
} }
bool cfg_ipc::get_server_enabled() const bool cfg_ipc::get_server_enabled() const

View file

@ -805,8 +805,8 @@ public:
{ {
if (ptr) if (ptr)
{ {
constexpr thread_state finished{3}; constexpr thread_state destroying_context{7};
*static_cast<Get*>(ptr.get()) = finished; *static_cast<Get*>(ptr.get()) = destroying_context;
} }
} }
@ -837,8 +837,8 @@ public:
{ {
if (ptr) if (ptr)
{ {
constexpr thread_state finished{3}; constexpr thread_state destroying_context{7};
*static_cast<Get*>(ptr.get()) = finished; *static_cast<Get*>(ptr.get()) = destroying_context;
} }
} }

View file

@ -146,7 +146,7 @@ void usb_device_buzz::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/
} }
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
ensure(pads.size() > m_last_controller); ensure(pads.size() > m_last_controller);
ensure(g_cfg_buzz.players.size() > m_last_controller); ensure(g_cfg_buzz.players.size() > m_last_controller);
@ -161,7 +161,7 @@ void usb_device_buzz::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/
} }
const auto& cfg = g_cfg_buzz.players[i]; const auto& cfg = g_cfg_buzz.players[i];
cfg->handle_input(pad, true, [&buf, &index](buzz_btn btn, u16 /*value*/, bool pressed) cfg->handle_input(pad, true, [&buf, &index](buzz_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/)
{ {
if (!pressed) if (!pressed)
return; return;

View file

@ -138,7 +138,7 @@ void usb_device_ghltar::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint
} }
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), m_controller_index); const auto& pad = ::at32(handler->GetPads(), m_controller_index);
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
@ -147,7 +147,7 @@ void usb_device_ghltar::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint
} }
const auto& cfg = ::at32(g_cfg_ghltar.players, m_controller_index); const auto& cfg = ::at32(g_cfg_ghltar.players, m_controller_index);
cfg->handle_input(pad, true, [&buf](ghltar_btn btn, u16 value, bool pressed) cfg->handle_input(pad, true, [&buf](ghltar_btn btn, pad_button /*pad_btn*/, u16 value, bool pressed, bool& /*abort*/)
{ {
if (!pressed) if (!pressed)
return; return;

View file

@ -195,7 +195,7 @@ void usb_device_gametablet::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endp
{ {
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto gamepad_handler = pad::get_current_handler(); const auto gamepad_handler = pad::get_pad_thread();
const auto& pads = gamepad_handler->GetPads(); const auto& pads = gamepad_handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)

View file

@ -227,7 +227,7 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint,
return; return;
} }
const auto input_callback = [&gc](guncon3_btn btn, u16 value, bool pressed) const auto input_callback = [&gc](guncon3_btn btn, pad_button /*pad_button*/, u16 value, bool pressed, bool& /*abort*/)
{ {
if (!pressed) if (!pressed)
return; return;
@ -255,7 +255,7 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint,
{ {
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto gamepad_handler = pad::get_current_handler(); const auto gamepad_handler = pad::get_pad_thread();
const auto& pads = gamepad_handler->GetPads(); const auto& pads = gamepad_handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)

View file

@ -31,12 +31,12 @@ void MouseHandlerBase::save(utils::serial& ar)
ar(inited ? m_info.max_connect : 0); ar(inited ? m_info.max_connect : 0);
} }
bool MouseHandlerBase::is_time_for_update(double elapsed_time) bool MouseHandlerBase::is_time_for_update(double elapsed_time_ms)
{ {
steady_clock::time_point now = steady_clock::now(); steady_clock::time_point now = steady_clock::now();
double elapsed = (now - last_update).count() / 1000'000.; const double elapsed_ms = (now - last_update).count() / 1'000'000.;
if (elapsed > elapsed_time) if (elapsed_ms > elapsed_time_ms)
{ {
last_update = now; last_update = now;
return true; return true;

View file

@ -127,7 +127,7 @@ protected:
std::vector<Mouse> m_mice; std::vector<Mouse> m_mice;
steady_clock::time_point last_update{}; steady_clock::time_point last_update{};
bool is_time_for_update(double elapsed_time = 10.0); // 4-10 ms, let's use 10 for now bool is_time_for_update(double elapsed_time_ms = 10.0); // 4-10 ms, let's use 10 for now
public: public:
shared_mutex mutex; shared_mutex mutex;

View file

@ -48,6 +48,7 @@ public:
cfg->pressure_intensity_button.def = ""; cfg->pressure_intensity_button.def = "";
cfg->analog_limiter_button.def = ""; cfg->analog_limiter_button.def = "";
cfg->orientation_reset_button.def = "";
// Apply defaults // Apply defaults
cfg->from_default(); cfg->from_default();

View file

@ -2,6 +2,7 @@
#include "PadHandler.h" #include "PadHandler.h"
#include "Emu/system_utils.hpp" #include "Emu/system_utils.hpp"
#include "Emu/system_config.h" #include "Emu/system_config.h"
#include "Emu/Cell/timers.hpp"
#include "Input/pad_thread.h" #include "Input/pad_thread.h"
#include "Input/product_info.h" #include "Input/product_info.h"
@ -494,6 +495,12 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr<Pad> pad)
pad->m_analog_limiter_button_index = static_cast<s32>(pad->m_buttons.size()) - 1; pad->m_analog_limiter_button_index = static_cast<s32>(pad->m_buttons.size()) - 1;
} }
if (b_has_orientation)
{
pad->m_buttons.emplace_back(special_button_offset, mapping[button::orientation_reset_button], special_button_value::orientation_reset);
pad->m_orientation_reset_button_index = static_cast<s32>(pad->m_buttons.size()) - 1;
}
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::up], CELL_PAD_CTRL_UP); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::up], CELL_PAD_CTRL_UP);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::down], CELL_PAD_CTRL_DOWN); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::down], CELL_PAD_CTRL_DOWN);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::left], CELL_PAD_CTRL_LEFT); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::left], CELL_PAD_CTRL_LEFT);
@ -600,6 +607,11 @@ std::array<std::set<u32>, PadHandlerBase::button::button_count> PadHandlerBase::
mapping[button::analog_limiter_button] = FindKeyCodes<u32, u32>(button_list, cfg->analog_limiter_button); mapping[button::analog_limiter_button] = FindKeyCodes<u32, u32>(button_list, cfg->analog_limiter_button);
} }
if (b_has_orientation)
{
mapping[button::orientation_reset_button] = FindKeyCodes<u32, u32>(button_list, cfg->orientation_reset_button);
}
return mapping; return mapping;
} }
@ -739,6 +751,8 @@ void PadHandlerBase::process()
if (!device || !pad) if (!device || !pad)
continue; continue;
pad->move_data.orientation_enabled = b_has_orientation && device->config && device->config->orientation_enabled.get();
const connection status = update_connection(device); const connection status = update_connection(device);
switch (status) switch (status)
@ -754,6 +768,11 @@ void PadHandlerBase::process()
last_connection_status[i] = true; last_connection_status[i] = true;
connected_devices++; connected_devices++;
if (b_has_orientation)
{
device->reset_orientation();
}
} }
if (status == connection::no_data) if (status == connection::no_data)
@ -790,6 +809,11 @@ void PadHandlerBase::process()
last_connection_status[i] = false; last_connection_status[i] = false;
connected_devices--; connected_devices--;
if (b_has_orientation)
{
device->reset_orientation();
}
} }
continue; continue;
} }
@ -797,6 +821,142 @@ void PadHandlerBase::process()
get_mapping(m_bindings[i]); get_mapping(m_bindings[i]);
get_extended_info(m_bindings[i]); get_extended_info(m_bindings[i]);
get_orientation(m_bindings[i]);
apply_pad_data(m_bindings[i]); apply_pad_data(m_bindings[i]);
} }
} }
void PadHandlerBase::set_raw_orientation(ps_move_data& move_data, f32 accel_x, f32 accel_y, f32 accel_z, f32 gyro_x, f32 gyro_y, f32 gyro_z)
{
if (!move_data.orientation_enabled)
{
move_data.reset_sensors();
return;
}
// This function expects DS3 sensor accel values in linear velocity (m/s²) and gyro values in angular velocity (degree/s)
// The default position is flat on the ground, pointing forward.
// The accelerometers constantly measure G forces.
// The gyros measure changes in orientation and will reset when the device isn't moved anymore.
move_data.accelerometer_x = -accel_x; // move_data: Increases if the device is rolled to the left
move_data.accelerometer_y = accel_z; // move_data: Increases if the device is pitched upwards
move_data.accelerometer_z = accel_y; // move_data: Increases if the device is moved upwards
move_data.gyro_x = degree_to_rad(-gyro_x); // move_data: Increases if the device is pitched upwards
move_data.gyro_y = degree_to_rad(gyro_z); // move_data: Increases if the device is rolled to the right
move_data.gyro_z = degree_to_rad(-gyro_y); // move_data: Increases if the device is yawed to the left
}
void PadHandlerBase::set_raw_orientation(Pad& pad)
{
if (!pad.move_data.orientation_enabled)
{
pad.move_data.reset_sensors();
return;
}
// acceleration (linear velocity in m/s²)
const f32 accel_x = (pad.m_sensors[0].m_value - 512) / static_cast<f32>(MOTION_ONE_G);
const f32 accel_y = (pad.m_sensors[1].m_value - 512) / static_cast<f32>(MOTION_ONE_G);
const f32 accel_z = (pad.m_sensors[2].m_value - 512) / static_cast<f32>(MOTION_ONE_G);
// gyro (angular velocity in degree/s)
constexpr f32 gyro_x = 0.0f;
const f32 gyro_y = (pad.m_sensors[3].m_value - 512) / (123.f / 90.f);
constexpr f32 gyro_z = 0.0f;
set_raw_orientation(pad.move_data, accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z);
}
void PadHandlerBase::get_orientation(const pad_ensemble& binding) const
{
if (!b_has_orientation) return;
const auto& pad = binding.pad;
const auto& device = binding.device;
if (!pad || !device) return;
if (pad->move_data.calibration_requested)
{
device->reset_orientation();
pad->move_data.quaternion = ps_move_data::default_quaternion;
pad->move_data.calibration_succeeded = true;
return;
}
if (!pad->move_data.orientation_enabled || pad->get_orientation_reset_button_active())
{
// This can be called extensively in quick succession, so let's just reset the pointer instead of creating a new object.
device->ahrs.reset();
pad->move_data.quaternion = ps_move_data::default_quaternion;
return;
}
device->update_orientation(pad->move_data);
}
void PadDevice::reset_orientation()
{
// Initialize Fusion
ahrs = std::make_shared<FusionAhrs>();
FusionAhrsInitialise(ahrs.get());
ahrs->settings.convention = FusionConvention::FusionConventionEnu;
ahrs->settings.gain = 0.0f; // If gain is set, the algorithm tries to adjust the orientation over time.
FusionAhrsSetSettings(ahrs.get(), &ahrs->settings);
FusionAhrsReset(ahrs.get());
}
void PadDevice::update_orientation(ps_move_data& move_data)
{
if (!ahrs)
{
reset_orientation();
}
// Get elapsed time since last update
const u64 now_us = get_system_time();
const float elapsed_sec = (last_ahrs_update_time_us == 0) ? 0.0f : ((now_us - last_ahrs_update_time_us) / 1'000'000.0f);
last_ahrs_update_time_us = now_us;
// The ps move handler's axis may differ from the Fusion axis, so we have to map them correctly.
// Don't ask how the axis work. It's basically been trial and error.
ensure(ahrs->settings.convention == FusionConvention::FusionConventionEnu); // East-North-Up
const FusionVector accelerometer{
.axis {
.x = -move_data.accelerometer_x,
.y = +move_data.accelerometer_y,
.z = +move_data.accelerometer_z
}
};
const FusionVector gyroscope{
.axis {
.x = +PadHandlerBase::rad_to_degree(move_data.gyro_x),
.y = +PadHandlerBase::rad_to_degree(move_data.gyro_z),
.z = -PadHandlerBase::rad_to_degree(move_data.gyro_y)
}
};
FusionVector magnetometer {};
if (move_data.magnetometer_enabled)
{
magnetometer = FusionVector{
.axis {
.x = move_data.magnetometer_x,
.y = move_data.magnetometer_y,
.z = move_data.magnetometer_z
}
};
}
// Update Fusion
FusionAhrsUpdate(ahrs.get(), gyroscope, accelerometer, magnetometer, elapsed_sec);
// Get quaternion
const FusionQuaternion quaternion = FusionAhrsGetQuaternion(ahrs.get());
move_data.quaternion[0] = quaternion.array[1];
move_data.quaternion[1] = quaternion.array[2];
move_data.quaternion[2] = quaternion.array[3];
move_data.quaternion[3] = quaternion.array[0];
}

View file

@ -5,6 +5,15 @@
#include "pad_config_types.h" #include "pad_config_types.h"
#include "util/types.hpp" #include "util/types.hpp"
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
#include "3rdparty/fusion/fusion/Fusion/Fusion.h"
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
#include <cmath> #include <cmath>
#include <functional> #include <functional>
#include <string> #include <string>
@ -38,6 +47,12 @@ public:
}; };
color color_override{}; color color_override{};
bool color_override_active{}; bool color_override_active{};
std::shared_ptr<FusionAhrs> ahrs; // Used to calculate quaternions from sensor data
u64 last_ahrs_update_time_us = 0; // Last ahrs update
void update_orientation(ps_move_data& move_data);
void reset_orientation();
}; };
struct pad_ensemble struct pad_ensemble
@ -125,6 +140,7 @@ protected:
pressure_intensity_button, pressure_intensity_button,
analog_limiter_button, analog_limiter_button,
orientation_reset_button,
button_count button_count
}; };
@ -153,6 +169,7 @@ protected:
bool b_has_config = false; bool b_has_config = false;
bool b_has_pressure_intensity_button = true; bool b_has_pressure_intensity_button = true;
bool b_has_analog_limiter_button = true; bool b_has_analog_limiter_button = true;
bool b_has_orientation = false;
std::array<cfg_pad, MAX_GAMEPADS> m_pad_configs; std::array<cfg_pad, MAX_GAMEPADS> m_pad_configs;
std::vector<pad_ensemble> m_bindings; std::vector<pad_ensemble> m_bindings;
@ -301,6 +318,7 @@ public:
bool has_battery_led() const { return b_has_battery_led; } bool has_battery_led() const { return b_has_battery_led; }
bool has_pressure_intensity_button() const { return b_has_pressure_intensity_button; } bool has_pressure_intensity_button() const { return b_has_pressure_intensity_button; }
bool has_analog_limiter_button() const { return b_has_analog_limiter_button; } bool has_analog_limiter_button() const { return b_has_analog_limiter_button; }
bool has_orientation() const { return b_has_orientation; }
u16 NormalizeStickInput(u16 raw_value, s32 threshold, s32 multiplier, bool ignore_threshold = false) const; u16 NormalizeStickInput(u16 raw_value, s32 threshold, s32 multiplier, bool ignore_threshold = false) const;
void convert_stick_values(u16& x_out, u16& y_out, s32 x_in, s32 y_in, u32 deadzone, u32 anti_deadzone, u32 padsquircling) const; void convert_stick_values(u16& x_out, u16& y_out, s32 x_in, s32 y_in, u32 deadzone, u32 anti_deadzone, u32 padsquircling) const;
@ -323,6 +341,18 @@ public:
virtual void get_motion_sensors(const std::string& pad_id, const motion_callback& callback, const motion_fail_callback& fail_callback, motion_preview_values preview_values, const std::array<AnalogSensor, 4>& sensors); virtual void get_motion_sensors(const std::string& pad_id, const motion_callback& callback, const motion_fail_callback& fail_callback, motion_preview_values preview_values, const std::array<AnalogSensor, 4>& sensors);
virtual std::unordered_map<u32, std::string> get_motion_axis_list() const { return {}; } virtual std::unordered_map<u32, std::string> get_motion_axis_list() const { return {}; }
static constexpr f32 PI = 3.14159265f;
static f32 degree_to_rad(f32 degree)
{
return degree * PI / 180.0f;
}
static f32 rad_to_degree(f32 radians)
{
return radians * 180.0f / PI;
};
private: private:
virtual std::shared_ptr<PadDevice> get_device(const std::string& /*device*/) { return nullptr; } virtual std::shared_ptr<PadDevice> get_device(const std::string& /*device*/) { return nullptr; }
virtual bool get_is_left_trigger(const std::shared_ptr<PadDevice>& /*device*/, u64 /*keyCode*/) { return false; } virtual bool get_is_left_trigger(const std::shared_ptr<PadDevice>& /*device*/, u64 /*keyCode*/) { return false; }
@ -336,10 +366,15 @@ private:
virtual std::unordered_map<u64, u16> get_button_values(const std::shared_ptr<PadDevice>& /*device*/) { return {}; } virtual std::unordered_map<u64, u16> get_button_values(const std::shared_ptr<PadDevice>& /*device*/) { return {}; }
virtual pad_preview_values get_preview_values(const std::unordered_map<u64, u16>& /*data*/) { return {}; } virtual pad_preview_values get_preview_values(const std::unordered_map<u64, u16>& /*data*/) { return {}; }
void get_orientation(const pad_ensemble& binding) const;
protected: protected:
virtual std::array<std::set<u32>, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg); virtual std::array<std::set<u32>, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg);
virtual void get_mapping(const pad_ensemble& binding); virtual void get_mapping(const pad_ensemble& binding);
void TranslateButtonPress(const std::shared_ptr<PadDevice>& device, u64 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false); void TranslateButtonPress(const std::shared_ptr<PadDevice>& device, u64 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false);
void init_configs(); void init_configs();
cfg_pad* get_config(const std::string& pad_id); cfg_pad* get_config(const std::string& pad_id);
static void set_raw_orientation(ps_move_data& move_data, f32 accel_x, f32 accel_y, f32 accel_z, f32 gyro_x, f32 gyro_y, f32 gyro_z);
static void set_raw_orientation(Pad& pad);
}; };

View file

@ -280,7 +280,7 @@ void usb_device_topshotelite::interrupt_transfer(u32 buf_size, u8* buf, u32 /*en
} }
bool up = false, right = false, down = false, left = false; bool up = false, right = false, down = false, left = false;
const auto input_callback = [&ts, &up, &down, &left, &right](topshotelite_btn btn, u16 value, bool pressed) const auto input_callback = [&ts, &up, &down, &left, &right](topshotelite_btn btn, pad_button /*pad_button*/, u16 value, bool pressed, bool& /*abort*/)
{ {
if (!pressed) if (!pressed)
return; return;
@ -315,7 +315,7 @@ void usb_device_topshotelite::interrupt_transfer(u32 buf_size, u8* buf, u32 /*en
{ {
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto gamepad_handler = pad::get_current_handler(); const auto gamepad_handler = pad::get_pad_thread();
const auto& pads = gamepad_handler->GetPads(); const auto& pads = gamepad_handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)

View file

@ -308,7 +308,7 @@ void usb_device_topshotfearmaster::interrupt_transfer(u32 buf_size, u8* buf, u32
} }
bool up = false, right = false, down = false, left = false; bool up = false, right = false, down = false, left = false;
const auto input_callback = [&ts, &up, &down, &left, &right](topshotfearmaster_btn btn, u16 value, bool pressed) const auto input_callback = [&ts, &up, &down, &left, &right](topshotfearmaster_btn btn, pad_button /*pad_button*/, u16 value, bool pressed, bool& /*abort*/)
{ {
if (!pressed) if (!pressed)
return; return;
@ -339,7 +339,7 @@ void usb_device_topshotfearmaster::interrupt_transfer(u32 buf_size, u8* buf, u32
{ {
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto gamepad_handler = pad::get_current_handler(); const auto gamepad_handler = pad::get_pad_thread();
const auto& pads = gamepad_handler->GetPads(); const auto& pads = gamepad_handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)

View file

@ -151,7 +151,7 @@ void usb_device_turntable::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpo
// All other bufs are always 0x00 // All other bufs are always 0x00
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
@ -159,7 +159,7 @@ void usb_device_turntable::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpo
return; return;
const auto& cfg = ::at32(g_cfg_turntable.players, m_controller_index); const auto& cfg = ::at32(g_cfg_turntable.players, m_controller_index);
cfg->handle_input(pad, true, [&buf](turntable_btn btn, u16 value, bool pressed) cfg->handle_input(pad, true, [&buf](turntable_btn btn, pad_button /*pad_btn*/, u16 value, bool pressed, bool& /*abort*/)
{ {
if (!pressed) if (!pressed)
return; return;

View file

@ -8,11 +8,7 @@ cfg_camera g_cfg_camera;
cfg_camera::cfg_camera() cfg_camera::cfg_camera()
: cfg::node() : cfg::node()
#ifdef _WIN32 , path(fs::get_config_dir(true) + "camera.yml")
, path(fs::get_config_dir() + "config/camera.yml")
#else
, path(fs::get_config_dir() + "camera.yml")
#endif
{ {
} }

View file

@ -88,7 +88,7 @@ public:
button_map.clear(); button_map.clear();
} }
void handle_input(std::shared_ptr<Pad> pad, bool press_only, const std::function<void(T, u16, bool)>& func) const void handle_input(std::shared_ptr<Pad> pad, bool press_only, const std::function<void(T, pad_button, u16, bool, bool&)>& func) const
{ {
if (!pad) if (!pad)
return; return;
@ -97,19 +97,25 @@ public:
{ {
if (button.m_pressed || !press_only) if (button.m_pressed || !press_only)
{ {
handle_input(func, button.m_offset, button.m_outKeyCode, button.m_value, button.m_pressed, true); if (handle_input(func, button.m_offset, button.m_outKeyCode, button.m_value, button.m_pressed, true))
{
return;
}
} }
} }
for (const AnalogStick& stick : pad->m_sticks) for (const AnalogStick& stick : pad->m_sticks)
{ {
handle_input(func, stick.m_offset, get_axis_keycode(stick.m_offset, stick.m_value), stick.m_value, true, true); if (handle_input(func, stick.m_offset, get_axis_keycode(stick.m_offset, stick.m_value), stick.m_value, true, true))
{
return;
}
} }
} }
void handle_input(const Mouse& mouse, const std::function<void(T, u16, bool)>& func) const void handle_input(const Mouse& mouse, const std::function<void(T, pad_button, u16, bool, bool&)>& func) const
{ {
for (int i = 0; i < 7; i++) for (int i = 0; i < 8; i++)
{ {
const MouseButtonCodes cell_code = get_mouse_button_code(i); const MouseButtonCodes cell_code = get_mouse_button_code(i);
if ((mouse.buttons & cell_code)) if ((mouse.buttons & cell_code))
@ -117,7 +123,11 @@ public:
const pad_button button = static_cast<pad_button>(static_cast<int>(pad_button::mouse_button_1) + i); const pad_button button = static_cast<pad_button>(static_cast<int>(pad_button::mouse_button_1) + i);
const u32 offset = pad_button_offset(button); const u32 offset = pad_button_offset(button);
const u32 keycode = pad_button_keycode(button); const u32 keycode = pad_button_keycode(button);
handle_input(func, offset, keycode, 255, true, true);
if (handle_input(func, offset, keycode, 255, true, true))
{
return;
}
} }
} }
} }
@ -163,10 +173,12 @@ protected:
return empty_set; return empty_set;
} }
void handle_input(const std::function<void(T, u16, bool)>& func, u32 offset, u32 keycode, u16 value, bool pressed, bool check_axis) const bool handle_input(const std::function<void(T, pad_button, u16, bool, bool&)>& func, u32 offset, u32 keycode, u16 value, bool pressed, bool check_axis) const
{ {
m_mutex.lock(); m_mutex.lock();
bool abort = false;
const auto& btns = find_button(offset, keycode); const auto& btns = find_button(offset, keycode);
if (btns.empty()) if (btns.empty())
{ {
@ -180,24 +192,26 @@ protected:
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y:
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X:
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y:
handle_input(func, offset, static_cast<u32>(axis_direction::both), value, pressed, false); abort = handle_input(func, offset, static_cast<u32>(axis_direction::both), value, pressed, false);
break; break;
default: default:
break; break;
} }
} }
return; return abort;
} }
for (const auto& btn : btns) for (const auto& btn : btns)
{ {
if (btn && func) if (btn && func)
{ {
func(btn->btn_id(), value, pressed); func(btn->btn_id(), btn->get(), value, pressed, abort);
if (abort) break;
} }
} }
m_mutex.unlock(); m_mutex.unlock();
return abort;
} }
}; };
@ -221,7 +235,7 @@ struct emulated_pads_config : cfg::node
m_mutex.lock(); m_mutex.lock();
bool result = false; bool result = false;
const std::string cfg_name = fmt::format("%sconfig/%s.yml", fs::get_config_dir(), cfg_id); const std::string cfg_name = fmt::format("%s%s.yml", fs::get_config_dir(true), cfg_id);
cfg_log.notice("Loading %s config: %s", cfg_id, cfg_name); cfg_log.notice("Loading %s config: %s", cfg_id, cfg_name);
from_default(); from_default();
@ -258,7 +272,7 @@ struct emulated_pads_config : cfg::node
{ {
std::lock_guard lock(m_mutex); std::lock_guard lock(m_mutex);
const std::string cfg_name = fmt::format("%sconfig/%s.yml", fs::get_config_dir(), cfg_id); const std::string cfg_name = fmt::format("%s%s.yml", fs::get_config_dir(true), cfg_id);
cfg_log.notice("Saving %s config to '%s'", cfg_id, cfg_name); cfg_log.notice("Saving %s config to '%s'", cfg_id, cfg_name);
if (!fs::create_path(fs::get_parent_dir(cfg_name))) if (!fs::create_path(fs::get_parent_dir(cfg_name)))

View file

@ -4,7 +4,7 @@
#include <array> #include <array>
enum class gem_btn enum class gem_btn : u32
{ {
start, start,
select, select,
@ -17,6 +17,18 @@ enum class gem_btn
x_axis, x_axis,
y_axis, y_axis,
combo_begin,
combo = combo_begin,
combo_start,
combo_select,
combo_triangle,
combo_circle,
combo_cross,
combo_square,
combo_move,
combo_t,
combo_end = combo_t,
count count
}; };
@ -41,6 +53,34 @@ struct cfg_fake_gems final : public emulated_pads_config<cfg_fake_gem, 4>
cfg_fake_gems() : emulated_pads_config<cfg_fake_gem, 4>("gem") {}; cfg_fake_gems() : emulated_pads_config<cfg_fake_gem, 4>("gem") {};
}; };
struct cfg_mouse_gem final : public emulated_pad_config<gem_btn>
{
cfg_mouse_gem(node* owner, const std::string& name) : emulated_pad_config(owner, name) {}
cfg_pad_btn<gem_btn> start{ this, "Start", gem_btn::start, pad_button::mouse_button_6 };
cfg_pad_btn<gem_btn> select{ this, "Select", gem_btn::select, pad_button::mouse_button_7 };
cfg_pad_btn<gem_btn> triangle{ this, "Triangle", gem_btn::triangle, pad_button::mouse_button_8 };
cfg_pad_btn<gem_btn> circle{ this, "Circle", gem_btn::circle, pad_button::mouse_button_4 };
cfg_pad_btn<gem_btn> cross{ this, "Cross", gem_btn::cross, pad_button::mouse_button_5 };
cfg_pad_btn<gem_btn> square{ this, "Square", gem_btn::square, pad_button::mouse_button_3 };
cfg_pad_btn<gem_btn> move{ this, "Move", gem_btn::move, pad_button::mouse_button_2 };
cfg_pad_btn<gem_btn> t{ this, "T", gem_btn::t, pad_button::mouse_button_1 };
cfg_pad_btn<gem_btn> combo{ this, "Combo", gem_btn::combo, pad_button::pad_button_max_enum };
cfg_pad_btn<gem_btn> combo_start{ this, "Combo Start", gem_btn::combo_start, pad_button::pad_button_max_enum };
cfg_pad_btn<gem_btn> combo_select{ this, "Combo Select", gem_btn::combo_select, pad_button::pad_button_max_enum };
cfg_pad_btn<gem_btn> combo_triangle{ this, "Combo Triangle", gem_btn::combo_triangle, pad_button::pad_button_max_enum };
cfg_pad_btn<gem_btn> combo_circle{ this, "Combo Circle", gem_btn::combo_circle, pad_button::pad_button_max_enum };
cfg_pad_btn<gem_btn> combo_cross{ this, "Combo Cross", gem_btn::combo_cross, pad_button::pad_button_max_enum };
cfg_pad_btn<gem_btn> combo_square{ this, "Combo Square", gem_btn::combo_square, pad_button::pad_button_max_enum };
cfg_pad_btn<gem_btn> combo_move{ this, "Combo Move", gem_btn::combo_move, pad_button::pad_button_max_enum };
cfg_pad_btn<gem_btn> combo_t{ this, "Combo T", gem_btn::combo_t, pad_button::pad_button_max_enum };
};
struct cfg_mouse_gems final : public emulated_pads_config<cfg_mouse_gem, 4>
{
cfg_mouse_gems() : emulated_pads_config<cfg_mouse_gem, 4>("gem_mouse") {};
};
struct cfg_gem final : public emulated_pad_config<gem_btn> struct cfg_gem final : public emulated_pad_config<gem_btn>
{ {
cfg_gem(node* owner, const std::string& name) : emulated_pad_config(owner, name) {} cfg_gem(node* owner, const std::string& name) : emulated_pad_config(owner, name) {}
@ -62,3 +102,4 @@ struct cfg_gems final : public emulated_pads_config<cfg_gem, 4>
extern cfg_gems g_cfg_gem_real; extern cfg_gems g_cfg_gem_real;
extern cfg_fake_gems g_cfg_gem_fake; extern cfg_fake_gems g_cfg_gem_fake;
extern cfg_mouse_gems g_cfg_gem_mouse;

View file

@ -4,11 +4,7 @@
#include "Utilities/File.h" #include "Utilities/File.h"
mouse_config::mouse_config() mouse_config::mouse_config()
#ifdef _WIN32 : cfg_name(fs::get_config_dir(true) + "config_mouse.yml")
: cfg_name(fs::get_config_dir() + "config/config_mouse.yml")
#else
: cfg_name(fs::get_config_dir() + "config_mouse.yml")
#endif
{ {
} }

View file

@ -32,6 +32,20 @@ std::string cfg_pad::get_buttons(std::vector<std::string> vec)
return fmt::merge(vec, ","); return fmt::merge(vec, ",");
} }
u8 cfg_pad::get_large_motor_speed(const std::array<VibrateMotor, 2>& motor_speed) const
{
const u8 idx = switch_vibration_motors ? 1 : 0;
const f32 multiplier = multiplier_vibration_motor_large / 100.0f;
return static_cast<u8>(std::clamp(motor_speed[idx].m_value * multiplier, 0.0f, 255.0f));
}
u8 cfg_pad::get_small_motor_speed(const std::array<VibrateMotor, 2>& motor_speed) const
{
const u8 idx = switch_vibration_motors ? 0 : 1;
const f32 multiplier = multiplier_vibration_motor_small / 100.0f;
return static_cast<u8>(std::clamp(motor_speed[idx].m_value * multiplier, 0.0f, 255.0f));
}
bool cfg_input::load(const std::string& title_id, const std::string& config_file, bool strict) bool cfg_input::load(const std::string& title_id, const std::string& config_file, bool strict)
{ {
input_log.notice("Loading pad config (title_id='%s', config_file='%s', strict=%d)", title_id, config_file, strict); input_log.notice("Loading pad config (title_id='%s', config_file='%s', strict=%d)", title_id, config_file, strict);

View file

@ -28,6 +28,9 @@ struct cfg_pad final : cfg::node
static std::vector<std::string> get_buttons(const std::string& str); static std::vector<std::string> get_buttons(const std::string& str);
static std::string get_buttons(std::vector<std::string> vec); static std::string get_buttons(std::vector<std::string> vec);
u8 get_large_motor_speed(const std::array<VibrateMotor, 2>& motor_speed) const;
u8 get_small_motor_speed(const std::array<VibrateMotor, 2>& motor_speed) const;
cfg::string ls_left{ this, "Left Stick Left", "" }; cfg::string ls_left{ this, "Left Stick Left", "" };
cfg::string ls_down{ this, "Left Stick Down", "" }; cfg::string ls_down{ this, "Left Stick Down", "" };
cfg::string ls_right{ this, "Left Stick Right", "" }; cfg::string ls_right{ this, "Left Stick Right", "" };
@ -66,6 +69,9 @@ struct cfg_pad final : cfg::node
cfg_sensor motion_sensor_z{ this, "Motion Sensor Z" }; cfg_sensor motion_sensor_z{ this, "Motion Sensor Z" };
cfg_sensor motion_sensor_g{ this, "Motion Sensor G" }; cfg_sensor motion_sensor_g{ this, "Motion Sensor G" };
cfg::string orientation_reset_button{ this, "Orientation Reset Button", "" };
cfg::_bool orientation_enabled{ this, "Orientation Enabled", false };
cfg::string pressure_intensity_button{ this, "Pressure Intensity Button", "" }; cfg::string pressure_intensity_button{ this, "Pressure Intensity Button", "" };
cfg::uint<0, 100> pressure_intensity{ this, "Pressure Intensity Percent", 50 }; cfg::uint<0, 100> pressure_intensity{ this, "Pressure Intensity Percent", 50 };
cfg::_bool pressure_intensity_toggle_mode{ this, "Pressure Intensity Toggle Mode", false }; cfg::_bool pressure_intensity_toggle_mode{ this, "Pressure Intensity Toggle Mode", false };
@ -93,8 +99,8 @@ struct cfg_pad final : cfg::node
cfg::uint<0, 100> led_battery_indicator_brightness{ this, "LED battery indicator brightness", 50 }; cfg::uint<0, 100> led_battery_indicator_brightness{ this, "LED battery indicator brightness", 50 };
cfg::_bool player_led_enabled{ this, "Player LED enabled", true }; cfg::_bool player_led_enabled{ this, "Player LED enabled", true };
cfg::_bool enable_vibration_motor_large{ this, "Enable Large Vibration Motor", true }; cfg::uint<0, 200> multiplier_vibration_motor_large{ this, "Large Vibration Motor Multiplier", 100 };
cfg::_bool enable_vibration_motor_small{ this, "Enable Small Vibration Motor", true }; cfg::uint<0, 200> multiplier_vibration_motor_small{ this, "Small Vibration Motor Multiplier", 100 };
cfg::_bool switch_vibration_motors{ this, "Switch Vibration Motors", false }; cfg::_bool switch_vibration_motors{ this, "Switch Vibration Motors", false };
cfg::_enum<mouse_movement_mode> mouse_move_mode{ this, "Mouse Movement Mode", mouse_movement_mode::relative }; cfg::_enum<mouse_movement_mode> mouse_move_mode{ this, "Mouse Movement Mode", mouse_movement_mode::relative };

View file

@ -39,7 +39,7 @@ void fmt_class_string<pad_button>::format(std::string& out, u64 arg)
case pad_button::rs_right: return "Right Stick Right"; case pad_button::rs_right: return "Right Stick Right";
case pad_button::rs_x: return "Right Stick X-Axis"; case pad_button::rs_x: return "Right Stick X-Axis";
case pad_button::rs_y: return "Right Stick Y-Axis"; case pad_button::rs_y: return "Right Stick Y-Axis";
case pad_button::pad_button_max_enum: return "MAX_ENUM"; case pad_button::pad_button_max_enum: return "";
case pad_button::mouse_button_1: return "Mouse Button 1"; case pad_button::mouse_button_1: return "Mouse Button 1";
case pad_button::mouse_button_2: return "Mouse Button 2"; case pad_button::mouse_button_2: return "Mouse Button 2";
case pad_button::mouse_button_3: return "Mouse Button 3"; case pad_button::mouse_button_3: return "Mouse Button 3";
@ -159,6 +159,20 @@ u32 get_axis_keycode(u32 offset, u16 value)
} }
} }
void ps_move_data::reset_sensors()
{
quaternion = default_quaternion;
accelerometer_x = 0.0f;
accelerometer_y = 0.0f;
accelerometer_z = 0.0f;
gyro_x = 0.0f;
gyro_y = 0.0f;
gyro_z = 0.0f;
magnetometer_x = 0.0f;
magnetometer_y = 0.0f;
magnetometer_z = 0.0f;
}
bool Pad::get_pressure_intensity_button_active(bool is_toggle_mode, u32 player_id) bool Pad::get_pressure_intensity_button_active(bool is_toggle_mode, u32 player_id)
{ {
if (m_pressure_intensity_button_index < 0) if (m_pressure_intensity_button_index < 0)
@ -238,3 +252,13 @@ bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id)
return analog_limiter_button.m_pressed; return analog_limiter_button.m_pressed;
} }
bool Pad::get_orientation_reset_button_active()
{
if (m_orientation_reset_button_index < 0)
{
return false;
}
return m_buttons[m_orientation_reset_button_index].m_pressed;
}

View file

@ -365,7 +365,8 @@ constexpr u32 special_button_offset = 666; // Must not conflict with other CELL
enum special_button_value enum special_button_value
{ {
pressure_intensity, pressure_intensity,
analog_limiter analog_limiter,
orientation_reset
}; };
struct Button struct Button
@ -470,8 +471,10 @@ struct ps_move_data
bool calibration_succeeded = false; bool calibration_succeeded = false;
bool magnetometer_enabled = false; bool magnetometer_enabled = false;
bool orientation_enabled = false;
std::array<f32, 4> quaternion { 1.0f, 0.0f, 0.0f, 0.0f }; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up) static constexpr std::array<f32, 4> default_quaternion { 1.0f, 0.0f, 0.0f, 0.0f };
std::array<f32, 4> quaternion = default_quaternion; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up)
f32 accelerometer_x = 0.0f; // linear velocity in m/s² f32 accelerometer_x = 0.0f; // linear velocity in m/s²
f32 accelerometer_y = 0.0f; // linear velocity in m/s² f32 accelerometer_y = 0.0f; // linear velocity in m/s²
f32 accelerometer_z = 0.0f; // linear velocity in m/s² f32 accelerometer_z = 0.0f; // linear velocity in m/s²
@ -482,6 +485,8 @@ struct ps_move_data
f32 magnetometer_y = 0.0f; f32 magnetometer_y = 0.0f;
f32 magnetometer_z = 0.0f; f32 magnetometer_z = 0.0f;
s16 temperature = 0; s16 temperature = 0;
void reset_sensors();
}; };
struct Pad struct Pad
@ -512,6 +517,9 @@ struct Pad
bool m_analog_limiter_enabled_last{}; // only used in keyboard_pad_handler bool m_analog_limiter_enabled_last{}; // only used in keyboard_pad_handler
bool get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id); bool get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id);
s32 m_orientation_reset_button_index{-1}; // Special button index. -1 if not set.
bool get_orientation_reset_button_active();
// Cable State: 0 - 1 plugged in ? // Cable State: 0 - 1 plugged in ?
u8 m_cable_state{0}; u8 m_cable_state{0};

View file

@ -8,13 +8,7 @@ cfg_rb3drums g_cfg_rb3drums;
cfg_rb3drums::cfg_rb3drums() cfg_rb3drums::cfg_rb3drums()
: cfg::node() : cfg::node()
#ifdef _WIN32 , path(fs::get_config_dir(true) + "rb3drums.yml")
,
path(fs::get_config_dir() + "config/rb3drums.yml")
#else
,
path(fs::get_config_dir() + "rb3drums.yml")
#endif
{ {
} }

View file

@ -8,11 +8,7 @@ cfg_recording g_cfg_recording;
cfg_recording::cfg_recording() cfg_recording::cfg_recording()
: cfg::node() : cfg::node()
#ifdef _WIN32 , path(fs::get_config_dir(true) + "recording.yml")
, path(fs::get_config_dir() + "config/recording.yml")
#else
, path(fs::get_config_dir() + "recording.yml")
#endif
{ {
} }

View file

@ -189,7 +189,7 @@ void usb_device_usio::save_backup()
void usb_device_usio::translate_input_taiko() void usb_device_usio::translate_input_taiko()
{ {
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
std::vector<u8> input_buf(0x60); std::vector<u8> input_buf(0x60);
constexpr le_t<u16> c_hit = 0x1800; constexpr le_t<u16> c_hit = 0x1800;
@ -203,7 +203,7 @@ void usb_device_usio::translate_input_taiko()
if (const auto& pad = ::at32(handler->GetPads(), pad_number); (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && is_input_allowed()) if (const auto& pad = ::at32(handler->GetPads(), pad_number); (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && is_input_allowed())
{ {
const auto& cfg = ::at32(g_cfg_usio.players, pad_number); const auto& cfg = ::at32(g_cfg_usio.players, pad_number);
cfg->handle_input(pad, false, [&](usio_btn btn, u16 /*value*/, bool pressed) cfg->handle_input(pad, false, [&](usio_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/)
{ {
switch (btn) switch (btn)
{ {
@ -273,7 +273,7 @@ void usb_device_usio::translate_input_taiko()
void usb_device_usio::translate_input_tekken() void usb_device_usio::translate_input_tekken()
{ {
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
std::vector<u8> input_buf(0x180); std::vector<u8> input_buf(0x180);
le_t<u64> digital_input[2]{}; le_t<u64> digital_input[2]{};
@ -288,7 +288,7 @@ void usb_device_usio::translate_input_tekken()
if (const auto& pad = ::at32(handler->GetPads(), pad_number); (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && is_input_allowed()) if (const auto& pad = ::at32(handler->GetPads(), pad_number); (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && is_input_allowed())
{ {
const auto& cfg = ::at32(g_cfg_usio.players, pad_number); const auto& cfg = ::at32(g_cfg_usio.players, pad_number);
cfg->handle_input(pad, false, [&](usio_btn btn, u16 /*value*/, bool pressed) cfg->handle_input(pad, false, [&](usio_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/)
{ {
switch (btn) switch (btn)
{ {

View file

@ -59,11 +59,7 @@ namespace np
{ {
std::string get_players_history_path() std::string get_players_history_path()
{ {
#ifdef _WIN32 return fs::get_config_dir(true) + "players_history.yml";
return fs::get_config_dir() + "config/players_history.yml";
#else
return fs::get_config_dir() + "players_history.yml";
#endif
} }
std::map<std::string, player_history> load_players_history() std::map<std::string, player_history> load_players_history()
@ -1440,7 +1436,7 @@ namespace np
void np_handler::save_players_history() void np_handler::save_players_history()
{ {
#ifdef _WIN32 #ifdef _WIN32
const std::string path_to_cfg = fs::get_config_dir() + "config/"; const std::string path_to_cfg = fs::get_config_dir(true);
if (!fs::create_path(path_to_cfg)) if (!fs::create_path(path_to_cfg))
{ {
nph_log.error("Could not create path: %s", path_to_cfg); nph_log.error("Could not create path: %s", path_to_cfg);

View file

@ -34,7 +34,7 @@ void cfg_rpcn::load()
void cfg_rpcn::save() const void cfg_rpcn::save() const
{ {
#ifdef _WIN32 #ifdef _WIN32
const std::string path_to_cfg = fs::get_config_dir() + "config/"; const std::string path_to_cfg = fs::get_config_dir(true);
if (!fs::create_path(path_to_cfg)) if (!fs::create_path(path_to_cfg))
{ {
rpcn_log.error("Could not create path: %s", path_to_cfg); rpcn_log.error("Could not create path: %s", path_to_cfg);
@ -51,11 +51,7 @@ void cfg_rpcn::save() const
std::string cfg_rpcn::get_path() std::string cfg_rpcn::get_path()
{ {
#ifdef _WIN32 return fs::get_config_dir(true) + "rpcn.yml";
return fs::get_config_dir() + "config/rpcn.yml";
#else
return fs::get_config_dir() + "rpcn.yml";
#endif
} }
std::string cfg_rpcn::generate_npid() std::string cfg_rpcn::generate_npid()

View file

@ -24,7 +24,7 @@ void cfg_upnp::load()
void cfg_upnp::save() const void cfg_upnp::save() const
{ {
#ifdef _WIN32 #ifdef _WIN32
const std::string path_to_cfg = fs::get_config_dir() + "config/"; const std::string path_to_cfg = fs::get_config_dir(true);
if (!fs::create_path(path_to_cfg)) if (!fs::create_path(path_to_cfg))
{ {
upnp_cfg_log.error("Could not create path: %s", path_to_cfg); upnp_cfg_log.error("Could not create path: %s", path_to_cfg);
@ -51,9 +51,5 @@ void cfg_upnp::set_device_url(std::string_view url)
std::string cfg_upnp::get_path() std::string cfg_upnp::get_path()
{ {
#ifdef _WIN32 return fs::get_config_dir(true) + "upnp.yml";
return fs::get_config_dir() + "config/upnp.yml";
#else
return fs::get_config_dir() + "upnp.yml";
#endif
} }

View file

@ -427,5 +427,17 @@ namespace rsx
} }
return result; return result;
} }
template <typename F, typename U>
requires std::is_invocable_r_v<U, F, const U&, const Ty&>
U reduce(U initial_value, F&& reducer) const
{
U accumulate = initial_value;
for (auto it = begin(); it != end(); ++it)
{
accumulate = reducer(accumulate, *it);
}
return accumulate;
}
}; };
} }

View file

@ -18,7 +18,21 @@ namespace rsx
case surface_target::surfaces_a_b_c: return{ 0, 1, 2 }; case surface_target::surfaces_a_b_c: return{ 0, 1, 2 };
case surface_target::surfaces_a_b_c_d: return{ 0, 1, 2, 3 }; case surface_target::surfaces_a_b_c_d: return{ 0, 1, 2, 3 };
} }
fmt::throw_exception("Wrong color_target"); fmt::throw_exception("Invalid color target %d", static_cast<int>(color_target));
}
u8 get_mrt_buffers_count(surface_target color_target)
{
switch (color_target)
{
case surface_target::none: return 0;
case surface_target::surface_a: return 1;
case surface_target::surface_b: return 1;
case surface_target::surfaces_a_b: return 2;
case surface_target::surfaces_a_b_c: return 3;
case surface_target::surfaces_a_b_c_d: return 4;
}
fmt::throw_exception("Invalid color target %d", static_cast<int>(color_target));
} }
usz get_aligned_pitch(surface_color_format format, u32 width) usz get_aligned_pitch(surface_color_format format, u32 width)

View file

@ -17,6 +17,7 @@ namespace rsx
namespace utility namespace utility
{ {
std::vector<u8> get_rtt_indexes(surface_target color_target); std::vector<u8> get_rtt_indexes(surface_target color_target);
u8 get_mrt_buffers_count(surface_target color_target);
usz get_aligned_pitch(surface_color_format format, u32 width); usz get_aligned_pitch(surface_color_format format, u32 width);
usz get_packed_pitch(surface_color_format format, u32 width); usz get_packed_pitch(surface_color_format format, u32 width);
} }

View file

@ -735,7 +735,7 @@ namespace rsx
utils::stream_vector(dst + 4, 0u, fog_mode, std::bit_cast<u32>(wpos_scale), std::bit_cast<u32>(wpos_bias)); utils::stream_vector(dst + 4, 0u, fog_mode, std::bit_cast<u32>(wpos_scale), std::bit_cast<u32>(wpos_bias));
} }
void draw_command_processor::fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase& prog) const void draw_command_processor::fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase* prog) const
{ {
auto& draw_call = REGS(m_ctx)->current_draw_clause; auto& draw_call = REGS(m_ctx)->current_draw_clause;
@ -745,8 +745,9 @@ namespace rsx
// Temp indirection table. Used to track "running" updates. // Temp indirection table. Used to track "running" updates.
rsx::simple_array<u32> instancing_indirection_table; rsx::simple_array<u32> instancing_indirection_table;
// indirection table size // indirection table size
const auto reloc_table = prog.has_indexed_constants ? decltype(prog.constant_ids){} : prog.constant_ids; const auto full_reupload = !prog || prog->has_indexed_constants;
const auto redirection_table_size = prog.has_indexed_constants ? 468u : ::size32(prog.constant_ids); const auto reloc_table = full_reupload ? decltype(prog->constant_ids){} : prog->constant_ids;
const auto redirection_table_size = full_reupload ? 468u : ::size32(prog->constant_ids);
instancing_indirection_table.resize(redirection_table_size); instancing_indirection_table.resize(redirection_table_size);
// Temp constants data // Temp constants data
@ -787,9 +788,9 @@ namespace rsx
continue; continue;
} }
const int translated_offset = prog.has_indexed_constants const int translated_offset = full_reupload
? instance_config.patch_load_offset ? instance_config.patch_load_offset
: prog.TranslateConstantsRange(instance_config.patch_load_offset, instance_config.patch_load_count); : prog->translate_constants_range(instance_config.patch_load_offset, instance_config.patch_load_count);
if (translated_offset >= 0) if (translated_offset >= 0)
{ {
@ -809,14 +810,14 @@ namespace rsx
continue; continue;
} }
ensure(!prog.has_indexed_constants); ensure(!full_reupload);
// Sparse update. Update records individually instead of bulk // Sparse update. Update records individually instead of bulk
// FIXME: Range batching optimization // FIXME: Range batching optimization
const auto load_end = instance_config.patch_load_offset + instance_config.patch_load_count; const auto load_end = instance_config.patch_load_offset + instance_config.patch_load_count;
for (u32 i = 0; i < redirection_table_size; ++i) for (u32 i = 0; i < redirection_table_size; ++i)
{ {
const auto read_index = prog.constant_ids[i]; const auto read_index = prog->constant_ids[i];
if (read_index < instance_config.patch_load_offset || read_index >= load_end) if (read_index < instance_config.patch_load_offset || read_index >= load_end)
{ {
// Reading outside "hot" range. // Reading outside "hot" range.

View file

@ -105,6 +105,6 @@ namespace rsx
// Fill instancing buffers. A single iobuf is used for both. 256byte alignment enforced to allow global bind // Fill instancing buffers. A single iobuf is used for both. 256byte alignment enforced to allow global bind
// Returns offsets to the index redirection lookup table and constants field array // Returns offsets to the index redirection lookup table and constants field array
void fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase& prog) const; void fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase* prog) const;
}; };
} }

View file

@ -39,7 +39,7 @@ u64 GLGSRender::get_cycles()
GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar) GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar)
{ {
m_shaders_cache = std::make_unique<gl::shader_cache>(m_prog_buffer, "opengl", "v1.94"); m_shaders_cache = std::make_unique<gl::shader_cache>(m_prog_buffer, "opengl", "v1.95");
if (g_cfg.video.disable_vertex_cache) if (g_cfg.video.disable_vertex_cache)
m_vertex_cache = std::make_unique<gl::null_vertex_cache>(); m_vertex_cache = std::make_unique<gl::null_vertex_cache>();
@ -52,6 +52,14 @@ GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar)
backend_config.supports_normalized_barycentrics = true; backend_config.supports_normalized_barycentrics = true;
} }
GLGSRender::~GLGSRender()
{
if (m_frame)
{
m_frame->reset();
}
}
extern CellGcmContextData current_context; extern CellGcmContextData current_context;
void GLGSRender::set_viewport() void GLGSRender::set_viewport()
@ -870,7 +878,7 @@ void GLGSRender::load_program_env()
} }
} }
if (update_fragment_constants && !update_instruction_buffers) if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program))
{ {
// Fragment constants // Fragment constants
auto mapping = m_fragment_constants_buffer->alloc_from_heap(fragment_constants_size, m_uniform_buffer_offset_align); auto mapping = m_fragment_constants_buffer->alloc_from_heap(fragment_constants_size, m_uniform_buffer_offset_align);
@ -970,12 +978,23 @@ void GLGSRender::load_program_env()
} }
} }
m_graphics_state.clear( rsx::flags32_t handled_flags =
rsx::pipeline_state::fragment_state_dirty | rsx::pipeline_state::fragment_state_dirty |
rsx::pipeline_state::vertex_state_dirty | rsx::pipeline_state::vertex_state_dirty |
rsx::pipeline_state::transform_constants_dirty | rsx::pipeline_state::transform_constants_dirty |
rsx::pipeline_state::fragment_constants_dirty | rsx::pipeline_state::fragment_texture_state_dirty;
rsx::pipeline_state::fragment_texture_state_dirty);
if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program))
{
handled_flags |= rsx::pipeline_state::fragment_constants_dirty;
}
m_graphics_state.clear(handled_flags);
}
bool GLGSRender::is_current_program_interpreted() const
{
return m_program && m_shader_interpreter.is_interpreter(m_program);
} }
void GLGSRender::upload_transform_constants(const rsx::io_buffer& buffer) void GLGSRender::upload_transform_constants(const rsx::io_buffer& buffer)
@ -1026,13 +1045,19 @@ void GLGSRender::update_vertex_env(const gl::vertex_upload_info& upload_info)
void GLGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 count) void GLGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 count)
{ {
if (!m_vertex_prog) if (!m_program || !m_vertex_prog)
{ {
// Shouldn't be reachable, but handle it correctly anyway // Shouldn't be reachable, but handle it correctly anyway
m_graphics_state |= rsx::pipeline_state::transform_constants_dirty; m_graphics_state |= rsx::pipeline_state::transform_constants_dirty;
return; return;
} }
if (!m_vertex_prog->overlaps_constants_range(index, count))
{
// Nothing meaningful to us
return;
}
std::pair<u32, u32> data_range {}; std::pair<u32, u32> data_range {};
void* data_source = nullptr; void* data_source = nullptr;
const auto bound_range = m_transform_constants_buffer->bound_range(); const auto bound_range = m_transform_constants_buffer->bound_range();
@ -1046,7 +1071,7 @@ void GLGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 cou
data_range = { bound_range.first + byte_offset, byte_count}; data_range = { bound_range.first + byte_offset, byte_count};
data_source = &REGS(ctx)->transform_constants[index]; data_source = &REGS(ctx)->transform_constants[index];
} }
else if (auto xform_id = m_vertex_prog->TranslateConstantsRange(index, count); xform_id >= 0) else if (auto xform_id = m_vertex_prog->translate_constants_range(index, count); xform_id >= 0)
{ {
const auto write_offset = xform_id * 16; const auto write_offset = xform_id * 16;
const auto byte_count = count * 16; const auto byte_count = count * 16;

View file

@ -159,6 +159,7 @@ public:
GLGSRender(utils::serial* ar) noexcept; GLGSRender(utils::serial* ar) noexcept;
GLGSRender() noexcept : GLGSRender(nullptr) {} GLGSRender() noexcept : GLGSRender(nullptr) {}
virtual ~GLGSRender();
private: private:
@ -205,6 +206,9 @@ public:
// GRAPH backend // GRAPH backend
void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override; void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override;
// Misc
bool is_current_program_interpreted() const override;
protected: protected:
void clear_surface(u32 arg) override; void clear_surface(u32 arg) override;
void begin() override; void begin() override;

View file

@ -347,7 +347,7 @@ namespace gl
return data; return data;
} }
bool shader_interpreter::is_interpreter(const glsl::program* program) bool shader_interpreter::is_interpreter(const glsl::program* program) const
{ {
return (program == &m_current_interpreter->prog); return (program == &m_current_interpreter->prog);
} }

View file

@ -84,6 +84,6 @@ namespace gl
void update_fragment_textures(const std::array<std::unique_ptr<rsx::sampled_image_descriptor_base>, 16>& descriptors, u16 reference_mask, u32* out); void update_fragment_textures(const std::array<std::unique_ptr<rsx::sampled_image_descriptor_base>, 16>& descriptors, u16 reference_mask, u32* out);
glsl::program* get(const interpreter::program_metadata& fp_metadata); glsl::program* get(const interpreter::program_metadata& fp_metadata);
bool is_interpreter(const glsl::program* program); bool is_interpreter(const glsl::program* program) const;
}; };
} }

View file

@ -14,6 +14,7 @@ public:
virtual ~GSFrameBase() = default; virtual ~GSFrameBase() = default;
virtual void close() = 0; virtual void close() = 0;
virtual void reset() = 0;
virtual bool shown() = 0; virtual bool shown() = 0;
virtual void hide() = 0; virtual void hide() = 0;
virtual void show() = 0; virtual void show() = 0;

View file

@ -18,7 +18,7 @@ GSRender::~GSRender()
{ {
m_context = nullptr; m_context = nullptr;
if (m_frame) if (m_frame && !m_continuous_mode)
{ {
m_frame->close(); m_frame->close();
} }
@ -38,8 +38,11 @@ void GSRender::on_exit()
rsx::thread::on_exit(); rsx::thread::on_exit();
if (m_frame) if (m_frame)
{
if (!m_continuous_mode)
{ {
m_frame->hide(); m_frame->hide();
}
m_frame->delete_context(m_context); m_frame->delete_context(m_context);
m_context = nullptr; m_context = nullptr;
} }

View file

@ -21,12 +21,15 @@ class GSRender : public rsx::thread
protected: protected:
GSFrameBase* m_frame; GSFrameBase* m_frame;
draw_context_t m_context = nullptr; draw_context_t m_context = nullptr;
bool m_continuous_mode = false;
public: public:
~GSRender() override; ~GSRender() override;
GSRender(utils::serial* ar) noexcept; GSRender(utils::serial* ar) noexcept;
void set_continuous_mode(bool continuous_mode) { m_continuous_mode = continuous_mode; }
void on_init_thread() override; void on_init_thread() override;
void on_exit() override; void on_exit() override;

View file

@ -114,6 +114,8 @@ namespace rsx
Emu.CallFromMainThread([]() Emu.CallFromMainThread([]()
{ {
// Make sure we keep the game window opened
Emu.SetContinuousMode(true);
Emu.Restart(false); Emu.Restart(false);
}); });
return page_navigation::exit; return page_navigation::exit;

View file

@ -26,6 +26,9 @@ namespace rsx
if (!suspend_mode) if (!suspend_mode)
{ {
Emu.after_kill_callback = []() { Emu.Restart(); }; Emu.after_kill_callback = []() { Emu.Restart(); };
// Make sure we keep the game window opened
Emu.SetContinuousMode(true);
} }
Emu.Kill(false, true); Emu.Kill(false, true);
}); });

View file

@ -265,7 +265,6 @@ namespace rsx
fade_animation.end = color4f(1.f); fade_animation.end = color4f(1.f);
fade_animation.active = true; fade_animation.active = true;
this->on_close = std::move(on_close);
visible = true; visible = true;
const auto notify = std::make_shared<atomic_t<u32>>(0); const auto notify = std::make_shared<atomic_t<u32>>(0);

View file

@ -193,7 +193,7 @@ namespace rsx
// Get gamepad input // Get gamepad input
std::lock_guard lock(pad::g_pad_mutex); std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler(); const auto handler = pad::get_pad_thread();
const PadInfo& rinfo = handler->GetInfo(); const PadInfo& rinfo = handler->GetInfo();
const bool ignore_gamepad_input = (!rinfo.now_connect || !input::g_pads_intercepted); const bool ignore_gamepad_input = (!rinfo.now_connect || !input::g_pads_intercepted);

View file

@ -22,6 +22,15 @@ namespace rsx
using namespace rsx::fragment_program; using namespace rsx::fragment_program;
// SIMD vector lanes
enum VectorLane : u8
{
X = 0,
Y = 1,
Z = 2,
W = 3,
};
FragmentProgramDecompiler::FragmentProgramDecompiler(const RSXFragmentProgram &prog, u32& size) FragmentProgramDecompiler::FragmentProgramDecompiler(const RSXFragmentProgram &prog, u32& size)
: m_size(size) : m_size(size)
, m_prog(prog) , m_prog(prog)
@ -141,8 +150,7 @@ void FragmentProgramDecompiler::SetDst(std::string code, u32 flags)
AddCode(m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + dest + ";"); AddCode(m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + dest + ";");
} }
u32 reg_index = dst.fp16 ? dst.dest_reg >> 1 : dst.dest_reg; const u32 reg_index = dst.fp16 ? (dst.dest_reg >> 1) : dst.dest_reg;
ensure(reg_index < temp_registers.size()); ensure(reg_index < temp_registers.size());
if (dst.opcode == RSX_FP_OPCODE_MOV && if (dst.opcode == RSX_FP_OPCODE_MOV &&
@ -754,14 +762,26 @@ std::string FragmentProgramDecompiler::BuildCode()
const std::string init_value = float4_type + "(0.)"; const std::string init_value = float4_type + "(0.)";
std::array<std::string, 4> output_register_names; std::array<std::string, 4> output_register_names;
std::array<u32, 4> ouput_register_indices = { 0, 2, 3, 4 }; std::array<u32, 4> ouput_register_indices = { 0, 2, 3, 4 };
bool shader_is_valid = false;
// Holder for any "cleanup" before exiting main
std::stringstream main_epilogue;
// Check depth export // Check depth export
if (m_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) if (m_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT)
{ {
// Hw tests show that the depth export register is default-initialized to 0 and not wpos.z!! // Hw tests show that the depth export register is default-initialized to 0 and not wpos.z!!
m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "r1", init_value); m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "r1", init_value);
shader_is_valid = (!!temp_registers[1].h1_writes);
auto& r1 = temp_registers[1];
if (r1.requires_gather(VectorLane::Z))
{
// r1.zw was not written to
properties.has_gather_op = true;
main_epilogue << " r1.z = " << float4_type << r1.gather_r() << ".z;\n";
// Emit debug warning. Useful to diagnose regressions, but should be removed in future.
rsx_log.warning("ROP reads from shader depth without writing to it. Final value will be gathered.");
}
} }
// Add the color output registers. They are statically written to and have guaranteed initialization (except r1.z which == wpos.z) // Add the color output registers. They are statically written to and have guaranteed initialization (except r1.z which == wpos.z)
@ -775,28 +795,49 @@ std::string FragmentProgramDecompiler::BuildCode()
output_register_names = { "h0", "h4", "h6", "h8" }; output_register_names = { "h0", "h4", "h6", "h8" };
} }
for (int n = 0; n < 4; ++n) for (u32 n = 0; n < 4; ++n)
{ {
if (!m_parr.HasParam(PF_PARAM_NONE, float4_type, output_register_names[n])) const auto& reg_name = output_register_names[n];
if (!m_parr.HasParam(PF_PARAM_NONE, float4_type, reg_name))
{ {
m_parr.AddParam(PF_PARAM_NONE, float4_type, output_register_names[n], init_value); m_parr.AddParam(PF_PARAM_NONE, float4_type, reg_name, init_value);
}
if (n >= m_prog.mrt_buffers_count)
{
// Skip gather
continue; continue;
} }
const auto block_index = ouput_register_indices[n]; const auto block_index = ouput_register_indices[n];
shader_is_valid |= (!!temp_registers[block_index].h0_writes); auto& r = temp_registers[block_index];
if (fp16_out)
{
// Check if we need a split/extract op
if (r.requires_split(0))
{
main_epilogue << " " << reg_name << " = " << float4_type << r.split_h0() << ";\n";
// Emit debug warning. Useful to diagnose regressions, but should be removed in future.
rsx_log.warning("ROP reads from %s without writing to it. Final value will be extracted from the 32-bit register.", reg_name);
} }
if (!shader_is_valid) continue;
{
properties.has_no_output = true;
if (!properties.has_discard_op)
{
// NOTE: Discard operation overrides output
rsx_log.warning("Shader does not write to any output register and will be NOPed");
main = "/*" + main + "*/";
} }
if (!r.requires_gather128())
{
// Nothing to do
continue;
}
// We need to gather the data from existing registers
main_epilogue << " " << reg_name << " = " << float4_type << r.gather_r() << ";\n";
properties.has_gather_op = true;
// Emit debug warning. Useful to diagnose regressions, but should be removed in future.
rsx_log.warning("ROP reads from %s without writing to it. Final value will be gathered.", reg_name);
} }
if (properties.has_dynamic_register_load) if (properties.has_dynamic_register_load)
@ -822,6 +863,9 @@ std::string FragmentProgramDecompiler::BuildCode()
OS << "#endif\n"; OS << "#endif\n";
OS << " discard;\n"; OS << " discard;\n";
OS << "}\n"; OS << "}\n";
// Don't consume any args
m_parr.Clear();
return OS.str(); return OS.str();
} }
@ -1019,6 +1063,12 @@ std::string FragmentProgramDecompiler::BuildCode()
insertMainStart(OS); insertMainStart(OS);
OS << main << std::endl; OS << main << std::endl;
if (const auto epilogue = main_epilogue.str(); !epilogue.empty())
{
OS << " // Epilogue\n";
OS << epilogue << std::endl;
}
insertMainEnd(OS); insertMainEnd(OS);
return OS.str(); return OS.str();
@ -1360,12 +1410,12 @@ std::string FragmentProgramDecompiler::Decompile()
switch (opcode) switch (opcode)
{ {
case RSX_FP_OPCODE_NOP: break; case RSX_FP_OPCODE_NOP:
break;
case RSX_FP_OPCODE_KIL: case RSX_FP_OPCODE_KIL:
properties.has_discard_op = true; properties.has_discard_op = true;
AddFlowOp("_kill()"); AddFlowOp("_kill()");
break; break;
default: default:
int prev_force_unit = forced_unit; int prev_force_unit = forced_unit;

View file

@ -1,116 +1,10 @@
#pragma once #pragma once
#include "ShaderParam.h" #include "ShaderParam.h"
#include "FragmentProgramRegister.h"
#include "RSXFragmentProgram.h" #include "RSXFragmentProgram.h"
#include <sstream> #include <sstream>
// Helper for GPR occupancy tracking
struct temp_register
{
bool aliased_r0 = false;
bool aliased_h0 = false;
bool aliased_h1 = false;
bool last_write_half[4] = { false, false, false, false };
u32 real_index = -1;
u32 h0_writes = 0u; // Number of writes to the first 64-bits of the register
u32 h1_writes = 0u; // Number of writes to the last 64-bits of the register
void tag(u32 index, bool half_register, bool x, bool y, bool z, bool w)
{
if (half_register)
{
if (index & 1)
{
if (x) last_write_half[2] = true;
if (y) last_write_half[2] = true;
if (z) last_write_half[3] = true;
if (w) last_write_half[3] = true;
aliased_h1 = true;
h1_writes++;
}
else
{
if (x) last_write_half[0] = true;
if (y) last_write_half[0] = true;
if (z) last_write_half[1] = true;
if (w) last_write_half[1] = true;
aliased_h0 = true;
h0_writes++;
}
}
else
{
if (x) last_write_half[0] = false;
if (y) last_write_half[1] = false;
if (z) last_write_half[2] = false;
if (w) last_write_half[3] = false;
aliased_r0 = true;
h0_writes++;
h1_writes++;
}
if (real_index == umax)
{
if (half_register)
real_index = index >> 1;
else
real_index = index;
}
}
bool requires_gather(u8 channel) const
{
//Data fetched from the single precision register requires merging of the two half registers
ensure(channel < 4);
if (aliased_h0 && channel < 2)
{
return last_write_half[channel];
}
if (aliased_h1 && channel > 1)
{
return last_write_half[channel];
}
return false;
}
bool requires_split(u32 /*index*/) const
{
//Data fetched from any of the two half registers requires sync with the full register
if (!(last_write_half[0] || last_write_half[1]) && aliased_r0)
{
//r0 has been written to
//TODO: Check for specific elements in real32 register
return true;
}
return false;
}
std::string gather_r() const
{
std::string h0 = "h" + std::to_string(real_index << 1);
std::string h1 = "h" + std::to_string(real_index << 1 | 1);
std::string reg = "r" + std::to_string(real_index);
std::string ret = "//Invalid gather";
if (aliased_h0 && aliased_h1)
ret = "(gather(" + h0 + ", " + h1 + "))";
else if (aliased_h0)
ret = "(gather(" + h0 + "), " + reg + ".zw)";
else if (aliased_h1)
ret = "(" + reg + ".xy, gather(" + h1 + "))";
return ret;
}
};
/** /**
* This class is used to translate RSX Fragment program to GLSL/HLSL code * This class is used to translate RSX Fragment program to GLSL/HLSL code
* Backend with text based shader can subclass this class and implement : * Backend with text based shader can subclass this class and implement :
@ -157,7 +51,7 @@ class FragmentProgramDecompiler
bool m_is_valid_ucode = true; bool m_is_valid_ucode = true;
std::array<temp_register, 64> temp_registers; std::array<rsx::MixedPrecisionRegister, 64> temp_registers;
std::string GetMask() const; std::string GetMask() const;

View file

@ -0,0 +1,196 @@
#include "stdafx.h"
#include "FragmentProgramRegister.h"
namespace rsx
{
MixedPrecisionRegister::MixedPrecisionRegister()
{
std::fill(content_mask.begin(), content_mask.end(), data_type_bits::undefined);
}
void MixedPrecisionRegister::tag_h0(bool x, bool y, bool z, bool w)
{
if (x) content_mask[0] = data_type_bits::f16;
if (y) content_mask[1] = data_type_bits::f16;
if (z) content_mask[2] = data_type_bits::f16;
if (w) content_mask[3] = data_type_bits::f16;
}
void MixedPrecisionRegister::tag_h1(bool x, bool y, bool z, bool w)
{
if (x) content_mask[4] = data_type_bits::f16;
if (y) content_mask[5] = data_type_bits::f16;
if (z) content_mask[6] = data_type_bits::f16;
if (w) content_mask[7] = data_type_bits::f16;
}
void MixedPrecisionRegister::tag_r(bool x, bool y, bool z, bool w)
{
if (x) content_mask[0] = content_mask[1] = data_type_bits::f32;
if (y) content_mask[2] = content_mask[3] = data_type_bits::f32;
if (z) content_mask[4] = content_mask[5] = data_type_bits::f32;
if (w) content_mask[6] = content_mask[7] = data_type_bits::f32;
}
void MixedPrecisionRegister::tag(u32 index, bool is_fp16, bool x, bool y, bool z, bool w)
{
if (file_index == umax)
{
// First-time use. Initialize...
const u32 real_index = is_fp16 ? (index >> 1) : index;
file_index = real_index;
}
if (is_fp16)
{
ensure((index / 2) == file_index);
if (index & 1)
{
tag_h1(x, y, z, w);
return;
}
tag_h0(x, y, z, w);
return;
}
tag_r(x, y, z, w);
}
std::string MixedPrecisionRegister::gather_r() const
{
const auto half_index = file_index << 1;
const std::string reg = "r" + std::to_string(file_index);
const std::string gather_half_regs[] = {
"gather(h" + std::to_string(half_index) + ")",
"gather(h" + std::to_string(half_index + 1) + ")"
};
std::string outputs[4];
for (int ch = 0; ch < 4; ++ch)
{
// FIXME: This approach ignores mixed register bits. Not ideal!!!!
const auto channel0 = content_mask[ch * 2];
const auto is_fp16_ch = channel0 == content_mask[ch * 2 + 1] && channel0 == data_type_bits::f16;
outputs[ch] = is_fp16_ch ? gather_half_regs[ch / 2] : reg;
}
// Grouping. Only replace relevant bits...
if (outputs[0] == outputs[1]) outputs[0] = "";
if (outputs[2] == outputs[3]) outputs[2] = "";
// Assemble
bool group = false;
std::string result = "";
constexpr std::string_view swz_mask = "xyzw";
for (int ch = 0; ch < 4; ++ch)
{
if (outputs[ch].empty())
{
group = true;
continue;
}
if (!result.empty())
{
result += ", ";
}
if (group)
{
ensure(ch > 0);
group = false;
if (outputs[ch] == reg)
{
result += reg + "." + swz_mask[ch - 1] + swz_mask[ch];
continue;
}
result += outputs[ch];
continue;
}
const int subch = outputs[ch] == reg ? ch : (ch % 2); // Avoid .xyxy.z and other such ugly swizzles
result += outputs[ch] + "." + swz_mask[subch];
}
// Optimize dual-gather (128-bit gather) to use special function
const std::string double_gather = gather_half_regs[0] + ", " + gather_half_regs[1];
if (result == double_gather)
{
result = "gather(h" + std::to_string(half_index) + ", h" + std::to_string(half_index + 1) + ")";
}
return "(" + result + ")";
}
std::string MixedPrecisionRegister::fetch_halfreg(u32 word_index) const
{
// Reads half-word 0 (H16x4) from a full real (R32x4) register
constexpr std::string_view swz_mask = "xyzw";
const std::string reg = "r" + std::to_string(file_index);
const std::string hreg = "h" + std::to_string(file_index * 2 + word_index);
const std::string word0_bits = "floatBitsToUint(" + reg + "." + swz_mask[word_index * 2] + ")";
const std::string word1_bits = "floatBitsToUint(" + reg + "." + swz_mask[word_index * 2 + 1] + ")";
const std::string words[] = {
"unpackHalf2x16(" + word0_bits + ")",
"unpackHalf2x16(" + word1_bits + ")"
};
// Assemble
std::string outputs[4];
ensure(word_index <= 1);
const int word_offset = word_index * 4;
for (int ch = 0; ch < 4; ++ch)
{
outputs[ch] = content_mask[ch + word_offset] == data_type_bits::f32
? words[ch / 2]
: hreg;
}
// Grouping. Only replace relevant bits...
if (outputs[0] == outputs[1]) outputs[0] = "";
if (outputs[2] == outputs[3]) outputs[2] = "";
// Assemble
bool group = false;
std::string result = "";
for (int ch = 0; ch < 4; ++ch)
{
if (outputs[ch].empty())
{
group = true;
continue;
}
if (!result.empty())
{
result += ", ";
}
if (group)
{
ensure(ch > 0);
group = false;
result += outputs[ch];
if (outputs[ch] == hreg)
{
result += std::string(".") + swz_mask[ch - 1] + swz_mask[ch];
}
continue;
}
const int subch = outputs[ch] == hreg ? ch : (ch % 2); // Avoid .xyxy.z and other such ugly swizzles
result += outputs[ch] + "." + swz_mask[subch];
}
return "(" + result + ")";
}
}

View file

@ -0,0 +1,111 @@
#pragma once
#include <util/types.hpp>
namespace rsx
{
class MixedPrecisionRegister
{
enum data_type_bits
{
undefined = 0,
f16 = 1,
f32 = 2
};
std::array<data_type_bits, 8> content_mask; // Content details for each half-word
u32 file_index = umax;
void tag_h0(bool x, bool y, bool z, bool w);
void tag_h1(bool x, bool y, bool z, bool w);
void tag_r(bool x, bool y, bool z, bool w);
std::string fetch_halfreg(u32 word_index) const;
public:
MixedPrecisionRegister();
void tag(u32 index, bool is_fp16, bool x, bool y, bool z, bool w);
std::string gather_r() const;
std::string split_h0() const
{
return fetch_halfreg(0);
}
std::string split_h1() const
{
return fetch_halfreg(1);
}
// Getters
// Return true if all values are unwritten to (undefined)
bool floating() const
{
return file_index == umax;
}
// Return true if the first half register is all undefined
bool floating_h0() const
{
return content_mask[0] == content_mask[1] &&
content_mask[1] == content_mask[2] &&
content_mask[2] == content_mask[3] &&
content_mask[3] == data_type_bits::undefined;
}
// Return true if the second half register is all undefined
bool floating_h1() const
{
return content_mask[4] == content_mask[5] &&
content_mask[5] == content_mask[6] &&
content_mask[6] == content_mask[7] &&
content_mask[7] == data_type_bits::undefined;
}
// Return true if any of the half-words are 16-bit
bool requires_gather(u8 channel) const
{
// Data fetched from the single precision register requires merging of the two half registers
const auto channel_offset = channel * 2;
ensure(channel_offset <= 6);
return (content_mask[channel_offset] == data_type_bits::f16 || content_mask[channel_offset + 1] == data_type_bits::f16);
}
// Return true if the entire 128-bit register is filled with 2xfp16x4 data words
bool requires_gather128() const
{
// Full 128-bit check
for (const auto& ch : content_mask)
{
if (ch == data_type_bits::f16)
{
return true;
}
}
return false;
}
// Return true if the half-register is polluted with fp32 data
bool requires_split(u32 word_index) const
{
const u32 content_offset = word_index * 4;
for (u32 i = 0; i < 4; ++i)
{
if (content_mask[content_offset + i] == data_type_bits::f32)
{
return true;
}
}
return false;
}
};
}

View file

@ -86,8 +86,11 @@ layout(location=0) in vec4 in_regs[16];
#define CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT 0xe #define CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT 0xe
#define CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS 0x40 #define CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS 0x40
#define GET_BITS(word, offset, count) bitfieldExtract(inst.words[word], offset, count) #define GET_BITS(bitfield, offset, count) bitfieldExtract(bitfield, offset, count)
#define TEST_BIT(word, offset) (GET_BITS(word, offset, 1) > 0) #define TEST_BIT(bitfield, offset) (GET_BITS(bitfield, offset, 1) > 0)
#define GET_INST_BITS(word, offset, count) GET_BITS(inst.words[word], offset, count)
#define TEST_INST_BIT(word, offset) (GET_INST_BITS(word, offset, 1) > 0)
#define select mix #define select mix
#define reg_mov(d, s, m) d = select(d, s, m) #define reg_mov(d, s, m) d = select(d, s, m)
@ -174,7 +177,7 @@ int counter = 0;
vec4 read_src(const in int index) vec4 read_src(const in int index)
{ {
ur0 = GET_BITS(index + 1, 0, 2); ur0 = GET_INST_BITS(index + 1, 0, 2);
switch (ur0) switch (ur0)
{ {
@ -183,14 +186,14 @@ vec4 read_src(const in int index)
switch(index) switch(index)
{ {
case 0: case 0:
ur1 = GET_BITS(1, 2, 6); break; ur1 = GET_INST_BITS(1, 2, 6); break;
case 1: case 1:
ur1 = GET_BITS(2, 2, 6); break; ur1 = GET_INST_BITS(2, 2, 6); break;
case 2: case 2:
ur1 = GET_BITS(3, 2, 6); break; ur1 = GET_INST_BITS(3, 2, 6); break;
} }
if (TEST_BIT(index + 1, 8)) if (TEST_INST_BIT(index + 1, 8))
{ {
vr0 = regs16[ur1]; vr0 = regs16[ur1];
} }
@ -202,7 +205,7 @@ vec4 read_src(const in int index)
} }
case RSX_FP_REGISTER_TYPE_INPUT: case RSX_FP_REGISTER_TYPE_INPUT:
{ {
ur1 = GET_BITS(0, 13, 4); ur1 = GET_INST_BITS(0, 13, 4);
switch (ur1) switch (ur1)
{ {
case 0: case 0:
@ -235,27 +238,27 @@ vec4 read_src(const in int index)
} }
} }
ur1 = GET_BITS(index + 1, 9, 8); ur1 = GET_INST_BITS(index + 1, 9, 8);
vr0 = shuffle(vr0, ur1); vr0 = shuffle(vr0, ur1);
// abs // abs
if (index == 0) if (index == 0)
{ {
if (TEST_BIT(1, 29)) vr0 = abs(vr0); if (TEST_INST_BIT(1, 29)) vr0 = abs(vr0);
} }
else else
{ {
ur1 = index + 1; ur1 = index + 1;
if (TEST_BIT(ur1, 18)) vr0 = abs(vr0); if (TEST_INST_BIT(ur1, 18)) vr0 = abs(vr0);
} }
// neg // neg
return (TEST_BIT(index + 1, 17))? -vr0 : vr0; return (TEST_INST_BIT(index + 1, 17))? -vr0 : vr0;
} }
vec4 read_cond() vec4 read_cond()
{ {
return shuffle(cc[GET_BITS(1, 31, 1)], GET_BITS(1, 21, 8)); return shuffle(cc[GET_INST_BITS(1, 31, 1)], GET_INST_BITS(1, 21, 8));
} }
bvec4 decode_cond(const in uint mode, const in vec4 cond) bvec4 decode_cond(const in uint mode, const in vec4 cond)
@ -283,7 +286,7 @@ bvec4 decode_cond(const in uint mode, const in vec4 cond)
bool check_cond() bool check_cond()
{ {
ur0 = GET_BITS(1, 18, 3); ur0 = GET_INST_BITS(1, 18, 3);
if (ur0 == 0x7) if (ur0 == 0x7)
{ {
return true; return true;
@ -351,14 +354,14 @@ vec3 _texcoord_xform(const in vec3 coord, const in sampler_info params)
vec4 _texture(in vec4 coord, float bias) vec4 _texture(in vec4 coord, float bias)
{ {
ur0 = GET_BITS(0, 17, 4); ur0 = GET_INST_BITS(0, 17, 4);
if (!IS_TEXTURE_RESIDENT(ur0)) if (!IS_TEXTURE_RESIDENT(ur0))
{ {
return vr_zero; return vr_zero;
} }
ur1 = ur0 + ur0; ur1 = ur0 + ur0;
const uint type = bitfieldExtract(texture_control, int(ur1), 2); const uint type = GET_BITS(texture_control, int(ur1), 2);
switch (type) switch (type)
{ {
@ -380,7 +383,7 @@ vec4 _texture(in vec4 coord, float bias)
break; break;
} }
if (TEST_BIT(0, 21)) if (TEST_INST_BIT(0, 21))
{ {
vr0 = vr0 * 2. - 1.; vr0 = vr0 * 2. - 1.;
} }
@ -390,14 +393,14 @@ vec4 _texture(in vec4 coord, float bias)
vec4 _textureLod(in vec4 coord, float lod) vec4 _textureLod(in vec4 coord, float lod)
{ {
ur0 = GET_BITS(0, 17, 4); ur0 = GET_INST_BITS(0, 17, 4);
if (!IS_TEXTURE_RESIDENT(ur0)) if (!IS_TEXTURE_RESIDENT(ur0))
{ {
return vr_zero; return vr_zero;
} }
ur1 = ur0 + ur0; ur1 = ur0 + ur0;
const uint type = bitfieldExtract(texture_control, int(ur1), 2); const uint type = GET_BITS(texture_control, int(ur1), 2);
switch (type) switch (type)
{ {
@ -419,7 +422,7 @@ vec4 _textureLod(in vec4 coord, float lod)
break; break;
} }
if (TEST_BIT(0, 21)) if (TEST_INST_BIT(0, 21))
{ {
// Normal-expand, v = 2v - 1 // Normal-expand, v = 2v - 1
vr0 += vr0; vr0 += vr0;
@ -436,27 +439,27 @@ void write_dst(const in vec4 value)
uvr0 = uvec4(uint(1 << 9), uint(1 << 10), uint(1 << 11), uint(1 << 12)); uvr0 = uvec4(uint(1 << 9), uint(1 << 10), uint(1 << 11), uint(1 << 12));
bvr0 = bvec4(uvr0 & inst.words.xxxx); bvr0 = bvec4(uvr0 & inst.words.xxxx);
if (TEST_BIT(0, 8)) // SET COND if (TEST_INST_BIT(0, 8)) // SET COND
{ {
ur0 = GET_BITS(1, 30, 1); ur0 = GET_INST_BITS(1, 30, 1);
reg_mov(cc[ur0], value, bvr0); reg_mov(cc[ur0], value, bvr0);
} }
if (TEST_BIT(0, 30)) // NO DEST if (TEST_INST_BIT(0, 30)) // NO DEST
{ {
return; return;
} }
ur1 = GET_BITS(2, 28, 3); ur1 = GET_INST_BITS(2, 28, 3);
sr0 = modifier_scale[ur1]; sr0 = modifier_scale[ur1];
vr0 = value * sr0; vr0 = value * sr0;
if (TEST_BIT(0, 31)) // SAT if (TEST_INST_BIT(0, 31)) // SAT
{ {
vr0 = clamp(vr0, 0, 1); vr0 = clamp(vr0, 0, 1);
} }
ur0 = GET_BITS(1, 18, 3); ur0 = GET_INST_BITS(1, 18, 3);
if (ur0 != 0x7) if (ur0 != 0x7)
{ {
vr1 = read_cond(); vr1 = read_cond();
@ -464,8 +467,8 @@ void write_dst(const in vec4 value)
bvr0 = bvec4(uvec4(bvr0) & uvec4(bvr1)); bvr0 = bvec4(uvec4(bvr0) & uvec4(bvr1));
} }
ur1 = GET_BITS(0, 1, 6); ur1 = GET_INST_BITS(0, 1, 6);
if (TEST_BIT(0, 7)) if (TEST_INST_BIT(0, 7))
{ {
reg_mov(regs16[ur1], vr0, bvr0); reg_mov(regs16[ur1], vr0, bvr0);
} }
@ -481,7 +484,7 @@ void initialize()
// NOTE: Register count is the number of 'full' registers that will be consumed. Hardware seems to do some renaming. // NOTE: Register count is the number of 'full' registers that will be consumed. Hardware seems to do some renaming.
// NOTE: Attempting to zero-initialize all the registers will slow things to a crawl! // NOTE: Attempting to zero-initialize all the registers will slow things to a crawl!
uint register_count = bitfieldExtract(shader_control, 24, 6); uint register_count = GET_BITS(shader_control, 24, 6);
ur0 = 0, ur1 = 0; ur0 = 0, ur1 = 0;
while (register_count > 0) while (register_count > 0)
{ {
@ -587,11 +590,11 @@ void main()
((fp_instructions[ip] << 8) & uvec4(0xFF00FF00)) | ((fp_instructions[ip] << 8) & uvec4(0xFF00FF00)) |
((fp_instructions[ip] >> 8) & uvec4(0x00FF00FF)); ((fp_instructions[ip] >> 8) & uvec4(0x00FF00FF));
inst.opcode = GET_BITS(0, 24, 6); inst.opcode = GET_INST_BITS(0, 24, 6);
inst.end = TEST_BIT(0, 0); inst.end = TEST_INST_BIT(0, 0);
#ifdef WITH_FLOW_CTRL #ifdef WITH_FLOW_CTRL
if (TEST_BIT(2, 31)) if (TEST_INST_BIT(2, 31))
{ {
// Flow control // Flow control
switch (inst.opcode | (1 << 6)) switch (inst.opcode | (1 << 6))
@ -623,8 +626,8 @@ void main()
case RSX_FP_OPCODE_REP: case RSX_FP_OPCODE_REP:
if (check_cond()) if (check_cond())
{ {
counter = int(GET_BITS(2, 2, 8) - GET_BITS(2, 10, 8)); counter = int(GET_INST_BITS(2, 2, 8) - GET_INST_BITS(2, 10, 8));
counter /= int(GET_BITS(2, 19, 8)); counter /= int(GET_INST_BITS(2, 19, 8));
loop_start_addr = ip + 1; loop_start_addr = ip + 1;
loop_end_addr = int(inst.words.w >> 2); loop_end_addr = int(inst.words.w >> 2);
} }

View file

@ -34,7 +34,8 @@ vec2 texture2DMSCoord(const in vec2 coords, const in uint flags)
return coords; return coords;
} }
const vec2 wrapped_coords = mod(coords, vec2(1.0)); const vec2 wrapped_coords_raw = mod(coords, vec2(1.0));
const vec2 wrapped_coords = mod(wrapped_coords_raw + vec2(1.0), vec2(1.0));
const bvec2 wrap_control_mask = bvec2(uvec2(flags) & uvec2(WRAP_S_MASK, WRAP_T_MASK)); const bvec2 wrap_control_mask = bvec2(uvec2(flags) & uvec2(WRAP_S_MASK, WRAP_T_MASK));
return _select(coords, wrapped_coords, wrap_control_mask); return _select(coords, wrapped_coords, wrap_control_mask);
} }

View file

@ -340,12 +340,16 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert
usz vertex_program_storage_hash::operator()(const RSXVertexProgram &program) const usz vertex_program_storage_hash::operator()(const RSXVertexProgram &program) const
{ {
usz hash = vertex_program_utils::get_vertex_program_ucode_hash(program); const usz ucode_hash = vertex_program_utils::get_vertex_program_ucode_hash(program);
hash ^= program.ctrl; const u32 state_params[] =
hash ^= program.output_mask; {
hash ^= program.texture_state.texture_dimensions; program.ctrl,
hash ^= program.texture_state.multisampled_textures; program.output_mask,
return hash; program.texture_state.texture_dimensions,
program.texture_state.multisampled_textures,
};
const usz metadata_hash = rpcs3::hash_array(state_params);
return rpcs3::hash64(ucode_hash, metadata_hash);
} }
bool vertex_program_compare::operator()(const RSXVertexProgram &binary1, const RSXVertexProgram &binary2) const bool vertex_program_compare::operator()(const RSXVertexProgram &binary1, const RSXVertexProgram &binary2) const
@ -541,24 +545,33 @@ usz fragment_program_utils::get_fragment_program_ucode_hash(const RSXFragmentPro
usz fragment_program_storage_hash::operator()(const RSXFragmentProgram& program) const usz fragment_program_storage_hash::operator()(const RSXFragmentProgram& program) const
{ {
usz hash = fragment_program_utils::get_fragment_program_ucode_hash(program); const usz ucode_hash = fragment_program_utils::get_fragment_program_ucode_hash(program);
hash ^= program.ctrl; const u32 state_params[] =
hash ^= +program.two_sided_lighting; {
hash ^= program.texture_state.texture_dimensions; program.ctrl,
hash ^= program.texture_state.shadow_textures; program.two_sided_lighting ? 1u : 0u,
hash ^= program.texture_state.redirected_textures; program.texture_state.texture_dimensions,
hash ^= program.texture_state.multisampled_textures; program.texture_state.shadow_textures,
hash ^= program.texcoord_control_mask; program.texture_state.redirected_textures,
program.texture_state.multisampled_textures,
return hash; program.texcoord_control_mask,
program.mrt_buffers_count
};
const usz metadata_hash = rpcs3::hash_array(state_params);
return rpcs3::hash64(ucode_hash, metadata_hash);
} }
bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, const RSXFragmentProgram& binary2) const bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, const RSXFragmentProgram& binary2) const
{ {
if (binary1.ctrl != binary2.ctrl || binary1.texture_state != binary2.texture_state || if (binary1.ucode_length != binary2.ucode_length ||
binary1.ctrl != binary2.ctrl ||
binary1.texture_state != binary2.texture_state ||
binary1.texcoord_control_mask != binary2.texcoord_control_mask || binary1.texcoord_control_mask != binary2.texcoord_control_mask ||
binary1.two_sided_lighting != binary2.two_sided_lighting) binary1.two_sided_lighting != binary2.two_sided_lighting ||
binary1.mrt_buffers_count != binary2.mrt_buffers_count)
{
return false; return false;
}
const void* instBuffer1 = binary1.get_data(); const void* instBuffer1 = binary1.get_data();
const void* instBuffer2 = binary2.get_data(); const void* instBuffer2 = binary2.get_data();
@ -569,7 +582,9 @@ bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, con
const auto inst2 = v128::loadu(instBuffer2, instIndex); const auto inst2 = v128::loadu(instBuffer2, instIndex);
if (inst1._u ^ inst2._u) if (inst1._u ^ inst2._u)
{
return false; return false;
}
instIndex++; instIndex++;
// Skip constants // Skip constants
@ -578,10 +593,12 @@ bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, con
fragment_program_utils::is_constant(inst1._u32[3])) fragment_program_utils::is_constant(inst1._u32[3]))
instIndex++; instIndex++;
bool end = ((inst1._u32[0] >> 8) & 0x1) && ((inst2._u32[0] >> 8) & 0x1); const bool end = ((inst1._u32[0] >> 8) & 0x1) && ((inst2._u32[0] >> 8) & 0x1);
if (end) if (end)
{
return true; return true;
} }
}
} }
namespace rsx namespace rsx

View file

@ -295,8 +295,8 @@ public:
Args&& ...args Args&& ...args
) )
{ {
const auto &vp_search = search_vertex_program(vertexShader); const auto& vp_search = search_vertex_program(vertexShader);
const auto &fp_search = search_fragment_program(fragmentShader); const auto& fp_search = search_fragment_program(fragmentShader);
const bool already_existing_fragment_program = std::get<1>(fp_search); const bool already_existing_fragment_program = std::get<1>(fp_search);
const bool already_existing_vertex_program = std::get<1>(vp_search); const bool already_existing_vertex_program = std::get<1>(vp_search);
@ -385,7 +385,13 @@ public:
void fill_fragment_constants_buffer(std::span<f32> dst_buffer, const fragment_program_type& fragment_program, const RSXFragmentProgram& rsx_prog, bool sanitize = false) const void fill_fragment_constants_buffer(std::span<f32> dst_buffer, const fragment_program_type& fragment_program, const RSXFragmentProgram& rsx_prog, bool sanitize = false) const
{ {
ensure((dst_buffer.size_bytes() >= ::narrow<int>(fragment_program.FragmentConstantOffsetCache.size()) * 16u)); if (dst_buffer.size_bytes() < (fragment_program.FragmentConstantOffsetCache.size() * 16))
{
// This can happen if CELL alters the shader after it has been loaded by RSX.
rsx_log.error("Insufficient constants buffer size passed to fragment program! Corrupt shader?");
return;
}
rsx::write_fragment_constants_to_buffer(dst_buffer, rsx_prog, fragment_program.FragmentConstantOffsetCache, sanitize); rsx::write_fragment_constants_to_buffer(dst_buffer, rsx_prog, fragment_program.FragmentConstantOffsetCache, sanitize);
} }

View file

@ -300,8 +300,10 @@ struct RSXFragmentProgram
u32 ucode_length = 0; u32 ucode_length = 0;
u32 total_length = 0; u32 total_length = 0;
u32 ctrl = 0; u32 ctrl = 0;
bool two_sided_lighting = false;
u32 texcoord_control_mask = 0; u32 texcoord_control_mask = 0;
u32 mrt_buffers_count = 0;
bool two_sided_lighting = false;
rsx::fragment_program_texture_state texture_state; rsx::fragment_program_texture_state texture_state;
rsx::fragment_program_texture_config texture_params; rsx::fragment_program_texture_config texture_params;

View file

@ -223,10 +223,10 @@ struct RSXVertexProgram
{ {
std::vector<u32> data; std::vector<u32> data;
rsx::vertex_program_texture_state texture_state; rsx::vertex_program_texture_state texture_state;
u32 ctrl; u32 ctrl = 0;
u32 output_mask; u32 output_mask = 0;
u32 base_address; u32 base_address = 0;
u32 entry; u32 entry = 0;
std::bitset<rsx::max_vertex_program_instructions> instruction_mask; std::bitset<rsx::max_vertex_program_instructions> instruction_mask;
std::set<u32> jump_table; std::set<u32> jump_table;

View file

@ -227,6 +227,14 @@ struct ParamArray
return name; return name;
} }
void Clear()
{
for (auto& param : params)
{
param.clear();
}
}
}; };
class ShaderVariable class ShaderVariable

View file

@ -110,7 +110,7 @@ namespace rsx
multisampled_textures == other.multisampled_textures; multisampled_textures == other.multisampled_textures;
} }
int VertexProgramBase::TranslateConstantsRange(int first_index, int count) const int VertexProgramBase::translate_constants_range(int first_index, int count) const
{ {
// The constant ids should be sorted, so just find the first one and check for continuity // The constant ids should be sorted, so just find the first one and check for continuity
int index = -1; int index = -1;
@ -157,4 +157,31 @@ namespace rsx
// OOB or partial match // OOB or partial match
return -1; return -1;
} }
bool VertexProgramBase::overlaps_constants_range(int first_index, int count) const
{
if (has_indexed_constants)
{
return true;
}
const int last_index = first_index + count - 1;
// Early rejection test
if (constant_ids.empty() || first_index > constant_ids.back() || last_index < first_index)
{
return false;
}
// Check for any hits
for (auto& idx : constant_ids)
{
if (idx >= first_index && idx <= last_index)
{
return true;
}
}
return false;
}
} }

View file

@ -67,6 +67,9 @@ namespace rsx
// Translates an incoming range of constants against our mapping. // Translates an incoming range of constants against our mapping.
// If there is no linear mapping available, return -1, otherwise returns the translated index of the first slot // If there is no linear mapping available, return -1, otherwise returns the translated index of the first slot
// TODO: Move this somewhere else during refactor // TODO: Move this somewhere else during refactor
int TranslateConstantsRange(int first_index, int count) const; int translate_constants_range(int first_index, int count) const;
// Returns true if this program consumes any constants in the range [first, first + count - 1]
bool overlaps_constants_range(int first_index, int count) const;
}; };
} }

View file

@ -1719,7 +1719,7 @@ namespace rsx
for (uint i = 0; i < mrt_buffers.size(); ++i) for (uint i = 0; i < mrt_buffers.size(); ++i)
{ {
if (rsx::method_registers.color_write_enabled(i)) if (m_ctx->register_state->color_write_enabled(i))
{ {
const auto real_index = mrt_buffers[i]; const auto real_index = mrt_buffers[i];
m_framebuffer_layout.color_write_enabled[real_index] = true; m_framebuffer_layout.color_write_enabled[real_index] = true;
@ -1727,6 +1727,14 @@ namespace rsx
} }
} }
if (::size32(mrt_buffers) != current_fragment_program.mrt_buffers_count &&
!m_graphics_state.test(rsx::pipeline_state::fragment_program_dirty) &&
!is_current_program_interpreted())
{
// Notify that we should recompile the FS
m_graphics_state |= rsx::pipeline_state::fragment_program_state_dirty;
}
return any_found; return any_found;
}; };
@ -2038,9 +2046,10 @@ namespace rsx
m_graphics_state.clear(rsx::pipeline_state::fragment_program_dirty); m_graphics_state.clear(rsx::pipeline_state::fragment_program_dirty);
current_fragment_program.ctrl = rsx::method_registers.shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT); current_fragment_program.ctrl = m_ctx->register_state->shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT);
current_fragment_program.texcoord_control_mask = rsx::method_registers.texcoord_control_mask(); current_fragment_program.texcoord_control_mask = m_ctx->register_state->texcoord_control_mask();
current_fragment_program.two_sided_lighting = rsx::method_registers.two_side_light_en(); current_fragment_program.two_sided_lighting = m_ctx->register_state->two_side_light_en();
current_fragment_program.mrt_buffers_count = rsx::utility::get_mrt_buffers_count(m_ctx->register_state->surface_color_target());
if (method_registers.current_draw_clause.classify_mode() == primitive_class::polygon) if (method_registers.current_draw_clause.classify_mode() == primitive_class::polygon)
{ {

View file

@ -436,6 +436,8 @@ namespace rsx
bool is_current_vertex_program_instanced() const { return !!(current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS); } bool is_current_vertex_program_instanced() const { return !!(current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS); }
virtual bool is_current_program_interpreted() const { return false; }
public: public:
void reset(); void reset();
void init(u32 ctrlAddress); void init(u32 ctrlAddress);

View file

@ -0,0 +1,160 @@
#include "stdafx.h"
#include "vkutils/device.h"
#include "vkutils/descriptors.h"
#include "VKCommonPipelineLayout.h"
#include "VKHelpers.h"
#include "Emu/RSX/Common/simple_array.hpp"
namespace vk
{
rsx::simple_array<VkDescriptorSetLayoutBinding> get_common_binding_table()
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
rsx::simple_array<VkDescriptorSetLayoutBinding> bindings(binding_table.instancing_constants_buffer_slot + 1);
u32 idx = 0;
// Vertex stream, one stream for cacheable data, one stream for transient data
for (int i = 0; i < 3; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_buffers_first_bind_slot + i;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_state_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_texture_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
bindings[idx].binding = binding_table.vertex_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.conditional_render_predicate_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.rasterizer_env_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.instancing_lookup_table_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.instancing_constants_buffer_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
return bindings;
}
std::tuple<VkPipelineLayout, VkDescriptorSetLayout> get_common_pipeline_layout(VkDevice dev)
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
auto bindings = get_common_binding_table();
u32 idx = ::size32(bindings);
bindings.resize(binding_table.total_descriptor_bindings);
for (auto binding = binding_table.textures_first_bind_slot;
binding < binding_table.vertex_textures_first_bind_slot;
binding++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
for (int i = 0; i < rsx::limits::vertex_textures_count; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_textures_first_bind_slot + i;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
ensure(idx == binding_table.total_descriptor_bindings);
std::array<VkPushConstantRange, 1> push_constants;
push_constants[0].offset = 0;
push_constants[0].size = 16;
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
if (vk::emulate_conditional_rendering())
{
// Conditional render toggle
push_constants[0].size = 20;
}
const auto set_layout = vk::descriptors::create_layout(bindings);
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &set_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants.data();
VkPipelineLayout result;
CHECK_RESULT(vkCreatePipelineLayout(dev, &layout_info, nullptr, &result));
return std::make_tuple(result, set_layout);
}
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "vkutils/shared.h"
#include "Emu/RSX/Common/simple_array.hpp"
namespace vk
{
// Grab standard layout for decompiled RSX programs. Also used by the interpreter.
// FIXME: This generates a bloated monstrosity that needs to die.
std::tuple<VkPipelineLayout, VkDescriptorSetLayout> get_common_pipeline_layout(VkDevice dev);
// Returns the standard binding layout without texture slots. Those have special handling depending on the consumer.
rsx::simple_array<VkDescriptorSetLayoutBinding> get_common_binding_table();
}

View file

@ -5,6 +5,7 @@
#include "VKAsyncScheduler.h" #include "VKAsyncScheduler.h"
#include "VKCommandStream.h" #include "VKCommandStream.h"
#include "VKCommonDecompiler.h" #include "VKCommonDecompiler.h"
#include "VKCommonPipelineLayout.h"
#include "VKCompute.h" #include "VKCompute.h"
#include "VKGSRender.h" #include "VKGSRender.h"
#include "VKHelpers.h" #include "VKHelpers.h"
@ -401,148 +402,6 @@ namespace vk
} }
} }
namespace
{
std::tuple<VkPipelineLayout, VkDescriptorSetLayout> get_shared_pipeline_layout(VkDevice dev)
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
rsx::simple_array<VkDescriptorSetLayoutBinding> bindings(binding_table.total_descriptor_bindings);
u32 idx = 0;
// Vertex stream, one stream for cacheable data, one stream for transient data
for (int i = 0; i < 3; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_buffers_first_bind_slot + i;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_state_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_texture_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
bindings[idx].binding = binding_table.vertex_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.conditional_render_predicate_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.rasterizer_env_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.instancing_lookup_table_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.instancing_constants_buffer_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
for (auto binding = binding_table.textures_first_bind_slot;
binding < binding_table.vertex_textures_first_bind_slot;
binding++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
for (int i = 0; i < rsx::limits::vertex_textures_count; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_textures_first_bind_slot + i;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
ensure(idx == binding_table.total_descriptor_bindings);
std::array<VkPushConstantRange, 1> push_constants;
push_constants[0].offset = 0;
push_constants[0].size = 16;
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
if (vk::emulate_conditional_rendering())
{
// Conditional render toggle
push_constants[0].size = 20;
}
const auto set_layout = vk::descriptors::create_layout(bindings);
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &set_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants.data();
VkPipelineLayout result;
CHECK_RESULT(vkCreatePipelineLayout(dev, &layout_info, nullptr, &result));
return std::make_tuple(result, set_layout);
}
}
u64 VKGSRender::get_cycles() u64 VKGSRender::get_cycles()
{ {
return thread_ctrl::get_cycles(static_cast<named_thread<VKGSRender>&>(*this)); return thread_ctrl::get_cycles(static_cast<named_thread<VKGSRender>&>(*this));
@ -633,7 +492,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
m_secondary_cb_list.create(m_secondary_command_buffer_pool, vk::command_buffer::access_type_hint::all); m_secondary_cb_list.create(m_secondary_command_buffer_pool, vk::command_buffer::access_type_hint::all);
//Precalculated stuff //Precalculated stuff
std::tie(m_pipeline_layout, m_descriptor_layouts) = get_shared_pipeline_layout(*m_device); std::tie(m_pipeline_layout, m_descriptor_layouts) = vk::get_common_pipeline_layout(*m_device);
//Occlusion //Occlusion
m_occlusion_query_manager = std::make_unique<vk::query_pool_manager>(*m_device, VK_QUERY_TYPE_OCCLUSION, OCCLUSION_MAX_POOL_SIZE); m_occlusion_query_manager = std::make_unique<vk::query_pool_manager>(*m_device, VK_QUERY_TYPE_OCCLUSION, OCCLUSION_MAX_POOL_SIZE);
@ -688,10 +547,10 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
} }
// Initialize optional allocation information with placeholders // Initialize optional allocation information with placeholders
m_vertex_env_buffer_info = { m_vertex_env_ring_info.heap->value, 0, 32 }; m_vertex_env_buffer_info = { m_vertex_env_ring_info.heap->value, 0, 16 };
m_vertex_constants_buffer_info = { m_transform_constants_ring_info.heap->value, 0, 32 }; m_vertex_constants_buffer_info = { m_transform_constants_ring_info.heap->value, 0, 16 };
m_fragment_env_buffer_info = { m_fragment_env_ring_info.heap->value, 0, 32 }; m_fragment_env_buffer_info = { m_fragment_env_ring_info.heap->value, 0, 16 };
m_fragment_texture_params_buffer_info = { m_fragment_texture_params_ring_info.heap->value, 0, 32 }; m_fragment_texture_params_buffer_info = { m_fragment_texture_params_ring_info.heap->value, 0, 16 };
m_raster_env_buffer_info = { m_raster_env_ring_info.heap->value, 0, 128 }; m_raster_env_buffer_info = { m_raster_env_ring_info.heap->value, 0, 128 };
const auto limits = m_device->gpu().get_limits(); const auto limits = m_device->gpu().get_limits();
@ -730,7 +589,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
else else
m_vertex_cache = std::make_unique<vk::weak_vertex_cache>(); m_vertex_cache = std::make_unique<vk::weak_vertex_cache>();
m_shaders_cache = std::make_unique<vk::shader_cache>(*m_prog_buffer, "vulkan", "v1.94"); m_shaders_cache = std::make_unique<vk::shader_cache>(*m_prog_buffer, "vulkan", "v1.95");
for (u32 i = 0; i < m_swapchain->get_swap_image_count(); ++i) for (u32 i = 0; i < m_swapchain->get_swap_image_count(); ++i)
{ {
@ -2192,7 +2051,7 @@ void VKGSRender::load_program_env()
return std::make_pair(m_instancing_buffer_ring_info.map(constants_data_table_offset, size), size); return std::make_pair(m_instancing_buffer_ring_info.map(constants_data_table_offset, size), size);
}); });
m_draw_processor.fill_constants_instancing_buffer(indirection_table_buf, constants_array_buf, *m_vertex_prog); m_draw_processor.fill_constants_instancing_buffer(indirection_table_buf, constants_array_buf, m_vertex_prog);
m_instancing_buffer_ring_info.unmap(); m_instancing_buffer_ring_info.unmap();
m_instancing_indirection_buffer_info = { m_instancing_buffer_ring_info.heap->value, indirection_table_offset, indirection_table_buf.size() }; m_instancing_indirection_buffer_info = { m_instancing_buffer_ring_info.heap->value, indirection_table_offset, indirection_table_buf.size() };
@ -2219,7 +2078,7 @@ void VKGSRender::load_program_env()
} }
} }
if (update_fragment_constants && !update_instruction_buffers) if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program))
{ {
check_heap_status(VK_HEAP_CHECK_FRAGMENT_CONSTANTS_STORAGE); check_heap_status(VK_HEAP_CHECK_FRAGMENT_CONSTANTS_STORAGE);
@ -2350,9 +2209,9 @@ void VKGSRender::load_program_env()
} }
// Clear flags // Clear flags
u32 handled_flags = rsx::pipeline_state::fragment_state_dirty | rsx::flags32_t handled_flags =
rsx::pipeline_state::fragment_state_dirty |
rsx::pipeline_state::vertex_state_dirty | rsx::pipeline_state::vertex_state_dirty |
rsx::pipeline_state::fragment_constants_dirty |
rsx::pipeline_state::fragment_texture_state_dirty; rsx::pipeline_state::fragment_texture_state_dirty;
if (!update_instancing_data) if (!update_instancing_data)
@ -2360,9 +2219,19 @@ void VKGSRender::load_program_env()
handled_flags |= rsx::pipeline_state::transform_constants_dirty; handled_flags |= rsx::pipeline_state::transform_constants_dirty;
} }
if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program))
{
handled_flags |= rsx::pipeline_state::fragment_constants_dirty;
}
m_graphics_state.clear(handled_flags); m_graphics_state.clear(handled_flags);
} }
bool VKGSRender::is_current_program_interpreted() const
{
return m_program && m_shader_interpreter.is_interpreter(m_program);
}
void VKGSRender::upload_transform_constants(const rsx::io_buffer& buffer) void VKGSRender::upload_transform_constants(const rsx::io_buffer& buffer)
{ {
const usz transform_constants_size = (!m_vertex_prog || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16; const usz transform_constants_size = (!m_vertex_prog || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16;
@ -2433,13 +2302,19 @@ void VKGSRender::update_vertex_env(u32 id, const vk::vertex_upload_info& vertex_
void VKGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 count) void VKGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 count)
{ {
if (!m_vertex_prog) if (!m_program || !m_vertex_prog)
{ {
// Shouldn't be reachable, but handle it correctly anyway // Shouldn't be reachable, but handle it correctly anyway
m_graphics_state |= rsx::pipeline_state::transform_constants_dirty; m_graphics_state |= rsx::pipeline_state::transform_constants_dirty;
return; return;
} }
if (!m_vertex_prog->overlaps_constants_range(index, count))
{
// Nothing meaningful to us
return;
}
// Hot-patching transform constants mid-draw (instanced draw) // Hot-patching transform constants mid-draw (instanced draw)
std::pair<VkDeviceSize, VkDeviceSize> data_range; std::pair<VkDeviceSize, VkDeviceSize> data_range;
void* data_source = nullptr; void* data_source = nullptr;
@ -2453,7 +2328,7 @@ void VKGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 cou
data_range = { m_vertex_constants_buffer_info.offset + byte_offset, byte_count }; data_range = { m_vertex_constants_buffer_info.offset + byte_offset, byte_count };
data_source = &REGS(ctx)->transform_constants[index]; data_source = &REGS(ctx)->transform_constants[index];
} }
else if (auto xform_id = m_vertex_prog->TranslateConstantsRange(index, count); xform_id >= 0) else if (auto xform_id = m_vertex_prog->translate_constants_range(index, count); xform_id >= 0)
{ {
const auto write_offset = xform_id * 16; const auto write_offset = xform_id * 16;
const auto byte_count = count * 16; const auto byte_count = count * 16;

View file

@ -288,6 +288,9 @@ public:
// GRAPH backend // GRAPH backend
void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override; void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override;
// Misc
bool is_current_program_interpreted() const override;
protected: protected:
void clear_surface(u32 mask) override; void clear_surface(u32 mask) override;
void begin() override; void begin() override;

View file

@ -63,7 +63,7 @@ namespace vk
const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& fs_inputs) const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& fs_inputs)
{ {
VkPipeline pipeline; VkPipeline pipeline;
CHECK_RESULT(vkCreateGraphicsPipelines(*m_device, nullptr, 1, &create_info, NULL, &pipeline)); CHECK_RESULT(vkCreateGraphicsPipelines(*m_device, VK_NULL_HANDLE, 1, &create_info, nullptr, &pipeline));
auto result = std::make_unique<vk::glsl::program>(*m_device, pipeline, pipe_layout, vs_inputs, fs_inputs); auto result = std::make_unique<vk::glsl::program>(*m_device, pipeline, pipe_layout, vs_inputs, fs_inputs);
result->link(); result->link();
return result; return result;

View file

@ -387,7 +387,7 @@ namespace vk
struct stencilonly_unresolve : depth_resolve_base struct stencilonly_unresolve : depth_resolve_base
{ {
VkClearRect region{}; VkClearRect clear_region{};
VkClearAttachment clear_info{}; VkClearAttachment clear_info{};
stencilonly_unresolve() stencilonly_unresolve()
@ -402,8 +402,8 @@ namespace vk
renderpass_config.set_depth_mask(false); renderpass_config.set_depth_mask(false);
clear_info.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; clear_info.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
region.baseArrayLayer = 0; clear_region.baseArrayLayer = 0;
region.layerCount = 1; clear_region.layerCount = 1;
static_parameters_width = 3; static_parameters_width = 3;
@ -425,7 +425,7 @@ namespace vk
void emit_geometry(vk::command_buffer& cmd) override void emit_geometry(vk::command_buffer& cmd) override
{ {
vkCmdClearAttachments(cmd, 1, &clear_info, 1, &region); vkCmdClearAttachments(cmd, 1, &clear_info, 1, &clear_region);
for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1)
{ {
@ -444,8 +444,8 @@ namespace vk
auto stencil_view = resolve_image->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_IDENTITY), VK_IMAGE_ASPECT_STENCIL_BIT); auto stencil_view = resolve_image->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_IDENTITY), VK_IMAGE_ASPECT_STENCIL_BIT);
region.rect.extent.width = resolve_image->width(); clear_region.rect.extent.width = msaa_image->width();
region.rect.extent.height = resolve_image->height(); clear_region.rect.extent.height = msaa_image->height();
overlay_pass::run( overlay_pass::run(
cmd, cmd,

View file

@ -1,5 +1,6 @@
#include "stdafx.h" #include "stdafx.h"
#include "VKShaderInterpreter.h" #include "VKShaderInterpreter.h"
#include "VKCommonPipelineLayout.h"
#include "VKVertexProgram.h" #include "VKVertexProgram.h"
#include "VKFragmentProgram.h" #include "VKFragmentProgram.h"
#include "VKGSRender.h" #include "VKGSRender.h"
@ -233,77 +234,12 @@ namespace vk
std::pair<VkDescriptorSetLayout, VkPipelineLayout> shader_interpreter::create_layout(VkDevice dev) std::pair<VkDescriptorSetLayout, VkPipelineLayout> shader_interpreter::create_layout(VkDevice dev)
{ {
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table(); const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
rsx::simple_array<VkDescriptorSetLayoutBinding> bindings(binding_table.total_descriptor_bindings); auto bindings = get_common_binding_table();
u32 idx = ::size32(bindings);
u32 idx = 0; bindings.resize(binding_table.total_descriptor_bindings);
// Vertex stream, one stream for cacheable data, one stream for transient data. Third stream contains vertex layout info
for (int i = 0; i < 3; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_buffers_first_bind_slot + i;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_state_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_texture_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
bindings[idx].binding = binding_table.vertex_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.conditional_render_predicate_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.rasterizer_env_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
// Texture 1D array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16; bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
@ -313,6 +249,7 @@ namespace vk
m_fragment_textures_start = bindings[idx].binding; m_fragment_textures_start = bindings[idx].binding;
idx++; idx++;
// Texture 2D array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16; bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
@ -321,6 +258,7 @@ namespace vk
idx++; idx++;
// Texture 3D array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16; bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
@ -329,6 +267,7 @@ namespace vk
idx++; idx++;
// Texture CUBE array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16; bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
@ -337,6 +276,7 @@ namespace vk
idx++; idx++;
// Vertex texture array (2D only)
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 4; bindings[idx].descriptorCount = 4;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
@ -345,6 +285,7 @@ namespace vk
idx++; idx++;
// Vertex program ucode block
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1; bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
@ -354,6 +295,7 @@ namespace vk
m_vertex_instruction_start = bindings[idx].binding; m_vertex_instruction_start = bindings[idx].binding;
idx++; idx++;
// Fragment program ucode block
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1; bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
@ -364,6 +306,22 @@ namespace vk
idx++; idx++;
bindings.resize(idx); bindings.resize(idx);
// Compile descriptor pool sizes
const u32 num_ubo = bindings.reduce(0, FN(x + (y.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ? y.descriptorCount : 0)));
const u32 num_texel_buffers = bindings.reduce(0, FN(x + (y.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER ? y.descriptorCount : 0)));
const u32 num_combined_image_sampler = bindings.reduce(0, FN(x + (y.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ? y.descriptorCount : 0)));
const u32 num_ssbo = bindings.reduce(0, FN(x + (y.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ? y.descriptorCount : 0)));
ensure(num_ubo > 0 && num_texel_buffers > 0 && num_combined_image_sampler > 0 && num_ssbo > 0);
m_descriptor_pool_sizes =
{
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , num_ubo },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , num_texel_buffers },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , num_combined_image_sampler },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, num_ssbo }
};
std::array<VkPushConstantRange, 1> push_constants; std::array<VkPushConstantRange, 1> push_constants;
push_constants[0].offset = 0; push_constants[0].offset = 0;
push_constants[0].size = 16; push_constants[0].size = 16;
@ -392,16 +350,7 @@ namespace vk
void shader_interpreter::create_descriptor_pools(const vk::render_device& dev) void shader_interpreter::create_descriptor_pools(const vk::render_device& dev)
{ {
const auto max_draw_calls = dev.get_descriptor_max_draw_calls(); const auto max_draw_calls = dev.get_descriptor_max_draw_calls();
m_descriptor_pool.create(dev, m_descriptor_pool_sizes, max_draw_calls);
rsx::simple_array<VkDescriptorPoolSize> sizes =
{
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 6 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , 3 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 68 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3 }
};
m_descriptor_pool.create(dev, sizes, max_draw_calls);
} }
void shader_interpreter::init(const vk::render_device& dev) void shader_interpreter::init(const vk::render_device& dev)
@ -410,6 +359,7 @@ namespace vk
std::tie(m_shared_descriptor_layout, m_shared_pipeline_layout) = create_layout(dev); std::tie(m_shared_descriptor_layout, m_shared_pipeline_layout) = create_layout(dev);
create_descriptor_pools(dev); create_descriptor_pools(dev);
rsx_log.notice("Building global vertex program interpreter...");
build_vs(); build_vs();
// TODO: Seed the cache // TODO: Seed the cache
} }
@ -449,6 +399,7 @@ namespace vk
} }
else else
{ {
rsx_log.notice("Compiling FS...");
fs = build_fs(compiler_opt); fs = build_fs(compiler_opt);
} }
@ -463,15 +414,17 @@ namespace vk
shader_stages[1].module = fs->get_handle(); shader_stages[1].module = fs->get_handle();
shader_stages[1].pName = "main"; shader_stages[1].pName = "main";
std::vector<VkDynamicState> dynamic_state_descriptors; std::vector<VkDynamicState> dynamic_state_descriptors =
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_VIEWPORT); {
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_SCISSOR); VK_DYNAMIC_STATE_VIEWPORT,
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_LINE_WIDTH); VK_DYNAMIC_STATE_SCISSOR,
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS); VK_DYNAMIC_STATE_LINE_WIDTH,
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); VK_DYNAMIC_STATE_BLEND_CONSTANTS,
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS); VK_DYNAMIC_STATE_STENCIL_REFERENCE,
VK_DYNAMIC_STATE_DEPTH_BIAS
};
if (vk::get_current_renderer()->get_depth_bounds_support()) if (vk::get_current_renderer()->get_depth_bounds_support())
{ {
@ -502,6 +455,9 @@ namespace vk
VkPipelineColorBlendStateCreateInfo cs = properties.state.cs; VkPipelineColorBlendStateCreateInfo cs = properties.state.cs;
cs.pAttachments = properties.state.att_state; cs.pAttachments = properties.state.att_state;
VkPipelineTessellationStateCreateInfo ts = {};
ts.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
VkGraphicsPipelineCreateInfo info = {}; VkGraphicsPipelineCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
info.pVertexInputState = &vi; info.pVertexInputState = &vi;
@ -511,6 +467,7 @@ namespace vk
info.pMultisampleState = &ms; info.pMultisampleState = &ms;
info.pViewportState = &vp; info.pViewportState = &vp;
info.pDepthStencilState = &properties.state.ds; info.pDepthStencilState = &properties.state.ds;
info.pTessellationState = &ts;
info.stageCount = 2; info.stageCount = 2;
info.pStages = shader_stages; info.pStages = shader_stages;
info.pDynamicState = &dynamic_state_info; info.pDynamicState = &dynamic_state_info;

View file

@ -41,6 +41,7 @@ namespace vk
std::unordered_map<pipeline_key, std::unique_ptr<glsl::program>, key_hasher> m_program_cache; std::unordered_map<pipeline_key, std::unique_ptr<glsl::program>, key_hasher> m_program_cache;
std::unordered_map<u64, std::unique_ptr<glsl::shader>> m_fs_cache; std::unordered_map<u64, std::unique_ptr<glsl::shader>> m_fs_cache;
rsx::simple_array<VkDescriptorPoolSize> m_descriptor_pool_sizes;
vk::descriptor_pool m_descriptor_pool; vk::descriptor_pool m_descriptor_pool;
u32 m_vertex_instruction_start = 0; u32 m_vertex_instruction_start = 0;

Some files were not shown because too many files have changed in this diff Show more