mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 11:36:13 +00:00
Merge branch 'master' into vsh_fix
This commit is contained in:
commit
c3b469a9ab
170 changed files with 3889 additions and 1616 deletions
2
3rdparty/CMakeLists.txt
vendored
2
3rdparty/CMakeLists.txt
vendored
|
@ -44,7 +44,7 @@ add_subdirectory(libpng EXCLUDE_FROM_ALL)
|
|||
|
||||
# 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)
|
||||
target_link_libraries(pugixml INTERFACE PkgConfig::PUGIXML)
|
||||
else()
|
||||
|
|
2
3rdparty/libpng/libpng
vendored
2
3rdparty/libpng/libpng
vendored
|
@ -1 +1 @@
|
|||
Subproject commit f5e92d76973a7a53f517579bc95d61483bf108c0
|
||||
Subproject commit 51f5bd68b9b806d2c92b4318164d28b49357da31
|
2
3rdparty/pugixml
vendored
2
3rdparty/pugixml
vendored
|
@ -1 +1 @@
|
|||
Subproject commit db78afc2b7d8f043b4bc6b185635d949ea2ed2a8
|
||||
Subproject commit ee86beb30e4973f5feffe3ce63bfa4fbadf72f38
|
3
3rdparty/wolfssl/CMakeLists.txt
vendored
3
3rdparty/wolfssl/CMakeLists.txt
vendored
|
@ -22,6 +22,5 @@ else()
|
|||
|
||||
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
|
||||
|
||||
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP)
|
||||
target_compile_definitions(wolfssl PUBLIC FP_MAX_BITS=8192)
|
||||
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_NO_OPTIONS_H)
|
||||
endif()
|
||||
|
|
|
@ -21,7 +21,7 @@ using namespace std::literals::string_literals;
|
|||
#include <cwchar>
|
||||
#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
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
const std::string& fs::get_config_dir()
|
||||
const std::string& fs::get_config_dir([[maybe_unused]] bool get_config_subdirectory)
|
||||
{
|
||||
// Use magic static
|
||||
static const std::string s_dir = []
|
||||
|
@ -2103,6 +2103,14 @@ const std::string& fs::get_config_dir()
|
|||
return dir;
|
||||
}();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (get_config_subdirectory)
|
||||
{
|
||||
static const std::string subdir = s_dir + "config/";
|
||||
return subdir;
|
||||
}
|
||||
#endif
|
||||
|
||||
return s_dir;
|
||||
}
|
||||
|
||||
|
@ -2144,6 +2152,16 @@ const std::string& fs::get_cache_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()
|
||||
{
|
||||
static const std::string s_dir = []
|
||||
|
|
|
@ -599,12 +599,15 @@ namespace fs
|
|||
// Get executable containing directory
|
||||
std::string get_executable_dir();
|
||||
|
||||
// Get configuration directory
|
||||
const std::string& get_config_dir();
|
||||
// Get configuration directory. Set get_config_subdirectory to true to get the nested config dir on windows.
|
||||
const std::string& get_config_dir(bool get_config_subdirectory = false);
|
||||
|
||||
// Get common cache directory
|
||||
const std::string& get_cache_dir();
|
||||
|
||||
// Get common log directory
|
||||
const std::string& get_log_dir();
|
||||
|
||||
// Temporary directory
|
||||
const std::string& get_temp_dir();
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ enum class thread_state : u32
|
|||
aborting = 1, // The thread has been joined in the destructor or explicitly aborted
|
||||
errored = 2, // Set after the emergency_exit call
|
||||
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>
|
||||
|
@ -702,14 +703,17 @@ public:
|
|||
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
|
||||
thread::join(true);
|
||||
}
|
||||
|
||||
if (s == thread_state::destroying_context)
|
||||
{
|
||||
if constexpr (std::is_assignable_v<Context&, thread_state>)
|
||||
{
|
||||
static_cast<Context&>(*this) = thread_state::finished;
|
||||
static_cast<Context&>(*this) = thread_state::destroying_context;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,19 +125,15 @@ patch_engine::patch_engine()
|
|||
|
||||
std::string patch_engine::get_patch_config_path()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const std::string config_dir = fs::get_config_dir() + "config/";
|
||||
const std::string config_dir = fs::get_config_dir(true);
|
||||
const std::string patch_path = config_dir + "patch_config.yml";
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!fs::create_path(config_dir))
|
||||
{
|
||||
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
|
||||
return patch_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)
|
||||
{
|
||||
// applied_total may be non-empty, do not clear it
|
||||
|
||||
if (!m_map.contains(name))
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -14,6 +14,7 @@ enum class cheat_type : u8
|
|||
signed_16_cheat,
|
||||
signed_32_cheat,
|
||||
signed_64_cheat,
|
||||
float_32_cheat,
|
||||
max
|
||||
};
|
||||
|
||||
|
|
|
@ -49,12 +49,11 @@ std::string rXmlNode::GetName()
|
|||
return {};
|
||||
}
|
||||
|
||||
std::string rXmlNode::GetAttribute(const std::string& name)
|
||||
std::string rXmlNode::GetAttribute(std::string_view name)
|
||||
{
|
||||
if (handle)
|
||||
{
|
||||
const auto pred = [&name](const pugi::xml_attribute& attr) { return (name == attr.name()); };
|
||||
if (const pugi::xml_attribute attr = handle.find_attribute(pred))
|
||||
if (const pugi::xml_attribute attr = handle.attribute(name))
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@ struct rXmlNode
|
|||
std::shared_ptr<rXmlNode> GetChildren();
|
||||
std::shared_ptr<rXmlNode> GetNext();
|
||||
std::string GetName();
|
||||
std::string GetAttribute(const std::string& name);
|
||||
std::string GetAttribute(std::string_view name);
|
||||
std::string GetNodeContent();
|
||||
|
||||
pugi::xml_node handle{};
|
||||
|
@ -34,7 +34,7 @@ struct rXmlDocument
|
|||
rXmlDocument();
|
||||
rXmlDocument(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();
|
||||
|
||||
pugi::xml_document handle{};
|
||||
|
|
|
@ -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/git $<TARGET_FILE_DIR:rpcs3>/git
|
||||
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>"
|
||||
--verbose 0 "$<TARGET_FILE:rpcs3>")
|
||||
endif()
|
||||
|
|
|
@ -172,6 +172,7 @@ target_link_libraries(rpcs3_emu
|
|||
|
||||
# Cell
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Cell/ErrorCodes.cpp
|
||||
Cell/MFC.cpp
|
||||
Cell/PPUAnalyser.cpp
|
||||
Cell/PPUDisAsm.cpp
|
||||
|
@ -557,6 +558,7 @@ target_sources(rpcs3_emu PRIVATE
|
|||
RSX/Program/CgBinaryFragmentProgram.cpp
|
||||
RSX/Program/CgBinaryVertexProgram.cpp
|
||||
RSX/Program/FragmentProgramDecompiler.cpp
|
||||
RSX/Program/FragmentProgramRegister.cpp
|
||||
RSX/Program/GLSLCommon.cpp
|
||||
RSX/Program/ProgramStateCache.cpp
|
||||
RSX/Program/program_util.cpp
|
||||
|
@ -593,6 +595,7 @@ if(TARGET 3rdparty_vulkan)
|
|||
RSX/VK/VKAsyncScheduler.cpp
|
||||
RSX/VK/VKCommandStream.cpp
|
||||
RSX/VK/VKCommonDecompiler.cpp
|
||||
RSX/VK/VKCommonPipelineLayout.cpp
|
||||
RSX/VK/VKCompute.cpp
|
||||
RSX/VK/VKDMA.cpp
|
||||
RSX/VK/VKDraw.cpp
|
||||
|
|
91
rpcs3/Emu/Cell/ErrorCodes.cpp
Normal file
91
rpcs3/Emu/Cell/ErrorCodes.cpp
Normal 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;
|
||||
}
|
|
@ -270,35 +270,75 @@ void AtracXdecContext::exec(ppu_thread& ppu)
|
|||
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++)
|
||||
{
|
||||
cellAtracXdec.trace("Command counter: %llu, waiting for next command...", cmd_counter);
|
||||
|
||||
if (!skip_getting_command)
|
||||
for (;;)
|
||||
{
|
||||
lv2_obj::sleep(ppu);
|
||||
std::lock_guard lock{queue_mutex};
|
||||
savestate = atracxdec_state::initial;
|
||||
|
||||
while (cmd_queue.empty() && !ppu.is_stopped())
|
||||
{
|
||||
lv2_obj::sleep(ppu);
|
||||
queue_not_empty.wait(queue_mutex, 20000);
|
||||
}
|
||||
ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK);
|
||||
|
||||
if (ppu.is_stopped())
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return;
|
||||
}
|
||||
|
||||
cmd_queue.pop(cmd);
|
||||
|
||||
if (!run_thread)
|
||||
if (ppu.state & cpu_flag::again)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
|
||||
}
|
||||
|
||||
cmd_queue.pop(cmd);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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()));
|
||||
|
||||
switch (cmd.type)
|
||||
|
@ -327,8 +367,6 @@ void AtracXdecContext::exec(ppu_thread& ppu)
|
|||
}
|
||||
case AtracXdecCmdType::end_seq:
|
||||
{
|
||||
skip_getting_command = true;
|
||||
|
||||
// Block savestate creation during callbacks
|
||||
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;
|
||||
}
|
||||
|
||||
skip_getting_command = false;
|
||||
|
||||
// Doesn't do anything else
|
||||
notify_seq_done.cbFunc(ppu, notify_seq_done.cbArg);
|
||||
break;
|
||||
}
|
||||
case AtracXdecCmdType::decode_au:
|
||||
{
|
||||
skip_getting_command = true;
|
||||
|
||||
ensure(!!cmd.au_start_addr); // Not checked on LLE
|
||||
|
||||
cellAtracXdec.trace("Waiting for output to be consumed...");
|
||||
|
||||
lv2_obj::sleep(ppu);
|
||||
std::unique_lock output_mutex_lock{output_mutex};
|
||||
ensure(sys_mutex_lock(ppu, output_mutex, 0) == CELL_OK);
|
||||
|
||||
while (output_locked && !ppu.is_stopped())
|
||||
if (ppu.state & cpu_flag::again)
|
||||
{
|
||||
lv2_obj::sleep(ppu);
|
||||
output_consumed.wait(output_mutex, 20000);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!run_thread)
|
||||
{
|
||||
ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK);
|
||||
ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK);
|
||||
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;
|
||||
|
||||
|
@ -578,14 +634,12 @@ void AtracXdecContext::exec(ppu_thread& ppu)
|
|||
return;
|
||||
}
|
||||
|
||||
skip_getting_command = false;
|
||||
|
||||
// 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
|
||||
notify_au_done.cbFunc(ppu, cmd.pcm_handle, notify_au_done.cbArg);
|
||||
|
||||
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;
|
||||
|
||||
|
@ -614,29 +668,46 @@ void AtracXdecContext::exec(ppu_thread& ppu)
|
|||
template <AtracXdecCmdType type>
|
||||
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)
|
||||
{
|
||||
// Close command is only sent if the queue is empty on LLE
|
||||
if (!cmd_queue.empty())
|
||||
{
|
||||
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_queue.full())
|
||||
{
|
||||
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
|
||||
return CELL_ADEC_ERROR_ATX_BUSY;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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,
|
||||
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 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);
|
||||
|
@ -725,29 +819,32 @@ error_code _CellAdecCoreOpClose_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext
|
|||
return {};
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
cellAtracXdec.notice("_CellAdecCoreOpClose_atracx(handle=*0x%x)", handle);
|
||||
|
||||
ensure(!!handle); // Not checked on LLE
|
||||
|
||||
ensure(sys_mutex_lock(ppu, handle->run_thread_mutex, 0) == CELL_OK);
|
||||
handle->run_thread = false;
|
||||
ensure(sys_mutex_unlock(ppu, handle->run_thread_mutex) == CELL_OK);
|
||||
|
||||
handle->send_command<AtracXdecCmdType::close>(ppu);
|
||||
|
||||
{
|
||||
std::lock_guard lock{handle->output_mutex};
|
||||
handle->output_locked = false;
|
||||
}
|
||||
ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK);
|
||||
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)
|
||||
{
|
||||
// Other thread already closed the decoder
|
||||
return CELL_ADEC_ERROR_FATAL;
|
||||
}
|
||||
error_code ret = sys_cond_destroy(ppu, handle->queue_not_empty);
|
||||
ret = ret ? ret : sys_cond_destroy(ppu, handle->run_thread_cond);
|
||||
ret = ret ? ret : sys_cond_destroy(ppu, handle->output_consumed);
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
cellAtracXdec.trace("_CellAdecCoreOpReleasePcm_atracx(handle=*0x%x, pcmHandle=%d, outBuffer=*0x%x)", handle, pcmHandle, outBuffer);
|
||||
|
||||
ensure(!!handle); // Not checked on LLE
|
||||
|
||||
std::lock_guard lock{handle->output_mutex};
|
||||
handle->output_locked = false;
|
||||
handle->output_consumed.notify_one();
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ constexpr int averror_eof = AVERROR_EOF; // Workaround for old-style-cast error
|
|||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "Utilities/cond.h"
|
||||
#include "cellPamf.h"
|
||||
#include "cellAdec.h"
|
||||
|
||||
|
@ -215,16 +214,28 @@ struct AtracXdecDecoder
|
|||
|
||||
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
|
||||
{
|
||||
be_t<u64> thread_id; // sys_ppu_thread_t
|
||||
|
||||
shared_mutex queue_mutex; // sys_mutex_t
|
||||
cond_variable queue_not_empty; // sys_cond_t
|
||||
be_t<u32> queue_mutex; // sys_mutex_t
|
||||
be_t<u32> queue_not_empty; // sys_cond_t
|
||||
AdecCmdQueue<AtracXdecCmd> cmd_queue;
|
||||
|
||||
shared_mutex output_mutex; // sys_mutex_t
|
||||
cond_variable output_consumed; // sys_cond_t
|
||||
be_t<u32> output_mutex; // sys_mutex_t
|
||||
be_t<u32> output_consumed; // sys_cond_t
|
||||
be_t<u32> output_locked = false;
|
||||
|
||||
be_t<u32> run_thread_mutex; // sys_mutex_t
|
||||
|
@ -239,10 +250,10 @@ struct AtracXdecContext
|
|||
const vm::bptr<u8> work_mem;
|
||||
|
||||
// HLE exclusive
|
||||
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
|
||||
b8 skip_getting_command = false; // For savestates; skips getting a new command from the queue
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
u8 spurs_stuff[58]; // 120 bytes on LLE, pointers to CellSpurs, CellSpursTaskset, etc.
|
||||
|
||||
|
|
|
@ -148,9 +148,44 @@ void camera_context::save(utils::serial& ar)
|
|||
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);
|
||||
|
||||
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)
|
||||
|
@ -435,7 +470,6 @@ error_code cellCameraInit()
|
|||
g_camera.attr[CELL_CAMERA_USBLOAD] = { 4 };
|
||||
break;
|
||||
}
|
||||
|
||||
case fake_camera_type::eyetoy2:
|
||||
{
|
||||
g_camera.attr[CELL_CAMERA_SATURATION] = { 64 };
|
||||
|
@ -455,7 +489,6 @@ error_code cellCameraInit()
|
|||
g_camera.attr[CELL_CAMERA_AGCHIGH] = { 64 };
|
||||
break;
|
||||
}
|
||||
|
||||
case fake_camera_type::uvc1_1:
|
||||
{
|
||||
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
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
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.
|
||||
|
||||
g_camera.is_attached = true;
|
||||
g_camera.is_attached = g_cfg.io.camera != camera_handler::null;
|
||||
g_camera.init = 1;
|
||||
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
|
||||
if (!is_attached)
|
||||
{
|
||||
g_camera.send_attach_state(true);
|
||||
is_attached = g_camera.is_attached;
|
||||
g_camera.is_attached = is_attached = true;
|
||||
g_camera.is_attached_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1606,9 +1639,15 @@ void camera_context::operator()()
|
|||
{
|
||||
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;
|
||||
|
||||
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
|
||||
continue;
|
||||
|
@ -1783,6 +1822,7 @@ void camera_context::reset_state()
|
|||
read_mode = CELL_CAMERA_READ_FUNCCALL;
|
||||
is_streaming = false;
|
||||
is_attached = false;
|
||||
is_attached_dirty = false;
|
||||
is_open = false;
|
||||
info.framerate = 0;
|
||||
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
|
||||
is_attached = attached;
|
||||
is_attached_dirty = false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
{
|
||||
std::lock_guard lock_data_map(mutex_notify_data_map);
|
||||
|
||||
notify_data_map[key] = { source, flag };
|
||||
}
|
||||
|
||||
// send ATTACH event - HACKY
|
||||
send_attach_state(is_attached);
|
||||
is_attached_dirty = true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
send_attach_state(false);
|
||||
is_attached = false;
|
||||
is_attached_dirty = true;
|
||||
}
|
||||
if (handler)
|
||||
{
|
||||
|
@ -1938,7 +1978,8 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state
|
|||
if (!is_attached)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -427,6 +427,7 @@ public:
|
|||
atomic_t<u8> read_mode{CELL_CAMERA_READ_FUNCCALL};
|
||||
atomic_t<bool> is_streaming{false};
|
||||
atomic_t<bool> is_attached{false};
|
||||
atomic_t<bool> is_attached_dirty{false};
|
||||
atomic_t<bool> is_open{false};
|
||||
|
||||
CellCameraInfoEx info{};
|
||||
|
@ -471,6 +472,13 @@ using camera_thread = named_thread<camera_context>;
|
|||
/// Shared data between cellGem and cellCamera
|
||||
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<s32> width{640};
|
||||
atomic_t<s32> height{480};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -196,7 +196,7 @@ bool cellPad_NotifyStateChange(usz index, u64 /*state*/, bool locked, bool is_bl
|
|||
return true;
|
||||
}
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pads = handler->GetPads();
|
||||
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.reported_info = {};
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
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())
|
||||
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& 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)
|
||||
{
|
||||
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 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())
|
||||
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& pad = pads[port_no];
|
||||
|
||||
|
@ -740,7 +740,7 @@ error_code cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
|
|||
if (!info)
|
||||
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();
|
||||
|
||||
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())
|
||||
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& 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())
|
||||
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& 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())
|
||||
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& pad = pads[port_no];
|
||||
|
||||
|
@ -923,7 +923,7 @@ error_code cellPadGetInfo(vm::ptr<CellPadInfo> info)
|
|||
|
||||
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();
|
||||
info->max_connect = config.max_connect;
|
||||
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));
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const PadInfo& rinfo = handler->GetInfo();
|
||||
info->max_connect = config.get_max_connect(); // Here it is forcibly clamped
|
||||
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())
|
||||
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& pad = pads[port_no];
|
||||
|
||||
|
@ -1074,7 +1074,7 @@ error_code cellPadInfoPressMode(u32 port_no)
|
|||
if (port_no >= config.get_max_connect())
|
||||
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& pad = pads[port_no];
|
||||
|
||||
|
@ -1101,7 +1101,7 @@ error_code cellPadInfoSensorMode(u32 port_no)
|
|||
if (port_no >= config.get_max_connect())
|
||||
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& pad = pads[port_no];
|
||||
|
||||
|
@ -1129,7 +1129,7 @@ error_code cellPadSetPressMode(u32 port_no, u32 mode)
|
|||
if (port_no >= CELL_PAD_MAX_PORT_NUM)
|
||||
return CELL_OK;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pads = handler->GetPads();
|
||||
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)
|
||||
return CELL_OK;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
|
@ -1190,7 +1190,7 @@ error_code cellPadLddRegisterController()
|
|||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
|
||||
const s32 handle = handler->AddLddPad();
|
||||
|
||||
|
@ -1215,7 +1215,7 @@ error_code cellPadLddDataInsert(s32 handle, vm::ptr<CellPadData> data)
|
|||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
auto& pads = handler->GetPads();
|
||||
|
||||
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)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
auto& pads = handler->GetPads();
|
||||
|
||||
if (handle < 0 || static_cast<u32>(handle) >= pads.size())
|
||||
|
@ -1264,7 +1264,7 @@ error_code cellPadLddUnregisterController(s32 handle)
|
|||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
if (handle < 0 || static_cast<u32>(handle) >= pads.size())
|
||||
|
|
|
@ -221,7 +221,7 @@ error_code sys_prx_get_module_info(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<s
|
|||
opt->info = info;
|
||||
|
||||
// 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)
|
||||
|
|
|
@ -96,6 +96,7 @@ struct ppu_module : public Type
|
|||
std::vector<ppu_segment> segs{};
|
||||
std::vector<ppu_segment> secs{};
|
||||
std::vector<ppu_function> funcs{};
|
||||
std::vector<u32> applied_patches;
|
||||
std::deque<std::shared_ptr<void>> allocations;
|
||||
std::map<u32, u32> addr_to_seg_index;
|
||||
|
||||
|
@ -185,7 +186,6 @@ struct main_ppu_module : public ppu_module<T>
|
|||
{
|
||||
u32 elf_entry{};
|
||||
u32 seg0_code_end{};
|
||||
std::vector<u32> applied_patches;
|
||||
|
||||
// Disable inherited savestate ordering
|
||||
void save(utils::serial&) = delete;
|
||||
|
|
|
@ -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_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_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);
|
||||
}
|
||||
|
||||
prx->applied_patches = applied;
|
||||
prx->analyse(toc, 0, end, applied, exported_funcs);
|
||||
|
||||
if (!ar && !virtual_load)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const be_t<u64> forced_upd = 3;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
if (s == thread_state::finished)
|
||||
if (s == thread_state::destroying_context && !m_destroyed.exchange(true))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
bool lv2_config_service_listener::check_service(const lv2_config_service& service) const
|
||||
{
|
||||
|
|
|
@ -161,6 +161,8 @@ public:
|
|||
|
||||
return null_ptr;
|
||||
}
|
||||
|
||||
~lv2_config() noexcept;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -276,7 +278,7 @@ public:
|
|||
|
||||
// Utilities
|
||||
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; }
|
||||
};
|
||||
|
||||
|
@ -342,7 +344,7 @@ public:
|
|||
|
||||
// Utilities
|
||||
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++;
|
||||
}
|
||||
|
||||
atomic_t<bool> m_destroyed = false;
|
||||
|
||||
friend class lv2_config;
|
||||
|
||||
public:
|
||||
const u32 id;
|
||||
|
||||
|
@ -391,8 +397,7 @@ public:
|
|||
|
||||
// Destructor
|
||||
lv2_config_service_event& operator=(thread_state s) noexcept;
|
||||
|
||||
~lv2_config_service_event() noexcept = default;
|
||||
~lv2_config_service_event() noexcept;
|
||||
|
||||
// Notify queue that this event exists
|
||||
bool notify() const;
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
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();
|
||||
|
||||
std::tie(dest.source, dest.data1, dest.data2, dest.data3) = event;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
|
|
@ -178,7 +178,7 @@ void lv2_socket::queue_wake(ppu_thread* ppu)
|
|||
|
||||
lv2_socket& lv2_socket::operator=(thread_state s) noexcept
|
||||
{
|
||||
if (s == thread_state::finished)
|
||||
if (s == thread_state::destroying_context)
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ struct lv2_overlay final : ppu_module<lv2_obj>
|
|||
|
||||
u32 entry{};
|
||||
u32 seg0_code_end{};
|
||||
std::vector<u32> applied_patches;
|
||||
|
||||
lv2_overlay() = default;
|
||||
lv2_overlay(utils::serial&){}
|
||||
|
|
|
@ -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);
|
||||
|
||||
if (!Emu.GetTitleID().length())
|
||||
if (Emu.GetTitleID().empty())
|
||||
{
|
||||
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();
|
||||
|
||||
// Make sure we keep the game window opened
|
||||
Emu.SetContinuousMode(true);
|
||||
Emu.Kill(false);
|
||||
});
|
||||
|
||||
|
|
|
@ -1034,7 +1034,7 @@ error_code _sys_prx_get_module_info(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<
|
|||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
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)
|
||||
|
|
|
@ -76,10 +76,22 @@ struct sys_prx_module_info_t
|
|||
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
|
||||
{
|
||||
be_t<u64> size; // 0x10
|
||||
vm::bptr<sys_prx_module_info_t> info;
|
||||
union
|
||||
{
|
||||
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
|
||||
|
@ -192,6 +204,9 @@ struct lv2_prx final : ppu_module<lv2_obj>
|
|||
u8 module_info_version[2]{};
|
||||
be_t<u16> module_info_attributes{};
|
||||
|
||||
u32 imports_start = umax;
|
||||
u32 imports_end = 0;
|
||||
|
||||
u32 exports_start = umax;
|
||||
u32 exports_end = 0;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ void cfg_ipc::load()
|
|||
void cfg_ipc::save() const
|
||||
{
|
||||
#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))
|
||||
{
|
||||
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()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return fs::get_config_dir() + "config/ipc.yml";
|
||||
#else
|
||||
return fs::get_config_dir() + "ipc.yml";
|
||||
#endif
|
||||
return fs::get_config_dir(true) + "ipc.yml";
|
||||
}
|
||||
|
||||
bool cfg_ipc::get_server_enabled() const
|
||||
|
|
|
@ -805,8 +805,8 @@ public:
|
|||
{
|
||||
if (ptr)
|
||||
{
|
||||
constexpr thread_state finished{3};
|
||||
*static_cast<Get*>(ptr.get()) = finished;
|
||||
constexpr thread_state destroying_context{7};
|
||||
*static_cast<Get*>(ptr.get()) = destroying_context;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -837,8 +837,8 @@ public:
|
|||
{
|
||||
if (ptr)
|
||||
{
|
||||
constexpr thread_state finished{3};
|
||||
*static_cast<Get*>(ptr.get()) = finished;
|
||||
constexpr thread_state destroying_context{7};
|
||||
*static_cast<Get*>(ptr.get()) = destroying_context;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pads = handler->GetPads();
|
||||
ensure(pads.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];
|
||||
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)
|
||||
return;
|
||||
|
|
|
@ -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);
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pad = ::at32(handler->GetPads(), m_controller_index);
|
||||
|
||||
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);
|
||||
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)
|
||||
return;
|
||||
|
|
|
@ -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);
|
||||
const auto gamepad_handler = pad::get_current_handler();
|
||||
const auto gamepad_handler = pad::get_pad_thread();
|
||||
const auto& pads = gamepad_handler->GetPads();
|
||||
const auto& pad = ::at32(pads, m_controller_index);
|
||||
if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)
|
||||
|
|
|
@ -227,7 +227,7 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint,
|
|||
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)
|
||||
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);
|
||||
const auto gamepad_handler = pad::get_current_handler();
|
||||
const auto gamepad_handler = pad::get_pad_thread();
|
||||
const auto& pads = gamepad_handler->GetPads();
|
||||
const auto& pad = ::at32(pads, m_controller_index);
|
||||
if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)
|
||||
|
|
|
@ -31,12 +31,12 @@ void MouseHandlerBase::save(utils::serial& ar)
|
|||
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();
|
||||
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;
|
||||
return true;
|
||||
|
|
|
@ -127,7 +127,7 @@ protected:
|
|||
std::vector<Mouse> m_mice;
|
||||
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:
|
||||
shared_mutex mutex;
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
|
||||
cfg->pressure_intensity_button.def = "";
|
||||
cfg->analog_limiter_button.def = "";
|
||||
cfg->orientation_reset_button.def = "";
|
||||
|
||||
// Apply defaults
|
||||
cfg->from_default();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "PadHandler.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/Cell/timers.hpp"
|
||||
#include "Input/pad_thread.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;
|
||||
}
|
||||
|
||||
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::down], CELL_PAD_CTRL_DOWN);
|
||||
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);
|
||||
}
|
||||
|
||||
if (b_has_orientation)
|
||||
{
|
||||
mapping[button::orientation_reset_button] = FindKeyCodes<u32, u32>(button_list, cfg->orientation_reset_button);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
@ -739,6 +751,8 @@ void PadHandlerBase::process()
|
|||
if (!device || !pad)
|
||||
continue;
|
||||
|
||||
pad->move_data.orientation_enabled = b_has_orientation && device->config && device->config->orientation_enabled.get();
|
||||
|
||||
const connection status = update_connection(device);
|
||||
|
||||
switch (status)
|
||||
|
@ -754,6 +768,11 @@ void PadHandlerBase::process()
|
|||
|
||||
last_connection_status[i] = true;
|
||||
connected_devices++;
|
||||
|
||||
if (b_has_orientation)
|
||||
{
|
||||
device->reset_orientation();
|
||||
}
|
||||
}
|
||||
|
||||
if (status == connection::no_data)
|
||||
|
@ -790,6 +809,11 @@ void PadHandlerBase::process()
|
|||
|
||||
last_connection_status[i] = false;
|
||||
connected_devices--;
|
||||
|
||||
if (b_has_orientation)
|
||||
{
|
||||
device->reset_orientation();
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -797,6 +821,142 @@ void PadHandlerBase::process()
|
|||
|
||||
get_mapping(m_bindings[i]);
|
||||
get_extended_info(m_bindings[i]);
|
||||
get_orientation(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];
|
||||
}
|
||||
|
|
|
@ -5,6 +5,15 @@
|
|||
#include "pad_config_types.h"
|
||||
#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 <functional>
|
||||
#include <string>
|
||||
|
@ -38,6 +47,12 @@ public:
|
|||
};
|
||||
color color_override{};
|
||||
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
|
||||
|
@ -125,6 +140,7 @@ protected:
|
|||
|
||||
pressure_intensity_button,
|
||||
analog_limiter_button,
|
||||
orientation_reset_button,
|
||||
|
||||
button_count
|
||||
};
|
||||
|
@ -153,6 +169,7 @@ protected:
|
|||
bool b_has_config = false;
|
||||
bool b_has_pressure_intensity_button = true;
|
||||
bool b_has_analog_limiter_button = true;
|
||||
bool b_has_orientation = false;
|
||||
|
||||
std::array<cfg_pad, MAX_GAMEPADS> m_pad_configs;
|
||||
std::vector<pad_ensemble> m_bindings;
|
||||
|
@ -301,6 +318,7 @@ public:
|
|||
bool has_battery_led() const { return b_has_battery_led; }
|
||||
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_orientation() const { return b_has_orientation; }
|
||||
|
||||
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;
|
||||
|
@ -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 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:
|
||||
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; }
|
||||
|
@ -336,10 +366,15 @@ private:
|
|||
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 {}; }
|
||||
|
||||
void get_orientation(const pad_ensemble& binding) const;
|
||||
|
||||
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 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 init_configs();
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
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)
|
||||
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);
|
||||
const auto gamepad_handler = pad::get_current_handler();
|
||||
const auto gamepad_handler = pad::get_pad_thread();
|
||||
const auto& pads = gamepad_handler->GetPads();
|
||||
const auto& pad = ::at32(pads, m_controller_index);
|
||||
if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)
|
||||
|
|
|
@ -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;
|
||||
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)
|
||||
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);
|
||||
const auto gamepad_handler = pad::get_current_handler();
|
||||
const auto gamepad_handler = pad::get_pad_thread();
|
||||
const auto& pads = gamepad_handler->GetPads();
|
||||
const auto& pad = ::at32(pads, m_controller_index);
|
||||
if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)
|
||||
|
|
|
@ -151,7 +151,7 @@ void usb_device_turntable::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpo
|
|||
// All other bufs are always 0x00
|
||||
|
||||
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& pad = ::at32(pads, m_controller_index);
|
||||
|
||||
|
@ -159,7 +159,7 @@ void usb_device_turntable::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpo
|
|||
return;
|
||||
|
||||
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)
|
||||
return;
|
||||
|
|
|
@ -8,11 +8,7 @@ cfg_camera g_cfg_camera;
|
|||
|
||||
cfg_camera::cfg_camera()
|
||||
: cfg::node()
|
||||
#ifdef _WIN32
|
||||
, path(fs::get_config_dir() + "config/camera.yml")
|
||||
#else
|
||||
, path(fs::get_config_dir() + "camera.yml")
|
||||
#endif
|
||||
, path(fs::get_config_dir(true) + "camera.yml")
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ public:
|
|||
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)
|
||||
return;
|
||||
|
@ -97,19 +97,25 @@ public:
|
|||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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 u32 offset = pad_button_offset(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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
bool abort = false;
|
||||
|
||||
const auto& btns = find_button(offset, keycode);
|
||||
if (btns.empty())
|
||||
{
|
||||
|
@ -180,24 +192,26 @@ protected:
|
|||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y:
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X:
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
return abort;
|
||||
}
|
||||
|
||||
for (const auto& btn : btns)
|
||||
{
|
||||
if (btn && func)
|
||||
{
|
||||
func(btn->btn_id(), value, pressed);
|
||||
func(btn->btn_id(), btn->get(), value, pressed, abort);
|
||||
if (abort) break;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutex.unlock();
|
||||
return abort;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -221,7 +235,7 @@ struct emulated_pads_config : cfg::node
|
|||
m_mutex.lock();
|
||||
|
||||
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);
|
||||
|
||||
from_default();
|
||||
|
@ -258,7 +272,7 @@ struct emulated_pads_config : cfg::node
|
|||
{
|
||||
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);
|
||||
|
||||
if (!fs::create_path(fs::get_parent_dir(cfg_name)))
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <array>
|
||||
|
||||
enum class gem_btn
|
||||
enum class gem_btn : u32
|
||||
{
|
||||
start,
|
||||
select,
|
||||
|
@ -17,6 +17,18 @@ enum class gem_btn
|
|||
x_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
|
||||
};
|
||||
|
||||
|
@ -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") {};
|
||||
};
|
||||
|
||||
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>
|
||||
{
|
||||
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_fake_gems g_cfg_gem_fake;
|
||||
extern cfg_mouse_gems g_cfg_gem_mouse;
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
#include "Utilities/File.h"
|
||||
|
||||
mouse_config::mouse_config()
|
||||
#ifdef _WIN32
|
||||
: cfg_name(fs::get_config_dir() + "config/config_mouse.yml")
|
||||
#else
|
||||
: cfg_name(fs::get_config_dir() + "config_mouse.yml")
|
||||
#endif
|
||||
: cfg_name(fs::get_config_dir(true) + "config_mouse.yml")
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,20 @@ std::string cfg_pad::get_buttons(std::vector<std::string> 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)
|
||||
{
|
||||
input_log.notice("Loading pad config (title_id='%s', config_file='%s', strict=%d)", title_id, config_file, strict);
|
||||
|
|
|
@ -28,6 +28,9 @@ struct cfg_pad final : cfg::node
|
|||
static std::vector<std::string> get_buttons(const std::string& str);
|
||||
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_down{ this, "Left Stick Down", "" };
|
||||
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_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::uint<0, 100> pressure_intensity{ this, "Pressure Intensity Percent", 50 };
|
||||
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::_bool player_led_enabled{ this, "Player LED enabled", true };
|
||||
|
||||
cfg::_bool enable_vibration_motor_large{ this, "Enable Large Vibration Motor", true };
|
||||
cfg::_bool enable_vibration_motor_small{ this, "Enable Small Vibration Motor", true };
|
||||
cfg::uint<0, 200> multiplier_vibration_motor_large{ this, "Large Vibration Motor Multiplier", 100 };
|
||||
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::_enum<mouse_movement_mode> mouse_move_mode{ this, "Mouse Movement Mode", mouse_movement_mode::relative };
|
||||
|
|
|
@ -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_x: return "Right Stick X-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_2: return "Mouse Button 2";
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -365,7 +365,8 @@ constexpr u32 special_button_offset = 666; // Must not conflict with other CELL
|
|||
enum special_button_value
|
||||
{
|
||||
pressure_intensity,
|
||||
analog_limiter
|
||||
analog_limiter,
|
||||
orientation_reset
|
||||
};
|
||||
|
||||
struct Button
|
||||
|
@ -470,8 +471,10 @@ struct ps_move_data
|
|||
bool calibration_succeeded = 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_y = 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_z = 0.0f;
|
||||
s16 temperature = 0;
|
||||
|
||||
void reset_sensors();
|
||||
};
|
||||
|
||||
struct Pad
|
||||
|
@ -512,6 +517,9 @@ struct Pad
|
|||
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);
|
||||
|
||||
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 ?
|
||||
u8 m_cable_state{0};
|
||||
|
||||
|
|
|
@ -8,13 +8,7 @@ cfg_rb3drums g_cfg_rb3drums;
|
|||
|
||||
cfg_rb3drums::cfg_rb3drums()
|
||||
: cfg::node()
|
||||
#ifdef _WIN32
|
||||
,
|
||||
path(fs::get_config_dir() + "config/rb3drums.yml")
|
||||
#else
|
||||
,
|
||||
path(fs::get_config_dir() + "rb3drums.yml")
|
||||
#endif
|
||||
, path(fs::get_config_dir(true) + "rb3drums.yml")
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,7 @@ cfg_recording g_cfg_recording;
|
|||
|
||||
cfg_recording::cfg_recording()
|
||||
: cfg::node()
|
||||
#ifdef _WIN32
|
||||
, path(fs::get_config_dir() + "config/recording.yml")
|
||||
#else
|
||||
, path(fs::get_config_dir() + "recording.yml")
|
||||
#endif
|
||||
, path(fs::get_config_dir(true) + "recording.yml")
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ void usb_device_usio::save_backup()
|
|||
void usb_device_usio::translate_input_taiko()
|
||||
{
|
||||
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);
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -273,7 +273,7 @@ void usb_device_usio::translate_input_taiko()
|
|||
void usb_device_usio::translate_input_tekken()
|
||||
{
|
||||
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);
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -59,11 +59,7 @@ namespace np
|
|||
{
|
||||
std::string get_players_history_path()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return fs::get_config_dir() + "config/players_history.yml";
|
||||
#else
|
||||
return fs::get_config_dir() + "players_history.yml";
|
||||
#endif
|
||||
return fs::get_config_dir(true) + "players_history.yml";
|
||||
}
|
||||
|
||||
std::map<std::string, player_history> load_players_history()
|
||||
|
@ -1440,7 +1436,7 @@ namespace np
|
|||
void np_handler::save_players_history()
|
||||
{
|
||||
#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))
|
||||
{
|
||||
nph_log.error("Could not create path: %s", path_to_cfg);
|
||||
|
|
|
@ -34,7 +34,7 @@ void cfg_rpcn::load()
|
|||
void cfg_rpcn::save() const
|
||||
{
|
||||
#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))
|
||||
{
|
||||
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()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return fs::get_config_dir() + "config/rpcn.yml";
|
||||
#else
|
||||
return fs::get_config_dir() + "rpcn.yml";
|
||||
#endif
|
||||
return fs::get_config_dir(true) + "rpcn.yml";
|
||||
}
|
||||
|
||||
std::string cfg_rpcn::generate_npid()
|
||||
|
|
|
@ -24,7 +24,7 @@ void cfg_upnp::load()
|
|||
void cfg_upnp::save() const
|
||||
{
|
||||
#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))
|
||||
{
|
||||
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()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return fs::get_config_dir() + "config/upnp.yml";
|
||||
#else
|
||||
return fs::get_config_dir() + "upnp.yml";
|
||||
#endif
|
||||
return fs::get_config_dir(true) + "upnp.yml";
|
||||
}
|
||||
|
|
|
@ -427,5 +427,17 @@ namespace rsx
|
|||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,7 +18,21 @@ namespace rsx
|
|||
case surface_target::surfaces_a_b_c: return{ 0, 1, 2 };
|
||||
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)
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace rsx
|
|||
namespace utility
|
||||
{
|
||||
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_packed_pitch(surface_color_format format, u32 width);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -745,8 +745,9 @@ namespace rsx
|
|||
// Temp indirection table. Used to track "running" updates.
|
||||
rsx::simple_array<u32> instancing_indirection_table;
|
||||
// indirection table size
|
||||
const auto reloc_table = prog.has_indexed_constants ? decltype(prog.constant_ids){} : prog.constant_ids;
|
||||
const auto redirection_table_size = prog.has_indexed_constants ? 468u : ::size32(prog.constant_ids);
|
||||
const auto full_reupload = !prog || prog->has_indexed_constants;
|
||||
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);
|
||||
|
||||
// Temp constants data
|
||||
|
@ -787,9 +788,9 @@ namespace rsx
|
|||
continue;
|
||||
}
|
||||
|
||||
const int translated_offset = prog.has_indexed_constants
|
||||
const int translated_offset = full_reupload
|
||||
? 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)
|
||||
{
|
||||
|
@ -809,14 +810,14 @@ namespace rsx
|
|||
continue;
|
||||
}
|
||||
|
||||
ensure(!prog.has_indexed_constants);
|
||||
ensure(!full_reupload);
|
||||
|
||||
// Sparse update. Update records individually instead of bulk
|
||||
// FIXME: Range batching optimization
|
||||
const auto load_end = instance_config.patch_load_offset + instance_config.patch_load_count;
|
||||
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)
|
||||
{
|
||||
// Reading outside "hot" range.
|
||||
|
|
|
@ -105,6 +105,6 @@ namespace rsx
|
|||
|
||||
// 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
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ u64 GLGSRender::get_cycles()
|
|||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
GLGSRender::~GLGSRender()
|
||||
{
|
||||
if (m_frame)
|
||||
{
|
||||
m_frame->reset();
|
||||
}
|
||||
}
|
||||
|
||||
extern CellGcmContextData current_context;
|
||||
|
||||
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
|
||||
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::vertex_state_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)
|
||||
|
@ -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)
|
||||
{
|
||||
if (!m_vertex_prog)
|
||||
if (!m_program || !m_vertex_prog)
|
||||
{
|
||||
// Shouldn't be reachable, but handle it correctly anyway
|
||||
m_graphics_state |= rsx::pipeline_state::transform_constants_dirty;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_vertex_prog->overlaps_constants_range(index, count))
|
||||
{
|
||||
// Nothing meaningful to us
|
||||
return;
|
||||
}
|
||||
|
||||
std::pair<u32, u32> data_range {};
|
||||
void* data_source = nullptr;
|
||||
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_source = ®S(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 byte_count = count * 16;
|
||||
|
|
|
@ -159,6 +159,7 @@ public:
|
|||
|
||||
GLGSRender(utils::serial* ar) noexcept;
|
||||
GLGSRender() noexcept : GLGSRender(nullptr) {}
|
||||
virtual ~GLGSRender();
|
||||
|
||||
private:
|
||||
|
||||
|
@ -205,6 +206,9 @@ public:
|
|||
// GRAPH backend
|
||||
void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override;
|
||||
|
||||
// Misc
|
||||
bool is_current_program_interpreted() const override;
|
||||
|
||||
protected:
|
||||
void clear_surface(u32 arg) override;
|
||||
void begin() override;
|
||||
|
|
|
@ -347,7 +347,7 @@ namespace gl
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
glsl::program* get(const interpreter::program_metadata& fp_metadata);
|
||||
bool is_interpreter(const glsl::program* program);
|
||||
bool is_interpreter(const glsl::program* program) const;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ public:
|
|||
virtual ~GSFrameBase() = default;
|
||||
|
||||
virtual void close() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual bool shown() = 0;
|
||||
virtual void hide() = 0;
|
||||
virtual void show() = 0;
|
||||
|
|
|
@ -18,7 +18,7 @@ GSRender::~GSRender()
|
|||
{
|
||||
m_context = nullptr;
|
||||
|
||||
if (m_frame)
|
||||
if (m_frame && !m_continuous_mode)
|
||||
{
|
||||
m_frame->close();
|
||||
}
|
||||
|
@ -39,7 +39,10 @@ void GSRender::on_exit()
|
|||
|
||||
if (m_frame)
|
||||
{
|
||||
m_frame->hide();
|
||||
if (!m_continuous_mode)
|
||||
{
|
||||
m_frame->hide();
|
||||
}
|
||||
m_frame->delete_context(m_context);
|
||||
m_context = nullptr;
|
||||
}
|
||||
|
|
|
@ -21,12 +21,15 @@ class GSRender : public rsx::thread
|
|||
protected:
|
||||
GSFrameBase* m_frame;
|
||||
draw_context_t m_context = nullptr;
|
||||
bool m_continuous_mode = false;
|
||||
|
||||
public:
|
||||
~GSRender() override;
|
||||
|
||||
GSRender(utils::serial* ar) noexcept;
|
||||
|
||||
void set_continuous_mode(bool continuous_mode) { m_continuous_mode = continuous_mode; }
|
||||
|
||||
void on_init_thread() override;
|
||||
void on_exit() override;
|
||||
|
||||
|
|
|
@ -114,6 +114,8 @@ namespace rsx
|
|||
|
||||
Emu.CallFromMainThread([]()
|
||||
{
|
||||
// Make sure we keep the game window opened
|
||||
Emu.SetContinuousMode(true);
|
||||
Emu.Restart(false);
|
||||
});
|
||||
return page_navigation::exit;
|
||||
|
|
|
@ -26,6 +26,9 @@ namespace rsx
|
|||
if (!suspend_mode)
|
||||
{
|
||||
Emu.after_kill_callback = []() { Emu.Restart(); };
|
||||
|
||||
// Make sure we keep the game window opened
|
||||
Emu.SetContinuousMode(true);
|
||||
}
|
||||
Emu.Kill(false, true);
|
||||
});
|
||||
|
|
|
@ -265,7 +265,6 @@ namespace rsx
|
|||
fade_animation.end = color4f(1.f);
|
||||
fade_animation.active = true;
|
||||
|
||||
this->on_close = std::move(on_close);
|
||||
visible = true;
|
||||
|
||||
const auto notify = std::make_shared<atomic_t<u32>>(0);
|
||||
|
|
|
@ -193,7 +193,7 @@ namespace rsx
|
|||
|
||||
// Get gamepad input
|
||||
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 bool ignore_gamepad_input = (!rinfo.now_connect || !input::g_pads_intercepted);
|
||||
|
|
|
@ -22,6 +22,15 @@ namespace rsx
|
|||
|
||||
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)
|
||||
: m_size(size)
|
||||
, 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 + ";");
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
if (dst.opcode == RSX_FP_OPCODE_MOV &&
|
||||
|
@ -754,14 +762,26 @@ std::string FragmentProgramDecompiler::BuildCode()
|
|||
const std::string init_value = float4_type + "(0.)";
|
||||
std::array<std::string, 4> output_register_names;
|
||||
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
|
||||
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!!
|
||||
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)
|
||||
|
@ -775,28 +795,49 @@ std::string FragmentProgramDecompiler::BuildCode()
|
|||
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;
|
||||
}
|
||||
|
||||
const auto block_index = ouput_register_indices[n];
|
||||
shader_is_valid |= (!!temp_registers[block_index].h0_writes);
|
||||
}
|
||||
auto& r = temp_registers[block_index];
|
||||
|
||||
if (!shader_is_valid)
|
||||
{
|
||||
properties.has_no_output = true;
|
||||
|
||||
if (!properties.has_discard_op)
|
||||
if (fp16_out)
|
||||
{
|
||||
// NOTE: Discard operation overrides output
|
||||
rsx_log.warning("Shader does not write to any output register and will be NOPed");
|
||||
main = "/*" + main + "*/";
|
||||
// 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);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -822,6 +863,9 @@ std::string FragmentProgramDecompiler::BuildCode()
|
|||
OS << "#endif\n";
|
||||
OS << " discard;\n";
|
||||
OS << "}\n";
|
||||
|
||||
// Don't consume any args
|
||||
m_parr.Clear();
|
||||
return OS.str();
|
||||
}
|
||||
|
||||
|
@ -1019,6 +1063,12 @@ std::string FragmentProgramDecompiler::BuildCode()
|
|||
|
||||
insertMainStart(OS);
|
||||
OS << main << std::endl;
|
||||
|
||||
if (const auto epilogue = main_epilogue.str(); !epilogue.empty())
|
||||
{
|
||||
OS << " // Epilogue\n";
|
||||
OS << epilogue << std::endl;
|
||||
}
|
||||
insertMainEnd(OS);
|
||||
|
||||
return OS.str();
|
||||
|
@ -1360,12 +1410,12 @@ std::string FragmentProgramDecompiler::Decompile()
|
|||
|
||||
switch (opcode)
|
||||
{
|
||||
case RSX_FP_OPCODE_NOP: break;
|
||||
case RSX_FP_OPCODE_NOP:
|
||||
break;
|
||||
case RSX_FP_OPCODE_KIL:
|
||||
properties.has_discard_op = true;
|
||||
AddFlowOp("_kill()");
|
||||
break;
|
||||
|
||||
default:
|
||||
int prev_force_unit = forced_unit;
|
||||
|
||||
|
|
|
@ -1,116 +1,10 @@
|
|||
#pragma once
|
||||
#include "ShaderParam.h"
|
||||
#include "FragmentProgramRegister.h"
|
||||
#include "RSXFragmentProgram.h"
|
||||
|
||||
#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
|
||||
* Backend with text based shader can subclass this class and implement :
|
||||
|
@ -157,7 +51,7 @@ class FragmentProgramDecompiler
|
|||
|
||||
bool m_is_valid_ucode = true;
|
||||
|
||||
std::array<temp_register, 64> temp_registers;
|
||||
std::array<rsx::MixedPrecisionRegister, 64> temp_registers;
|
||||
|
||||
std::string GetMask() const;
|
||||
|
||||
|
|
196
rpcs3/Emu/RSX/Program/FragmentProgramRegister.cpp
Normal file
196
rpcs3/Emu/RSX/Program/FragmentProgramRegister.cpp
Normal 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 + ")";
|
||||
}
|
||||
}
|
111
rpcs3/Emu/RSX/Program/FragmentProgramRegister.h
Normal file
111
rpcs3/Emu/RSX/Program/FragmentProgramRegister.h
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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_32_BITS_EXPORTS 0x40
|
||||
|
||||
#define GET_BITS(word, offset, count) bitfieldExtract(inst.words[word], offset, count)
|
||||
#define TEST_BIT(word, offset) (GET_BITS(word, offset, 1) > 0)
|
||||
#define GET_BITS(bitfield, offset, count) bitfieldExtract(bitfield, offset, count)
|
||||
#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 reg_mov(d, s, m) d = select(d, s, m)
|
||||
|
@ -174,7 +177,7 @@ int counter = 0;
|
|||
|
||||
vec4 read_src(const in int index)
|
||||
{
|
||||
ur0 = GET_BITS(index + 1, 0, 2);
|
||||
ur0 = GET_INST_BITS(index + 1, 0, 2);
|
||||
|
||||
switch (ur0)
|
||||
{
|
||||
|
@ -183,14 +186,14 @@ vec4 read_src(const in int index)
|
|||
switch(index)
|
||||
{
|
||||
case 0:
|
||||
ur1 = GET_BITS(1, 2, 6); break;
|
||||
ur1 = GET_INST_BITS(1, 2, 6); break;
|
||||
case 1:
|
||||
ur1 = GET_BITS(2, 2, 6); break;
|
||||
ur1 = GET_INST_BITS(2, 2, 6); break;
|
||||
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];
|
||||
}
|
||||
|
@ -202,7 +205,7 @@ vec4 read_src(const in int index)
|
|||
}
|
||||
case RSX_FP_REGISTER_TYPE_INPUT:
|
||||
{
|
||||
ur1 = GET_BITS(0, 13, 4);
|
||||
ur1 = GET_INST_BITS(0, 13, 4);
|
||||
switch (ur1)
|
||||
{
|
||||
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);
|
||||
|
||||
// abs
|
||||
if (index == 0)
|
||||
{
|
||||
if (TEST_BIT(1, 29)) vr0 = abs(vr0);
|
||||
if (TEST_INST_BIT(1, 29)) vr0 = abs(vr0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ur1 = index + 1;
|
||||
if (TEST_BIT(ur1, 18)) vr0 = abs(vr0);
|
||||
if (TEST_INST_BIT(ur1, 18)) vr0 = abs(vr0);
|
||||
}
|
||||
|
||||
// neg
|
||||
return (TEST_BIT(index + 1, 17))? -vr0 : vr0;
|
||||
return (TEST_INST_BIT(index + 1, 17))? -vr0 : vr0;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -283,7 +286,7 @@ bvec4 decode_cond(const in uint mode, const in vec4 cond)
|
|||
|
||||
bool check_cond()
|
||||
{
|
||||
ur0 = GET_BITS(1, 18, 3);
|
||||
ur0 = GET_INST_BITS(1, 18, 3);
|
||||
if (ur0 == 0x7)
|
||||
{
|
||||
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)
|
||||
{
|
||||
ur0 = GET_BITS(0, 17, 4);
|
||||
ur0 = GET_INST_BITS(0, 17, 4);
|
||||
if (!IS_TEXTURE_RESIDENT(ur0))
|
||||
{
|
||||
return vr_zero;
|
||||
}
|
||||
|
||||
ur1 = ur0 + ur0;
|
||||
const uint type = bitfieldExtract(texture_control, int(ur1), 2);
|
||||
const uint type = GET_BITS(texture_control, int(ur1), 2);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
@ -380,7 +383,7 @@ vec4 _texture(in vec4 coord, float bias)
|
|||
break;
|
||||
}
|
||||
|
||||
if (TEST_BIT(0, 21))
|
||||
if (TEST_INST_BIT(0, 21))
|
||||
{
|
||||
vr0 = vr0 * 2. - 1.;
|
||||
}
|
||||
|
@ -390,14 +393,14 @@ vec4 _texture(in vec4 coord, float bias)
|
|||
|
||||
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))
|
||||
{
|
||||
return vr_zero;
|
||||
}
|
||||
|
||||
ur1 = ur0 + ur0;
|
||||
const uint type = bitfieldExtract(texture_control, int(ur1), 2);
|
||||
const uint type = GET_BITS(texture_control, int(ur1), 2);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
@ -419,7 +422,7 @@ vec4 _textureLod(in vec4 coord, float lod)
|
|||
break;
|
||||
}
|
||||
|
||||
if (TEST_BIT(0, 21))
|
||||
if (TEST_INST_BIT(0, 21))
|
||||
{
|
||||
// Normal-expand, v = 2v - 1
|
||||
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));
|
||||
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);
|
||||
}
|
||||
|
||||
if (TEST_BIT(0, 30)) // NO DEST
|
||||
if (TEST_INST_BIT(0, 30)) // NO DEST
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ur1 = GET_BITS(2, 28, 3);
|
||||
ur1 = GET_INST_BITS(2, 28, 3);
|
||||
sr0 = modifier_scale[ur1];
|
||||
vr0 = value * sr0;
|
||||
|
||||
if (TEST_BIT(0, 31)) // SAT
|
||||
if (TEST_INST_BIT(0, 31)) // SAT
|
||||
{
|
||||
vr0 = clamp(vr0, 0, 1);
|
||||
}
|
||||
|
||||
ur0 = GET_BITS(1, 18, 3);
|
||||
ur0 = GET_INST_BITS(1, 18, 3);
|
||||
if (ur0 != 0x7)
|
||||
{
|
||||
vr1 = read_cond();
|
||||
|
@ -464,8 +467,8 @@ void write_dst(const in vec4 value)
|
|||
bvr0 = bvec4(uvec4(bvr0) & uvec4(bvr1));
|
||||
}
|
||||
|
||||
ur1 = GET_BITS(0, 1, 6);
|
||||
if (TEST_BIT(0, 7))
|
||||
ur1 = GET_INST_BITS(0, 1, 6);
|
||||
if (TEST_INST_BIT(0, 7))
|
||||
{
|
||||
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: 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;
|
||||
while (register_count > 0)
|
||||
{
|
||||
|
@ -587,11 +590,11 @@ void main()
|
|||
((fp_instructions[ip] << 8) & uvec4(0xFF00FF00)) |
|
||||
((fp_instructions[ip] >> 8) & uvec4(0x00FF00FF));
|
||||
|
||||
inst.opcode = GET_BITS(0, 24, 6);
|
||||
inst.end = TEST_BIT(0, 0);
|
||||
inst.opcode = GET_INST_BITS(0, 24, 6);
|
||||
inst.end = TEST_INST_BIT(0, 0);
|
||||
|
||||
#ifdef WITH_FLOW_CTRL
|
||||
if (TEST_BIT(2, 31))
|
||||
if (TEST_INST_BIT(2, 31))
|
||||
{
|
||||
// Flow control
|
||||
switch (inst.opcode | (1 << 6))
|
||||
|
@ -623,8 +626,8 @@ void main()
|
|||
case RSX_FP_OPCODE_REP:
|
||||
if (check_cond())
|
||||
{
|
||||
counter = int(GET_BITS(2, 2, 8) - GET_BITS(2, 10, 8));
|
||||
counter /= int(GET_BITS(2, 19, 8));
|
||||
counter = int(GET_INST_BITS(2, 2, 8) - GET_INST_BITS(2, 10, 8));
|
||||
counter /= int(GET_INST_BITS(2, 19, 8));
|
||||
loop_start_addr = ip + 1;
|
||||
loop_end_addr = int(inst.words.w >> 2);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ vec2 texture2DMSCoord(const in vec2 coords, const in uint flags)
|
|||
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));
|
||||
return _select(coords, wrapped_coords, wrap_control_mask);
|
||||
}
|
||||
|
|
|
@ -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 hash = vertex_program_utils::get_vertex_program_ucode_hash(program);
|
||||
hash ^= program.ctrl;
|
||||
hash ^= program.output_mask;
|
||||
hash ^= program.texture_state.texture_dimensions;
|
||||
hash ^= program.texture_state.multisampled_textures;
|
||||
return hash;
|
||||
const usz ucode_hash = vertex_program_utils::get_vertex_program_ucode_hash(program);
|
||||
const u32 state_params[] =
|
||||
{
|
||||
program.ctrl,
|
||||
program.output_mask,
|
||||
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
|
||||
|
@ -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 hash = fragment_program_utils::get_fragment_program_ucode_hash(program);
|
||||
hash ^= program.ctrl;
|
||||
hash ^= +program.two_sided_lighting;
|
||||
hash ^= program.texture_state.texture_dimensions;
|
||||
hash ^= program.texture_state.shadow_textures;
|
||||
hash ^= program.texture_state.redirected_textures;
|
||||
hash ^= program.texture_state.multisampled_textures;
|
||||
hash ^= program.texcoord_control_mask;
|
||||
|
||||
return hash;
|
||||
const usz ucode_hash = fragment_program_utils::get_fragment_program_ucode_hash(program);
|
||||
const u32 state_params[] =
|
||||
{
|
||||
program.ctrl,
|
||||
program.two_sided_lighting ? 1u : 0u,
|
||||
program.texture_state.texture_dimensions,
|
||||
program.texture_state.shadow_textures,
|
||||
program.texture_state.redirected_textures,
|
||||
program.texture_state.multisampled_textures,
|
||||
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
|
||||
{
|
||||
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.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;
|
||||
}
|
||||
|
||||
const void* instBuffer1 = binary1.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);
|
||||
|
||||
if (inst1._u ^ inst2._u)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
instIndex++;
|
||||
// Skip constants
|
||||
|
@ -578,9 +593,11 @@ bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, con
|
|||
fragment_program_utils::is_constant(inst1._u32[3]))
|
||||
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)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -293,10 +293,10 @@ public:
|
|||
bool compile_async,
|
||||
bool allow_notification,
|
||||
Args&& ...args
|
||||
)
|
||||
)
|
||||
{
|
||||
const auto &vp_search = search_vertex_program(vertexShader);
|
||||
const auto &fp_search = search_fragment_program(fragmentShader);
|
||||
const auto& vp_search = search_vertex_program(vertexShader);
|
||||
const auto& fp_search = search_fragment_program(fragmentShader);
|
||||
|
||||
const bool already_existing_fragment_program = std::get<1>(fp_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
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -300,8 +300,10 @@ struct RSXFragmentProgram
|
|||
u32 ucode_length = 0;
|
||||
u32 total_length = 0;
|
||||
u32 ctrl = 0;
|
||||
bool two_sided_lighting = false;
|
||||
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_config texture_params;
|
||||
|
|
|
@ -223,10 +223,10 @@ struct RSXVertexProgram
|
|||
{
|
||||
std::vector<u32> data;
|
||||
rsx::vertex_program_texture_state texture_state;
|
||||
u32 ctrl;
|
||||
u32 output_mask;
|
||||
u32 base_address;
|
||||
u32 entry;
|
||||
u32 ctrl = 0;
|
||||
u32 output_mask = 0;
|
||||
u32 base_address = 0;
|
||||
u32 entry = 0;
|
||||
std::bitset<rsx::max_vertex_program_instructions> instruction_mask;
|
||||
std::set<u32> jump_table;
|
||||
|
||||
|
|
|
@ -227,6 +227,14 @@ struct ParamArray
|
|||
|
||||
return name;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
for (auto& param : params)
|
||||
{
|
||||
param.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ShaderVariable
|
||||
|
|
|
@ -110,7 +110,7 @@ namespace rsx
|
|||
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
|
||||
int index = -1;
|
||||
|
@ -157,4 +157,31 @@ namespace rsx
|
|||
// OOB or partial match
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,9 @@ namespace rsx
|
|||
// 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
|
||||
// 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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1719,7 +1719,7 @@ namespace rsx
|
|||
|
||||
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];
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -2038,9 +2046,10 @@ namespace rsx
|
|||
|
||||
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.texcoord_control_mask = rsx::method_registers.texcoord_control_mask();
|
||||
current_fragment_program.two_sided_lighting = rsx::method_registers.two_side_light_en();
|
||||
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 = m_ctx->register_state->texcoord_control_mask();
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -436,6 +436,8 @@ namespace rsx
|
|||
|
||||
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:
|
||||
void reset();
|
||||
void init(u32 ctrlAddress);
|
||||
|
|
160
rpcs3/Emu/RSX/VK/VKCommonPipelineLayout.cpp
Normal file
160
rpcs3/Emu/RSX/VK/VKCommonPipelineLayout.cpp
Normal 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);
|
||||
}
|
||||
}
|
14
rpcs3/Emu/RSX/VK/VKCommonPipelineLayout.h
Normal file
14
rpcs3/Emu/RSX/VK/VKCommonPipelineLayout.h
Normal 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();
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include "VKAsyncScheduler.h"
|
||||
#include "VKCommandStream.h"
|
||||
#include "VKCommonDecompiler.h"
|
||||
#include "VKCommonPipelineLayout.h"
|
||||
#include "VKCompute.h"
|
||||
#include "VKGSRender.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()
|
||||
{
|
||||
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);
|
||||
|
||||
//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
|
||||
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
|
||||
m_vertex_env_buffer_info = { m_vertex_env_ring_info.heap->value, 0, 32 };
|
||||
m_vertex_constants_buffer_info = { m_transform_constants_ring_info.heap->value, 0, 32 };
|
||||
m_fragment_env_buffer_info = { m_fragment_env_ring_info.heap->value, 0, 32 };
|
||||
m_fragment_texture_params_buffer_info = { m_fragment_texture_params_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, 16 };
|
||||
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, 16 };
|
||||
m_raster_env_buffer_info = { m_raster_env_ring_info.heap->value, 0, 128 };
|
||||
|
||||
const auto limits = m_device->gpu().get_limits();
|
||||
|
@ -730,7 +589,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
|
|||
else
|
||||
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)
|
||||
{
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
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_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);
|
||||
|
||||
|
@ -2350,9 +2209,9 @@ void VKGSRender::load_program_env()
|
|||
}
|
||||
|
||||
// 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::fragment_constants_dirty |
|
||||
rsx::pipeline_state::fragment_texture_state_dirty;
|
||||
|
||||
if (!update_instancing_data)
|
||||
|
@ -2360,9 +2219,19 @@ void VKGSRender::load_program_env()
|
|||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!m_vertex_prog)
|
||||
if (!m_program || !m_vertex_prog)
|
||||
{
|
||||
// Shouldn't be reachable, but handle it correctly anyway
|
||||
m_graphics_state |= rsx::pipeline_state::transform_constants_dirty;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_vertex_prog->overlaps_constants_range(index, count))
|
||||
{
|
||||
// Nothing meaningful to us
|
||||
return;
|
||||
}
|
||||
|
||||
// Hot-patching transform constants mid-draw (instanced draw)
|
||||
std::pair<VkDeviceSize, VkDeviceSize> data_range;
|
||||
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_source = ®S(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 byte_count = count * 16;
|
||||
|
|
|
@ -288,6 +288,9 @@ public:
|
|||
// GRAPH backend
|
||||
void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override;
|
||||
|
||||
// Misc
|
||||
bool is_current_program_interpreted() const override;
|
||||
|
||||
protected:
|
||||
void clear_surface(u32 mask) override;
|
||||
void begin() override;
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace vk
|
|||
const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& fs_inputs)
|
||||
{
|
||||
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);
|
||||
result->link();
|
||||
return result;
|
||||
|
|
|
@ -387,7 +387,7 @@ namespace vk
|
|||
|
||||
struct stencilonly_unresolve : depth_resolve_base
|
||||
{
|
||||
VkClearRect region{};
|
||||
VkClearRect clear_region{};
|
||||
VkClearAttachment clear_info{};
|
||||
|
||||
stencilonly_unresolve()
|
||||
|
@ -402,8 +402,8 @@ namespace vk
|
|||
renderpass_config.set_depth_mask(false);
|
||||
|
||||
clear_info.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
region.baseArrayLayer = 0;
|
||||
region.layerCount = 1;
|
||||
clear_region.baseArrayLayer = 0;
|
||||
clear_region.layerCount = 1;
|
||||
|
||||
static_parameters_width = 3;
|
||||
|
||||
|
@ -425,7 +425,7 @@ namespace vk
|
|||
|
||||
void emit_geometry(vk::command_buffer& cmd) override
|
||||
{
|
||||
vkCmdClearAttachments(cmd, 1, &clear_info, 1, ®ion);
|
||||
vkCmdClearAttachments(cmd, 1, &clear_info, 1, &clear_region);
|
||||
|
||||
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);
|
||||
|
||||
region.rect.extent.width = resolve_image->width();
|
||||
region.rect.extent.height = resolve_image->height();
|
||||
clear_region.rect.extent.width = msaa_image->width();
|
||||
clear_region.rect.extent.height = msaa_image->height();
|
||||
|
||||
overlay_pass::run(
|
||||
cmd,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "stdafx.h"
|
||||
#include "VKShaderInterpreter.h"
|
||||
#include "VKCommonPipelineLayout.h"
|
||||
#include "VKVertexProgram.h"
|
||||
#include "VKFragmentProgram.h"
|
||||
#include "VKGSRender.h"
|
||||
|
@ -233,77 +234,12 @@ namespace vk
|
|||
std::pair<VkDescriptorSetLayout, VkPipelineLayout> shader_interpreter::create_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);
|
||||
auto bindings = get_common_binding_table();
|
||||
u32 idx = ::size32(bindings);
|
||||
|
||||
u32 idx = 0;
|
||||
|
||||
// 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++;
|
||||
bindings.resize(binding_table.total_descriptor_bindings);
|
||||
|
||||
// Texture 1D array
|
||||
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[idx].descriptorCount = 16;
|
||||
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
@ -313,6 +249,7 @@ namespace vk
|
|||
m_fragment_textures_start = bindings[idx].binding;
|
||||
idx++;
|
||||
|
||||
// Texture 2D array
|
||||
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[idx].descriptorCount = 16;
|
||||
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
@ -321,6 +258,7 @@ namespace vk
|
|||
|
||||
idx++;
|
||||
|
||||
// Texture 3D array
|
||||
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[idx].descriptorCount = 16;
|
||||
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
@ -329,6 +267,7 @@ namespace vk
|
|||
|
||||
idx++;
|
||||
|
||||
// Texture CUBE array
|
||||
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[idx].descriptorCount = 16;
|
||||
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
@ -337,6 +276,7 @@ namespace vk
|
|||
|
||||
idx++;
|
||||
|
||||
// Vertex texture array (2D only)
|
||||
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[idx].descriptorCount = 4;
|
||||
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
|
@ -345,6 +285,7 @@ namespace vk
|
|||
|
||||
idx++;
|
||||
|
||||
// Vertex program ucode block
|
||||
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
bindings[idx].descriptorCount = 1;
|
||||
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
|
@ -354,6 +295,7 @@ namespace vk
|
|||
m_vertex_instruction_start = bindings[idx].binding;
|
||||
idx++;
|
||||
|
||||
// Fragment program ucode block
|
||||
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
bindings[idx].descriptorCount = 1;
|
||||
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
@ -364,6 +306,22 @@ namespace vk
|
|||
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;
|
||||
push_constants[0].offset = 0;
|
||||
push_constants[0].size = 16;
|
||||
|
@ -392,16 +350,7 @@ namespace vk
|
|||
void shader_interpreter::create_descriptor_pools(const vk::render_device& dev)
|
||||
{
|
||||
const auto max_draw_calls = dev.get_descriptor_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);
|
||||
m_descriptor_pool.create(dev, m_descriptor_pool_sizes, max_draw_calls);
|
||||
}
|
||||
|
||||
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);
|
||||
create_descriptor_pools(dev);
|
||||
|
||||
rsx_log.notice("Building global vertex program interpreter...");
|
||||
build_vs();
|
||||
// TODO: Seed the cache
|
||||
}
|
||||
|
@ -449,6 +399,7 @@ namespace vk
|
|||
}
|
||||
else
|
||||
{
|
||||
rsx_log.notice("Compiling FS...");
|
||||
fs = build_fs(compiler_opt);
|
||||
}
|
||||
|
||||
|
@ -463,15 +414,17 @@ namespace vk
|
|||
shader_stages[1].module = fs->get_handle();
|
||||
shader_stages[1].pName = "main";
|
||||
|
||||
std::vector<VkDynamicState> dynamic_state_descriptors;
|
||||
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_VIEWPORT);
|
||||
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_SCISSOR);
|
||||
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_LINE_WIDTH);
|
||||
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
|
||||
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);
|
||||
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
|
||||
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
|
||||
dynamic_state_descriptors.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS);
|
||||
std::vector<VkDynamicState> dynamic_state_descriptors =
|
||||
{
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
VK_DYNAMIC_STATE_LINE_WIDTH,
|
||||
VK_DYNAMIC_STATE_BLEND_CONSTANTS,
|
||||
VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
|
||||
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
|
||||
VK_DYNAMIC_STATE_STENCIL_REFERENCE,
|
||||
VK_DYNAMIC_STATE_DEPTH_BIAS
|
||||
};
|
||||
|
||||
if (vk::get_current_renderer()->get_depth_bounds_support())
|
||||
{
|
||||
|
@ -502,6 +455,9 @@ namespace vk
|
|||
VkPipelineColorBlendStateCreateInfo cs = properties.state.cs;
|
||||
cs.pAttachments = properties.state.att_state;
|
||||
|
||||
VkPipelineTessellationStateCreateInfo ts = {};
|
||||
ts.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
|
||||
|
||||
VkGraphicsPipelineCreateInfo info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
info.pVertexInputState = &vi;
|
||||
|
@ -511,6 +467,7 @@ namespace vk
|
|||
info.pMultisampleState = &ms;
|
||||
info.pViewportState = &vp;
|
||||
info.pDepthStencilState = &properties.state.ds;
|
||||
info.pTessellationState = &ts;
|
||||
info.stageCount = 2;
|
||||
info.pStages = shader_stages;
|
||||
info.pDynamicState = &dynamic_state_info;
|
||||
|
|
|
@ -41,6 +41,7 @@ namespace vk
|
|||
|
||||
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;
|
||||
rsx::simple_array<VkDescriptorPoolSize> m_descriptor_pool_sizes;
|
||||
vk::descriptor_pool m_descriptor_pool;
|
||||
|
||||
u32 m_vertex_instruction_start = 0;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue