mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-03-10 23:45:16 +01:00
Merge branch 'master' into windows-clang
This commit is contained in:
commit
3bf182734f
8
.github/workflows/rpcs3.yml
vendored
8
.github/workflows/rpcs3.yml
vendored
|
|
@ -33,23 +33,23 @@ jobs:
|
|||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.8"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.9"
|
||||
build_sh: "/rpcs3/.ci/build-linux.sh"
|
||||
compiler: clang
|
||||
UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
|
||||
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux"
|
||||
- os: ubuntu-24.04
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.8"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.9"
|
||||
build_sh: "/rpcs3/.ci/build-linux.sh"
|
||||
compiler: gcc
|
||||
- os: ubuntu-24.04-arm
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.8"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9"
|
||||
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
|
||||
compiler: clang
|
||||
UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
|
||||
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
|
||||
- os: ubuntu-24.04-arm
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.8"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9"
|
||||
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
|
||||
compiler: gcc
|
||||
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}
|
||||
|
|
|
|||
2
3rdparty/OpenAL/openal-soft
vendored
2
3rdparty/OpenAL/openal-soft
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 75c00596307bf05ba7bbc8c7022836bf52f17477
|
||||
Subproject commit c41d64c6a35f6174bf4a27010aeac52a8d3bb2c6
|
||||
2
3rdparty/libsdl-org/SDL
vendored
2
3rdparty/libsdl-org/SDL
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit a962f40bbba175e9716557a25d5d7965f134a3d3
|
||||
Subproject commit 683181b47cfabd293e3ea409f838915b8297a4fd
|
||||
2
3rdparty/zlib/zlib
vendored
2
3rdparty/zlib/zlib
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf
|
||||
Subproject commit da607da739fa6047df13e66a2af6b8bec7c2a498
|
||||
|
|
@ -117,6 +117,7 @@ static fs::error to_error(DWORD e)
|
|||
case ERROR_NEGATIVE_SEEK: return fs::error::inval;
|
||||
case ERROR_DIRECTORY: return fs::error::inval;
|
||||
case ERROR_INVALID_NAME: return fs::error::inval;
|
||||
case ERROR_INVALID_FUNCTION: return fs::error::inval;
|
||||
case ERROR_SHARING_VIOLATION: return fs::error::acces;
|
||||
case ERROR_DIR_NOT_EMPTY: return fs::error::notempty;
|
||||
case ERROR_NOT_READY: return fs::error::noent;
|
||||
|
|
@ -584,7 +585,7 @@ namespace fs
|
|||
if (!GetFileInformationByHandleEx(m_handle, FileIdInfo, &info, sizeof(info)))
|
||||
{
|
||||
// Try GetFileInformationByHandle as a fallback
|
||||
BY_HANDLE_FILE_INFORMATION info2;
|
||||
BY_HANDLE_FILE_INFORMATION info2{};
|
||||
ensure(GetFileInformationByHandle(m_handle, &info2));
|
||||
|
||||
info = {};
|
||||
|
|
@ -1658,11 +1659,40 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||
|
||||
// Check if the handle is actually valid.
|
||||
// This can fail on empty mounted drives (e.g. with ERROR_NOT_READY or ERROR_INVALID_FUNCTION).
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
BY_HANDLE_FILE_INFORMATION info{};
|
||||
if (!GetFileInformationByHandle(handle, &info))
|
||||
{
|
||||
const DWORD last_error = GetLastError();
|
||||
CloseHandle(handle);
|
||||
g_tls_error = to_error(GetLastError());
|
||||
|
||||
if (last_error == ERROR_INVALID_FUNCTION)
|
||||
{
|
||||
g_tls_error = fs::error::isdir;
|
||||
return;
|
||||
}
|
||||
|
||||
g_tls_error = to_error(last_error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
CloseHandle(handle);
|
||||
g_tls_error = fs::error::isdir;
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
|
||||
{
|
||||
CloseHandle(handle);
|
||||
g_tls_error = fs::error::acces;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mode & fs::write) && (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
|
||||
{
|
||||
CloseHandle(handle);
|
||||
g_tls_error = fs::error::readonly;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2605,7 +2635,7 @@ bool fs::pending_file::commit(bool overwrite)
|
|||
while (file_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// Get file ID (used to check for hardlinks)
|
||||
BY_HANDLE_FILE_INFORMATION file_info;
|
||||
BY_HANDLE_FILE_INFORMATION file_info{};
|
||||
|
||||
if (!GetFileInformationByHandle(file_handle, &file_info) || file_info.nNumberOfLinks == 1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ std::string wchar_to_utf8(std::wstring_view src);
|
|||
std::string utf16_to_utf8(std::u16string_view src);
|
||||
std::u16string utf8_to_utf16(std::string_view src);
|
||||
|
||||
// Copy null-terminated string from a std::string or a char array to a char array with truncation
|
||||
template <typename D, typename T>
|
||||
// Copy null-terminated string from a std::basic_string or a char array to a char array with truncation
|
||||
template <typename D, typename T> requires requires (D& d, T& t) { std::declval<decltype(&t[0])&>() = &d[0]; }
|
||||
inline void strcpy_trunc(D&& dst, const T& src)
|
||||
{
|
||||
const usz count = std::size(src) >= std::size(dst) ? std::max<usz>(std::size(dst), 1) - 1 : std::size(src);
|
||||
std::memcpy(std::data(dst), std::data(src), count);
|
||||
std::memset(std::data(dst) + count, 0, std::size(dst) - count);
|
||||
std::copy_n(std::data(src), count, std::data(dst));
|
||||
std::fill_n(std::data(dst) + count, std::size(dst) - count, std::remove_cvref_t<decltype(dst[0])>{});
|
||||
}
|
||||
|
||||
// Convert string to signed integer
|
||||
|
|
|
|||
|
|
@ -821,6 +821,14 @@ struct color4_base
|
|||
a *= rhs;
|
||||
}
|
||||
|
||||
void operator += (const color4_base<T>& rhs)
|
||||
{
|
||||
r += rhs.r;
|
||||
g += rhs.g;
|
||||
b += rhs.b;
|
||||
a += rhs.a;
|
||||
}
|
||||
|
||||
constexpr color4_base<T> operator * (const color4_base<T>& rhs) const
|
||||
{
|
||||
return { r * rhs.r, g * rhs.g, b * rhs.b, a * rhs.a };
|
||||
|
|
|
|||
|
|
@ -206,11 +206,7 @@ struct cpu_prof
|
|||
// Print only 7 hash characters out of 11 (which covers roughly 48 bits)
|
||||
if (type_id == 2)
|
||||
{
|
||||
fmt::append(results, "\n\t[%s", fmt::base57(be_t<u64>{name}));
|
||||
results.resize(results.size() - 4);
|
||||
|
||||
// Print chunk address from lowest 16 bits
|
||||
fmt::append(results, "...chunk-0x%05x]: %.4f%% (%u)", (name & 0xffff) * 4, _frac * 100., count);
|
||||
fmt::append(results, "\n\t[%s]: %.4f%% (%u)", spu_block_hash{name}, _frac * 100., count);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1397,9 +1397,9 @@ struct SceNpBasicMessageDetails
|
|||
// Presence details of an user
|
||||
struct SceNpBasicPresenceDetails
|
||||
{
|
||||
s8 title[SCE_NP_BASIC_PRESENCE_TITLE_SIZE_MAX];
|
||||
s8 status[SCE_NP_BASIC_PRESENCE_STATUS_SIZE_MAX];
|
||||
s8 comment[SCE_NP_BASIC_PRESENCE_COMMENT_SIZE_MAX];
|
||||
char title[SCE_NP_BASIC_PRESENCE_TITLE_SIZE_MAX];
|
||||
char status[SCE_NP_BASIC_PRESENCE_STATUS_SIZE_MAX];
|
||||
char comment[SCE_NP_BASIC_PRESENCE_COMMENT_SIZE_MAX];
|
||||
u8 data[SCE_NP_BASIC_MAX_PRESENCE_SIZE];
|
||||
be_t<u32> size;
|
||||
be_t<s32> state;
|
||||
|
|
@ -1410,9 +1410,9 @@ struct SceNpBasicPresenceDetails2
|
|||
{
|
||||
be_t<u32> struct_size;
|
||||
be_t<s32> state;
|
||||
s8 title[SCE_NP_BASIC_PRESENCE_TITLE_SIZE_MAX];
|
||||
s8 status[SCE_NP_BASIC_PRESENCE_EXTENDED_STATUS_SIZE_MAX];
|
||||
s8 comment[SCE_NP_BASIC_PRESENCE_COMMENT_SIZE_MAX];
|
||||
char title[SCE_NP_BASIC_PRESENCE_TITLE_SIZE_MAX];
|
||||
char status[SCE_NP_BASIC_PRESENCE_EXTENDED_STATUS_SIZE_MAX];
|
||||
char comment[SCE_NP_BASIC_PRESENCE_COMMENT_SIZE_MAX];
|
||||
u8 data[SCE_NP_BASIC_MAX_PRESENCE_SIZE];
|
||||
be_t<u32> size;
|
||||
};
|
||||
|
|
@ -1420,9 +1420,9 @@ struct SceNpBasicPresenceDetails2
|
|||
// Country/region code
|
||||
struct SceNpCountryCode
|
||||
{
|
||||
s8 data[2];
|
||||
s8 term;
|
||||
s8 padding[1];
|
||||
char data[2];
|
||||
char term;
|
||||
char padding[1];
|
||||
};
|
||||
|
||||
// Date information
|
||||
|
|
@ -1451,8 +1451,8 @@ struct SceNpScoreGameInfo
|
|||
// Ranking comment structure
|
||||
struct SceNpScoreComment
|
||||
{
|
||||
s8 data[SCE_NP_SCORE_COMMENT_MAXLEN];
|
||||
s8 term[1];
|
||||
char data[SCE_NP_SCORE_COMMENT_MAXLEN];
|
||||
char term[1];
|
||||
};
|
||||
|
||||
// Ranking information structure
|
||||
|
|
@ -1524,15 +1524,15 @@ struct SceNpScoreNpIdPcId
|
|||
// Basic clan information to be used in raking
|
||||
struct SceNpScoreClanBasicInfo
|
||||
{
|
||||
s8 clanName[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1];
|
||||
s8 clanTag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1];
|
||||
char clanName[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1];
|
||||
char clanTag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1];
|
||||
u8 reserved[10];
|
||||
};
|
||||
|
||||
// Clan member information handled in ranking
|
||||
struct SceNpScoreClansMemberDescription
|
||||
{
|
||||
s8 description[SCE_NP_CLANS_CLAN_DESCRIPTION_MAX_LENGTH + 1];
|
||||
char description[SCE_NP_CLANS_CLAN_DESCRIPTION_MAX_LENGTH + 1];
|
||||
};
|
||||
|
||||
// Clan ranking information
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ void comment_constant(std::string& last_opcode, u64 value, bool print_float = fa
|
|||
// Comment constant formation
|
||||
fmt::append(last_opcode, " #0x%xh", value);
|
||||
|
||||
if (print_float && ((value >> 31) <= 1u || (value >> 31) == 0x1'ffff'ffffu))
|
||||
if (print_float && ((value >> 31) <= 1u || (value >> 31) == 0x1'ffff'ffffu) && (value > 0x3fffff && (value << 32 >> 32) < 0xffc00000))
|
||||
{
|
||||
const f32 float_val = std::bit_cast<f32>(static_cast<u32>(value));
|
||||
|
||||
|
|
|
|||
|
|
@ -51,22 +51,22 @@ struct spu_itype
|
|||
RDCH,
|
||||
RCHCNT,
|
||||
|
||||
BR, // branch_tag first
|
||||
BR, // branch_tag first, zregmod_tag (2) first
|
||||
BRA,
|
||||
BRNZ,
|
||||
BRZ,
|
||||
BRHNZ,
|
||||
BRHZ,
|
||||
BRSL,
|
||||
BRASL,
|
||||
IRET,
|
||||
BI,
|
||||
BISLED,
|
||||
BISL,
|
||||
BIZ,
|
||||
BINZ,
|
||||
BIHZ,
|
||||
BIHNZ, // branch_tag last
|
||||
BIHNZ, // zregmod_tag (2) last
|
||||
BRSL,
|
||||
BRASL,
|
||||
BISL, // branch_tag last
|
||||
|
||||
ILH, // constant_tag_first
|
||||
ILHU,
|
||||
|
|
@ -245,7 +245,7 @@ struct spu_itype
|
|||
// Test for branch instruction
|
||||
friend constexpr bool operator &(type value, branch_tag)
|
||||
{
|
||||
return value >= BR && value <= BIHNZ;
|
||||
return value >= BR && value <= BISL;
|
||||
}
|
||||
|
||||
// Test for floating point instruction
|
||||
|
|
@ -299,7 +299,7 @@ struct spu_itype
|
|||
// Test for non register-modifying instruction
|
||||
friend constexpr bool operator &(type value, zregmod_tag)
|
||||
{
|
||||
return value >= HEQ && value <= STQR;
|
||||
return (value >= HEQ && value <= STQR) || (value >= BR && value <= BIHNZ);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1176,108 +1176,6 @@ void spu_cache::initialize(bool build_existing_cache)
|
|||
if ((g_cfg.core.spu_decoder == spu_decoder_type::asmjit || g_cfg.core.spu_decoder == spu_decoder_type::llvm) && !func_list.empty())
|
||||
{
|
||||
spu_log.success("SPU Runtime: Built %u functions.", func_list.size());
|
||||
|
||||
if (g_cfg.core.spu_debug)
|
||||
{
|
||||
std::string dump;
|
||||
dump.reserve(10'000'000);
|
||||
|
||||
std::map<std::span<u8>, spu_program*, span_less<u8>> sorted;
|
||||
|
||||
for (auto&& f : func_list)
|
||||
{
|
||||
// Interpret as a byte string
|
||||
std::span<u8> data = {reinterpret_cast<u8*>(f.data.data()), f.data.size() * sizeof(u32)};
|
||||
|
||||
sorted[data] = &f;
|
||||
}
|
||||
|
||||
std::unordered_set<u32> depth_n;
|
||||
|
||||
u32 n_max = 0;
|
||||
|
||||
for (auto&& [bytes, f] : sorted)
|
||||
{
|
||||
{
|
||||
sha1_context ctx;
|
||||
u8 output[20];
|
||||
|
||||
sha1_starts(&ctx);
|
||||
sha1_update(&ctx, bytes.data(), bytes.size());
|
||||
sha1_finish(&ctx, output);
|
||||
fmt::append(dump, "\n\t[%s] ", fmt::base57(output));
|
||||
}
|
||||
|
||||
u32 depth_m = 0;
|
||||
|
||||
for (auto&& [data, f2] : sorted)
|
||||
{
|
||||
u32 depth = 0;
|
||||
|
||||
if (f2 == f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < bytes.size(); i++)
|
||||
{
|
||||
if (i < data.size() && data[i] == bytes[i])
|
||||
{
|
||||
depth++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
depth_n.emplace(depth);
|
||||
depth_m = std::max(depth, depth_m);
|
||||
}
|
||||
|
||||
fmt::append(dump, "c=%06d,d=%06d ", depth_n.size(), depth_m);
|
||||
|
||||
bool sk = false;
|
||||
|
||||
for (u32 i = 0; i < std::min<usz>(bytes.size(), std::max<usz>(256, depth_m)); i++)
|
||||
{
|
||||
if (depth_m == i)
|
||||
{
|
||||
dump += '|';
|
||||
sk = true;
|
||||
}
|
||||
|
||||
fmt::append(dump, "%02x", bytes[i]);
|
||||
|
||||
if (i % 4 == 3)
|
||||
{
|
||||
if (sk)
|
||||
{
|
||||
sk = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
dump += ' ';
|
||||
}
|
||||
|
||||
dump += ' ';
|
||||
}
|
||||
}
|
||||
|
||||
fmt::append(dump, "\n\t%49s", "");
|
||||
|
||||
for (u32 i = 0; i < std::min<usz>(f->data.size(), std::max<usz>(64, utils::aligned_div<u32>(depth_m, 4))); i++)
|
||||
{
|
||||
fmt::append(dump, "%-10s", g_spu_iname.decode(std::bit_cast<be_t<u32>>(f->data[i])));
|
||||
}
|
||||
|
||||
n_max = std::max(n_max, ::size32(depth_n));
|
||||
|
||||
depth_n.clear();
|
||||
}
|
||||
|
||||
spu_log.notice("SPU Cache Dump (max_c=%d): %s", n_max, dump);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize global cache instance
|
||||
|
|
@ -3705,6 +3603,11 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
|||
|
||||
default:
|
||||
{
|
||||
if (type & spu_itype::zregmod)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Unconst
|
||||
const u32 op_rt = type & spu_itype::_quadrop ? +op.rt4 : +op.rt;
|
||||
m_regmod[pos / 4] = op_rt;
|
||||
|
|
@ -7488,6 +7391,7 @@ void spu_recompiler_base::dump(const spu_program& result, std::string& out)
|
|||
SPUDisAsm dis_asm(cpu_disasm_mode::dump, reinterpret_cast<const u8*>(result.data.data()), result.lower_bound);
|
||||
|
||||
std::string hash;
|
||||
be_t<u64> hash_start{};
|
||||
|
||||
if (!result.data.empty())
|
||||
{
|
||||
|
|
@ -7498,6 +7402,7 @@ void spu_recompiler_base::dump(const spu_program& result, std::string& out)
|
|||
sha1_update(&ctx, reinterpret_cast<const u8*>(result.data.data()), result.data.size() * 4);
|
||||
sha1_finish(&ctx, output);
|
||||
fmt::append(hash, "%s", fmt::base57(output));
|
||||
std::memcpy(&hash_start, output, sizeof(hash_start));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -7510,7 +7415,7 @@ void spu_recompiler_base::dump(const spu_program& result, std::string& out)
|
|||
{
|
||||
if (m_block_info[bb.first / 4])
|
||||
{
|
||||
fmt::append(out, "A: [0x%05x] %s\n", bb.first, m_entry_info[bb.first / 4] ? (m_ret_info[bb.first / 4] ? "Chunk" : "Entry") : "Block");
|
||||
fmt::append(out, "A: [0x%05x] %s [%s]\n", bb.first, m_entry_info[bb.first / 4] ? (m_ret_info[bb.first / 4] ? "Chunk" : "Entry") : "Block", spu_block_hash{(hash_start & -65536) + bb.first / 4});
|
||||
|
||||
fmt::append(out, "\t F: 0x%05x\n", bb.second.func);
|
||||
|
||||
|
|
|
|||
|
|
@ -903,8 +903,14 @@ public:
|
|||
|
||||
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(+op.ra); is_const)
|
||||
{
|
||||
if (value % 0x200 != 0)
|
||||
{
|
||||
// si10 is overwritten - likely an analysis mistake
|
||||
return;
|
||||
}
|
||||
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value | static_cast<u32>(op.si10));
|
||||
comment_constant(last_opcode, value | static_cast<u32>(op.si10), false);
|
||||
}
|
||||
}
|
||||
void ORHI(spu_opcode_t op)
|
||||
|
|
@ -941,8 +947,14 @@ public:
|
|||
|
||||
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(op.ra); is_const)
|
||||
{
|
||||
if (value % 0x200 != 0)
|
||||
{
|
||||
// si10 is overwritten - likely an analysis mistake
|
||||
return;
|
||||
}
|
||||
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value + static_cast<u32>(op.si10));
|
||||
comment_constant(last_opcode, value + static_cast<u32>(op.si10), false);
|
||||
}
|
||||
}
|
||||
void AHI(spu_opcode_t op)
|
||||
|
|
@ -963,8 +975,14 @@ public:
|
|||
|
||||
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(op.ra); is_const)
|
||||
{
|
||||
if (value % 0x200 != 0)
|
||||
{
|
||||
// si10 is overwritten - likely an analysis mistake
|
||||
return;
|
||||
}
|
||||
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value ^ static_cast<u32>(op.si10));
|
||||
comment_constant(last_opcode, value ^ static_cast<u32>(op.si10), false);
|
||||
}
|
||||
}
|
||||
void XORHI(spu_opcode_t op)
|
||||
|
|
|
|||
|
|
@ -2160,6 +2160,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if (bb.preds.size() >= 2)
|
||||
{
|
||||
if (g_cfg.core.spu_prof || g_cfg.core.spu_debug)
|
||||
{
|
||||
m_ir->CreateStore(m_ir->getInt64((m_hash_start & -65536) | (baddr >> 2)), spu_ptr(&spu_thread::block_hash));
|
||||
}
|
||||
}
|
||||
|
||||
// State check at the beginning of the chunk
|
||||
if (need_check || (bi == 0 && g_cfg.core.spu_block_size != spu_block_size_type::safe))
|
||||
{
|
||||
|
|
@ -2802,12 +2810,9 @@ public:
|
|||
std::string& llvm_log = function_log;
|
||||
raw_string_ostream out(llvm_log);
|
||||
|
||||
if (g_cfg.core.spu_debug)
|
||||
{
|
||||
fmt::append(llvm_log, "LLVM IR at 0x%x:\n", func.entry_point);
|
||||
out << *_module; // print IR
|
||||
out << "\n\n";
|
||||
}
|
||||
fmt::append(llvm_log, "LLVM IR at 0x%x:\n", func.entry_point);
|
||||
out << *_module; // print IR
|
||||
out << "\n\n";
|
||||
|
||||
if (verifyModule(*_module, &out))
|
||||
{
|
||||
|
|
@ -3274,12 +3279,9 @@ public:
|
|||
std::string llvm_log;
|
||||
raw_string_ostream out(llvm_log);
|
||||
|
||||
if (g_cfg.core.spu_debug)
|
||||
{
|
||||
fmt::append(llvm_log, "LLVM IR (interpreter):\n");
|
||||
out << *_module; // print IR
|
||||
out << "\n\n";
|
||||
}
|
||||
fmt::append(llvm_log, "LLVM IR (interpreter):\n");
|
||||
out << *_module; // print IR
|
||||
out << "\n\n";
|
||||
|
||||
if (verifyModule(*_module, &out))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ void fmt_class_string<spu_block_hash>::format(std::string& out, u64 arg)
|
|||
out.resize(out.size() - 4);
|
||||
|
||||
// Print chunk address from lowest 16 bits
|
||||
fmt::append(out, "...chunk-0x%05x", (arg & 0xffff) * 4);
|
||||
fmt::append(out, "-0x%05x", (arg & 0xffff) * 4);
|
||||
}
|
||||
|
||||
enum class spu_block_hash_short : u64{};
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ CellError lv2_cond::on_id_create()
|
|||
if (!mutex)
|
||||
{
|
||||
_mutex = static_cast<shared_ptr<lv2_obj>>(ensure(idm::get_unlocked<lv2_obj, lv2_mutex>(mtx_id)));
|
||||
mutex = static_cast<lv2_mutex*>(_mutex.get());
|
||||
}
|
||||
|
||||
// Defer function
|
||||
|
|
|
|||
|
|
@ -174,20 +174,28 @@ bool lv2_config_service_listener::check_service(const lv2_config_service& servic
|
|||
return true;
|
||||
}
|
||||
|
||||
bool lv2_config_service_listener::notify(const shared_ptr<lv2_config_service_event>& event)
|
||||
{
|
||||
service_events.emplace_back(event);
|
||||
return event->notify();
|
||||
}
|
||||
|
||||
bool lv2_config_service_listener::notify(const shared_ptr<lv2_config_service>& service)
|
||||
{
|
||||
if (!check_service(*service))
|
||||
return false;
|
||||
{
|
||||
std::lock_guard lock(mutex_service_events);
|
||||
|
||||
// Create service event and notify queue!
|
||||
const auto event = lv2_config_service_event::create(handle, service, *this);
|
||||
return notify(event);
|
||||
if (!check_service(*service))
|
||||
return false;
|
||||
|
||||
// Create service event and notify queue!
|
||||
const auto event = lv2_config_service_event::create(handle, service, *this);
|
||||
service_events.emplace_back(event);
|
||||
|
||||
if (!event->notify())
|
||||
{
|
||||
// If we fail to deliver the event to the queue just clean the event up or it'll hold the listener alive forever
|
||||
g_fxo->get<lv2_config>().remove_service_event(event->id);
|
||||
service_events.pop_back();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void lv2_config_service_listener::notify_all()
|
||||
|
|
@ -267,7 +275,7 @@ void lv2_config_service_event::write(sys_config_service_event_t *dst) const
|
|||
{
|
||||
const auto registered = service->is_registered();
|
||||
|
||||
dst->service_listener_handle = listener.get_id();
|
||||
dst->service_listener_handle = listener_id;
|
||||
dst->registered = registered;
|
||||
dst->service_id = service->id;
|
||||
dst->user_id = service->user_id;
|
||||
|
|
@ -346,7 +354,7 @@ error_code sys_config_get_service_event(u32 config_hdl, u32 event_id, vm::ptr<sy
|
|||
|
||||
// Find service_event object
|
||||
const auto event = g_fxo->get<lv2_config>().find_event(event_id);
|
||||
if (!event)
|
||||
if (!event || event->handle != cfg)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -296,11 +296,10 @@ private:
|
|||
|
||||
// The service listener owns the service events - service events will not be freed as long as their corresponding listener exists
|
||||
// This has been confirmed to be the case in realhw
|
||||
shared_mutex mutex_service_events;
|
||||
std::vector<shared_ptr<lv2_config_service_event>> service_events;
|
||||
shared_ptr<lv2_config_handle> handle;
|
||||
|
||||
bool notify(const shared_ptr<lv2_config_service_event>& event);
|
||||
|
||||
public:
|
||||
const sys_config_service_id service_id;
|
||||
const u64 min_verbosity;
|
||||
|
|
@ -370,14 +369,14 @@ public:
|
|||
// This has been confirmed to be the case in realhw
|
||||
const shared_ptr<lv2_config_handle> handle;
|
||||
const shared_ptr<lv2_config_service> service;
|
||||
const lv2_config_service_listener& listener;
|
||||
const u32 listener_id;
|
||||
|
||||
// Constructors (should not be used directly)
|
||||
lv2_config_service_event(shared_ptr<lv2_config_handle> _handle, shared_ptr<lv2_config_service> _service, const lv2_config_service_listener& _listener) noexcept
|
||||
: id(get_next_id())
|
||||
, handle(std::move(_handle))
|
||||
, service(std::move(_service))
|
||||
, listener(_listener)
|
||||
, listener_id(_listener.get_id())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ error_code sys_dbg_write_process_memory(s32 pid, u32 address, u32 size, vm::cptr
|
|||
i += op_size;
|
||||
}
|
||||
|
||||
if (!is_exec || i >= end)
|
||||
if ((!is_exec || i >= end) && exec_update_size > 0)
|
||||
{
|
||||
// Commit executable data update
|
||||
// The read memory is also super ptr so memmove can work correctly on all implementations
|
||||
|
|
|
|||
|
|
@ -170,8 +170,7 @@ CellError lv2_event_queue::send(lv2_event event, bool* notified_thread, lv2_even
|
|||
{
|
||||
if (auto cpu = get_current_cpu_thread())
|
||||
{
|
||||
cpu->state += cpu_flag::again;
|
||||
cpu->state += cpu_flag::exit;
|
||||
cpu->state += cpu_flag::again + cpu_flag::exit;
|
||||
}
|
||||
|
||||
sys_event.warning("Ignored event!");
|
||||
|
|
@ -309,6 +308,15 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode)
|
|||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
for (auto cpu = head; cpu; cpu = cpu->get_next_cpu())
|
||||
{
|
||||
if (cpu->state & cpu_flag::again)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
if (!queue.events.empty())
|
||||
{
|
||||
// Copy events for logging, does not empty
|
||||
|
|
@ -321,17 +329,6 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode)
|
|||
{
|
||||
qlock.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto cpu = head; cpu; cpu = cpu->get_next_cpu())
|
||||
{
|
||||
if (cpu->state & cpu_flag::again)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
|
@ -621,7 +618,7 @@ error_code sys_event_port_create(cpu_thread& cpu, vm::ptr<u32> eport_id, s32 por
|
|||
|
||||
sys_event.warning("sys_event_port_create(eport_id=*0x%x, port_type=%d, name=0x%llx)", eport_id, port_type, name);
|
||||
|
||||
if (port_type != SYS_EVENT_PORT_LOCAL && port_type != 3)
|
||||
if (port_type != SYS_EVENT_PORT_LOCAL && port_type != SYS_EVENT_PORT_IPC)
|
||||
{
|
||||
sys_event.error("sys_event_port_create(): unknown port type (%d)", port_type);
|
||||
return CELL_EINVAL;
|
||||
|
|
@ -675,8 +672,9 @@ error_code sys_event_port_connect_local(cpu_thread& cpu, u32 eport_id, u32 equeu
|
|||
std::lock_guard lock(id_manager::g_mutex);
|
||||
|
||||
const auto port = idm::check_unlocked<lv2_obj, lv2_event_port>(eport_id);
|
||||
auto queue = idm::get_unlocked<lv2_obj, lv2_event_queue>(equeue_id);
|
||||
|
||||
if (!port || !idm::check_unlocked<lv2_obj, lv2_event_queue>(equeue_id))
|
||||
if (!port || !queue)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
|
@ -691,7 +689,7 @@ error_code sys_event_port_connect_local(cpu_thread& cpu, u32 eport_id, u32 equeu
|
|||
return CELL_EISCONN;
|
||||
}
|
||||
|
||||
port->queue = idm::get_unlocked<lv2_obj, lv2_event_queue>(equeue_id);
|
||||
port->queue = std::move(queue);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include <deque>
|
||||
|
||||
class cpu_thread;
|
||||
class spu_thrread;
|
||||
|
||||
// Event Queue Type
|
||||
enum : u32
|
||||
|
|
|
|||
|
|
@ -19,6 +19,22 @@ lv2_event_flag::lv2_event_flag(utils::serial& ar)
|
|||
ar(pattern);
|
||||
}
|
||||
|
||||
// Always set result
|
||||
struct sys_event_store_result
|
||||
{
|
||||
vm::ptr<u64> ptr;
|
||||
u64 val = 0;
|
||||
|
||||
~sys_event_store_result() noexcept
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
cpu_thread::get_current()->check_state();
|
||||
*ptr = val;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::function<void(void*)> lv2_event_flag::load(utils::serial& ar)
|
||||
{
|
||||
return load_func(make_shared<lv2_event_flag>(stx::exact_t<utils::serial&>(ar)));
|
||||
|
|
@ -120,21 +136,7 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm
|
|||
ppu.gpr[5] = mode;
|
||||
ppu.gpr[6] = 0;
|
||||
|
||||
// Always set result
|
||||
struct store_result
|
||||
{
|
||||
vm::ptr<u64> ptr;
|
||||
u64 val = 0;
|
||||
|
||||
~store_result() noexcept
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
cpu_thread::get_current()->check_state();
|
||||
*ptr = val;
|
||||
}
|
||||
}
|
||||
} store{result};
|
||||
sys_event_store_result store{result};
|
||||
|
||||
if (!lv2_event_flag::check_mode(mode))
|
||||
{
|
||||
|
|
@ -273,21 +275,7 @@ error_code sys_event_flag_trywait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode,
|
|||
|
||||
sys_event_flag.trace("sys_event_flag_trywait(id=0x%x, bitptn=0x%llx, mode=0x%x, result=*0x%x)", id, bitptn, mode, result);
|
||||
|
||||
// Always set result
|
||||
struct store_result
|
||||
{
|
||||
vm::ptr<u64> ptr;
|
||||
u64 val = 0;
|
||||
|
||||
~store_result() noexcept
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
cpu_thread::get_current()->check_state();
|
||||
*ptr = val;
|
||||
}
|
||||
}
|
||||
} store{result};
|
||||
sys_event_store_result store{result};
|
||||
|
||||
if (!lv2_event_flag::check_mode(mode))
|
||||
{
|
||||
|
|
@ -556,8 +544,6 @@ error_code sys_event_flag_get(ppu_thread& ppu, u32 id, vm::ptr<u64> flags)
|
|||
return +flag.pattern;
|
||||
});
|
||||
|
||||
ppu.check_state();
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
if (flags) *flags = 0;
|
||||
|
|
@ -569,6 +555,8 @@ error_code sys_event_flag_get(ppu_thread& ppu, u32 id, vm::ptr<u64> flags)
|
|||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
|
||||
*flags = flag.ret;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -382,10 +382,12 @@ lv2_fs_object::lv2_fs_object(utils::serial& ar, bool)
|
|||
|
||||
u64 lv2_file::op_read(const fs::file& file, vm::ptr<void> buf, u64 size, u64 opt_pos)
|
||||
{
|
||||
if (u64 region = buf.addr() >> 28, region_end = (buf.addr() & 0xfff'ffff) + (size & 0xfff'ffff); region == region_end && ((region >> 28) == 0 || region >= 0xC))
|
||||
if (u64 region = buf.addr() >> 28, region_end = (buf.addr() + size) >> 28;
|
||||
size < u32{umax} && region == region_end && (region == 0 || region == 0xD) && vm::check_addr(buf.addr(), vm::page_writable, static_cast<u32>(size)))
|
||||
{
|
||||
// Optimize reads from safe memory
|
||||
return (opt_pos == umax ? file.read(buf.get_ptr(), size) : file.read_at(opt_pos, buf.get_ptr(), size));
|
||||
const auto buf_ptr = vm::get_super_ptr(buf.addr());
|
||||
return (opt_pos == umax ? file.read(buf_ptr, size) : file.read_at(opt_pos, buf_ptr, size));
|
||||
}
|
||||
|
||||
// Copy data from intermediate buffer (avoid passing vm pointer to a native API)
|
||||
|
|
@ -412,6 +414,14 @@ u64 lv2_file::op_read(const fs::file& file, vm::ptr<void> buf, u64 size, u64 opt
|
|||
|
||||
u64 lv2_file::op_write(const fs::file& file, vm::cptr<void> buf, u64 size)
|
||||
{
|
||||
if (u64 region = buf.addr() >> 28, region_end = (buf.addr() + size) >> 28;
|
||||
size < u32{umax} && region == region_end && (region == 0 || region == 0xD) && vm::check_addr(buf.addr(), vm::page_readable, static_cast<u32>(size)))
|
||||
{
|
||||
// Optimize writes from safe memory
|
||||
const auto buf_ptr = vm::get_super_ptr(buf.addr());
|
||||
return file.write(buf_ptr, size);
|
||||
}
|
||||
|
||||
// Copy data to intermediate buffer (avoid passing vm pointer to a native API)
|
||||
std::vector<uchar> local_buf(std::min<u64>(size, 65536));
|
||||
|
||||
|
|
@ -1391,7 +1401,8 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
|||
// Add additional entries for split file candidates (while ends with .66600)
|
||||
while (mp.mp != &g_mp_sys_dev_hdd1 && data.back().name.ends_with(".66600"))
|
||||
{
|
||||
data.emplace_back(data.back()).name.resize(data.back().name.size() - 6);
|
||||
fs::dir_entry copy = data.back();
|
||||
data.emplace_back(copy).name.resize(copy.name.size() - 6);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2147,6 +2158,7 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
|||
sys_fs.notice("sys_fs_fcntl(0xc0000006): %s", vpath);
|
||||
|
||||
// Check only mountpoint
|
||||
vpath = vpath.substr(0, vpath.find_first_of('\0'));
|
||||
vpath = vpath.substr(0, vpath.find_first_of("/", 1));
|
||||
|
||||
// Some mountpoints seem to be handled specially
|
||||
|
|
@ -2635,8 +2647,6 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr
|
|||
|
||||
error_code sys_fs_fdatasync(ppu_thread& ppu, u32 fd)
|
||||
{
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.trace("sys_fs_fdadasync(fd=%d)", fd);
|
||||
|
||||
const auto file = idm::get_unlocked<lv2_fs_object, lv2_file>(fd);
|
||||
|
|
@ -2661,8 +2671,6 @@ error_code sys_fs_fdatasync(ppu_thread& ppu, u32 fd)
|
|||
|
||||
error_code sys_fs_fsync(ppu_thread& ppu, u32 fd)
|
||||
{
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.trace("sys_fs_fsync(fd=%d)", fd);
|
||||
|
||||
const auto file = idm::get_unlocked<lv2_fs_object, lv2_file>(fd);
|
||||
|
|
@ -2903,14 +2911,6 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr<char> path, s32 mode)
|
|||
{
|
||||
// Try to locate split files
|
||||
|
||||
for (u32 i = 66601; i <= 66699; i++)
|
||||
{
|
||||
if (mp != &g_mp_sys_dev_hdd1 && !fs::get_stat(fmt::format("%s.%u", local_path, i), info) && !info.is_directory)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs::get_stat(local_path + ".66600", info) && !info.is_directory)
|
||||
{
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -487,6 +487,8 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
|||
{
|
||||
ensure(cond.unqueue(cond.sq, &ppu));
|
||||
ppu.state += cpu_flag::again;
|
||||
cond.lwmutex_waiters--;
|
||||
mutex->lwcond_waiters--;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ error_code sys_mmapper_allocate_shared_memory_ext(ppu_thread& ppu, u64 ipc_key,
|
|||
}
|
||||
}
|
||||
|
||||
if (flags & ~SYS_MEMORY_PAGE_SIZE_MASK)
|
||||
if (flags & ~SYS_MEMORY_GRANULARITY_MASK)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
|
@ -401,6 +401,11 @@ error_code sys_mmapper_allocate_shared_memory_from_container_ext(ppu_thread& ppu
|
|||
sys_mmapper.todo("sys_mmapper_allocate_shared_memory_from_container_ext(ipc_key=0x%x, size=0x%x, flags=0x%x, cid=0x%x, entries=*0x%x, entry_count=0x%x, mem_id=*0x%x)", ipc_key, size, flags, cid, entries,
|
||||
entry_count, mem_id);
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return CELL_EALIGN;
|
||||
}
|
||||
|
||||
switch (flags & SYS_MEMORY_PAGE_SIZE_MASK)
|
||||
{
|
||||
case SYS_MEMORY_PAGE_SIZE_1M:
|
||||
|
|
@ -546,8 +551,7 @@ error_code sys_mmapper_free_address(ppu_thread& ppu, u32 addr)
|
|||
|
||||
// If a memory block is freed, remove it from page notification table.
|
||||
auto& pf_entries = g_fxo->get<page_fault_notification_entries>();
|
||||
std::lock_guard lock(pf_entries.mutex);
|
||||
|
||||
std::unique_lock lock(pf_entries.mutex);
|
||||
auto ind_to_remove = pf_entries.entries.begin();
|
||||
for (; ind_to_remove != pf_entries.entries.end(); ++ind_to_remove)
|
||||
{
|
||||
|
|
@ -558,7 +562,11 @@ error_code sys_mmapper_free_address(ppu_thread& ppu, u32 addr)
|
|||
}
|
||||
if (ind_to_remove != pf_entries.entries.end())
|
||||
{
|
||||
u32 port_id = ind_to_remove->port_id;
|
||||
pf_entries.entries.erase(ind_to_remove);
|
||||
lock.unlock();
|
||||
sys_event_port_disconnect(ppu, port_id);
|
||||
sys_event_port_destroy(ppu, port_id);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
|
|
@ -826,7 +834,6 @@ error_code sys_mmapper_enable_page_fault_notification(ppu_thread& ppu, u32 start
|
|||
|
||||
vm::var<u32> port_id(0);
|
||||
error_code res = sys_event_port_create(ppu, port_id, SYS_EVENT_PORT_LOCAL, SYS_MEMORY_PAGE_FAULT_EVENT_KEY);
|
||||
sys_event_port_connect_local(ppu, *port_id, event_queue_id);
|
||||
|
||||
if (res + 0u == CELL_EAGAIN)
|
||||
{
|
||||
|
|
@ -834,6 +841,8 @@ error_code sys_mmapper_enable_page_fault_notification(ppu_thread& ppu, u32 start
|
|||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
sys_event_port_connect_local(ppu, *port_id, event_queue_id);
|
||||
|
||||
auto& pf_entries = g_fxo->get<page_fault_notification_entries>();
|
||||
std::unique_lock lock(pf_entries.mutex);
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ error_code sys_mutex_create(ppu_thread& ppu, vm::ptr<u32> mutex_id, vm::ptr<sys_
|
|||
sys_mutex.todo("sys_mutex_create(): unexpected adaptive (0x%x)", _attr.adaptive);
|
||||
}
|
||||
|
||||
if (auto error = lv2_obj::create<lv2_mutex>(_attr.pshared, _attr.ipc_key, _attr.flags, [&]()
|
||||
if (auto error = lv2_obj::create<lv2_mutex>(_attr.pshared, ipc_key, _attr.flags, [&]()
|
||||
{
|
||||
return make_shared<lv2_mutex>(
|
||||
_attr.protocol,
|
||||
|
|
|
|||
|
|
@ -173,7 +173,11 @@ struct lv2_mutex final : lv2_obj
|
|||
|
||||
if (sq == data.sq)
|
||||
{
|
||||
atomic_storage<u32>::release(control.raw().owner, res->id);
|
||||
if (cpu_flag::again - res->state)
|
||||
{
|
||||
atomic_storage<u32>::release(control.raw().owner, res->id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -563,37 +563,34 @@ error_code sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr
|
|||
return not_an_error(result);
|
||||
}
|
||||
|
||||
if (!sock.ret)
|
||||
while (auto state = ppu.state.fetch_sub(cpu_flag::signal))
|
||||
{
|
||||
while (auto state = ppu.state.fetch_sub(cpu_flag::signal))
|
||||
if (is_stopped(state))
|
||||
{
|
||||
if (is_stopped(state))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (state & cpu_flag::signal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ppu.state.wait(state);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ppu.gpr[3] == static_cast<u64>(-SYS_NET_EINTR))
|
||||
if (state & cpu_flag::signal)
|
||||
{
|
||||
return -SYS_NET_EINTR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (result < 0)
|
||||
{
|
||||
return sys_net_error{result};
|
||||
}
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
|
||||
return not_an_error(result);
|
||||
if (ppu.gpr[3] == static_cast<u64>(-SYS_NET_EINTR))
|
||||
{
|
||||
return -SYS_NET_EINTR;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (result < 0)
|
||||
{
|
||||
return sys_net_error{result};
|
||||
}
|
||||
|
||||
return not_an_error(result);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
|
|
@ -1295,7 +1292,7 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
|
|||
|
||||
if (auto sock = idm::check_unlocked<lv2_socket>(fds_buf[i].fd))
|
||||
{
|
||||
signaled += sock->poll(fds_buf[i], _fds[i]);
|
||||
sock->poll(fds_buf[i], _fds[i]);
|
||||
#ifdef _WIN32
|
||||
connecting[i] = sock->is_connecting();
|
||||
#endif
|
||||
|
|
@ -1303,7 +1300,6 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
|
|||
else
|
||||
{
|
||||
fds_buf[i].revents |= SYS_NET_POLLNVAL;
|
||||
signaled++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1536,9 +1532,9 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
|
|||
for (s32 i = 0; i < nfds; i++)
|
||||
{
|
||||
bool sig = false;
|
||||
if (_fds[i].revents & (POLLIN | POLLHUP | POLLERR))
|
||||
if ((_fds[i].revents & (POLLIN | POLLHUP | POLLERR)) && _readfds.bit(i))
|
||||
sig = true, rread.set(i);
|
||||
if (_fds[i].revents & (POLLOUT | POLLERR))
|
||||
if ((_fds[i].revents & (POLLOUT | POLLERR)) && _writefds.bit(i))
|
||||
sig = true, rwrite.set(i);
|
||||
|
||||
if (sig)
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ public:
|
|||
virtual void close() = 0;
|
||||
virtual s32 shutdown(s32 how) = 0;
|
||||
|
||||
virtual s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) = 0;
|
||||
virtual void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) = 0;
|
||||
virtual std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) = 0;
|
||||
|
||||
error_code abort_socket(s32 flags);
|
||||
|
|
|
|||
|
|
@ -1147,14 +1147,14 @@ s32 lv2_socket_native::shutdown(s32 how)
|
|||
return -get_last_error(false);
|
||||
}
|
||||
|
||||
s32 lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd)
|
||||
void lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd)
|
||||
{
|
||||
// Check for fake packet for dns interceptions
|
||||
auto& dnshook = g_fxo->get<np::dnshook>();
|
||||
if (sn_pfd.events & SYS_NET_POLLIN && dnshook.is_dns(sn_pfd.fd) && dnshook.is_dns_queue(sn_pfd.fd))
|
||||
{
|
||||
sn_pfd.revents |= SYS_NET_POLLIN;
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
if (sn_pfd.events & ~(SYS_NET_POLLIN | SYS_NET_POLLOUT | SYS_NET_POLLERR))
|
||||
{
|
||||
|
|
@ -1171,8 +1171,6 @@ s32 lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd)
|
|||
{
|
||||
native_pfd.events |= POLLOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::tuple<bool, bool, bool> lv2_socket_native::select(bs_t<lv2_socket::poll_t> selected, pollfd& native_pfd)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public:
|
|||
std::optional<s32> sendto(s32 flags, const std::vector<u8>& buf, std::optional<sys_net_sockaddr> opt_sn_addr, bool is_lock = true) override;
|
||||
std::optional<s32> sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock = true) override;
|
||||
|
||||
s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
|
||||
void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
|
||||
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) override;
|
||||
|
||||
bool is_socket_connected();
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ s32 lv2_socket_p2p::shutdown([[maybe_unused]] s32 how)
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 lv2_socket_p2p::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
|
||||
void lv2_socket_p2p::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
ensure(vport);
|
||||
|
|
@ -381,8 +381,6 @@ s32 lv2_socket_p2p::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native
|
|||
{
|
||||
sn_pfd.revents |= SYS_NET_POLLOUT;
|
||||
}
|
||||
|
||||
return sn_pfd.revents ? 1 : 0;
|
||||
}
|
||||
|
||||
std::tuple<bool, bool, bool> lv2_socket_p2p::select(bs_t<lv2_socket::poll_t> selected, [[maybe_unused]] pollfd& native_pfd)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public:
|
|||
void close() override;
|
||||
s32 shutdown(s32 how) override;
|
||||
|
||||
s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
|
||||
void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
|
||||
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) override;
|
||||
|
||||
void handle_new_data(sys_net_sockaddr_in_p2p p2p_addr, std::vector<u8> p2p_data);
|
||||
|
|
|
|||
|
|
@ -985,7 +985,7 @@ s32 lv2_socket_p2ps::shutdown([[maybe_unused]] s32 how)
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 lv2_socket_p2ps::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
|
||||
void lv2_socket_p2ps::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
sys_net.trace("[P2PS] poll checking for 0x%X", sn_pfd.events);
|
||||
|
|
@ -1002,14 +1002,7 @@ s32 lv2_socket_p2ps::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& nativ
|
|||
{
|
||||
sn_pfd.revents |= SYS_NET_POLLOUT;
|
||||
}
|
||||
|
||||
if (sn_pfd.revents)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::tuple<bool, bool, bool> lv2_socket_p2ps::select(bs_t<lv2_socket::poll_t> selected, [[maybe_unused]] pollfd& native_pfd)
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ public:
|
|||
void close() override;
|
||||
s32 shutdown(s32 how) override;
|
||||
|
||||
s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
|
||||
void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
|
||||
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) override;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -134,10 +134,9 @@ s32 lv2_socket_raw::shutdown([[maybe_unused]] s32 how)
|
|||
return {};
|
||||
}
|
||||
|
||||
s32 lv2_socket_raw::poll([[maybe_unused]] sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
|
||||
void lv2_socket_raw::poll([[maybe_unused]] sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
|
||||
{
|
||||
LOG_ONCE(raw_poll, "lv2_socket_raw::poll");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::tuple<bool, bool, bool> lv2_socket_raw::select([[maybe_unused]] bs_t<lv2_socket::poll_t> selected, [[maybe_unused]] pollfd& native_pfd)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,6 @@ public:
|
|||
void close() override;
|
||||
s32 shutdown(s32 how) override;
|
||||
|
||||
s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
|
||||
void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
|
||||
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -305,6 +305,7 @@ error_code sys_ppu_thread_detach(ppu_thread& ppu, u32 thread_id)
|
|||
{
|
||||
// Join and notify thread (it is detached from IDM now so it must be done explicitly now)
|
||||
*ptr = thread_state::finished;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -899,7 +899,7 @@ error_code _sys_prx_register_library(ppu_thread& ppu, vm::ptr<void> library)
|
|||
{
|
||||
for (u32 lib_addr = prx.exports_start, index = 0; lib_addr < prx.exports_end; index++, lib_addr += vm::read8(lib_addr) ? vm::read8(lib_addr) : sizeof_lib)
|
||||
{
|
||||
if (std::memcpy(vm::base(lib_addr), mem_copy.data(), sizeof_lib) == 0)
|
||||
if (std::memcmp(vm::base(lib_addr), mem_copy.data(), sizeof_lib) == 0)
|
||||
{
|
||||
atomic_storage<char>::release(prx.m_external_loaded_flags[index], true);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -46,14 +46,14 @@ namespace rsxaudio_ringbuf_reader
|
|||
static void set_timestamp(rsxaudio_shmem::ringbuf_t& ring_buf, u64 timestamp)
|
||||
{
|
||||
const s32 entry_idx_raw = (ring_buf.read_idx + ring_buf.rw_max_idx - (ring_buf.rw_max_idx > 2) - 1) % ring_buf.rw_max_idx;
|
||||
const s32 entry_idx = std::clamp<s32>(entry_idx_raw, 0, SYS_RSXAUDIO_RINGBUF_SZ);
|
||||
const s32 entry_idx = std::clamp<s32>(entry_idx_raw, 0, SYS_RSXAUDIO_RINGBUF_SZ - 1);
|
||||
|
||||
ring_buf.entries[entry_idx].timestamp = convert_to_timebased_time(timestamp);
|
||||
}
|
||||
|
||||
static std::tuple<bool /*notify*/, u64 /*blk_idx*/, u64 /*timestamp*/> update_status(rsxaudio_shmem::ringbuf_t& ring_buf)
|
||||
{
|
||||
const s32 read_idx = std::clamp<s32>(ring_buf.read_idx, 0, SYS_RSXAUDIO_RINGBUF_SZ);
|
||||
const s32 read_idx = std::clamp<s32>(ring_buf.read_idx, 0, SYS_RSXAUDIO_RINGBUF_SZ - 1);
|
||||
|
||||
if ((ring_buf.entries[read_idx].valid & 1) == 0U)
|
||||
{
|
||||
|
|
@ -61,7 +61,7 @@ namespace rsxaudio_ringbuf_reader
|
|||
}
|
||||
|
||||
const s32 entry_idx_raw = (ring_buf.read_idx + ring_buf.rw_max_idx - (ring_buf.rw_max_idx > 2)) % ring_buf.rw_max_idx;
|
||||
const s32 entry_idx = std::clamp<s32>(entry_idx_raw, 0, SYS_RSXAUDIO_RINGBUF_SZ);
|
||||
const s32 entry_idx = std::clamp<s32>(entry_idx_raw, 0, SYS_RSXAUDIO_RINGBUF_SZ - 1);
|
||||
|
||||
ring_buf.entries[read_idx].valid = 0;
|
||||
ring_buf.queue_notify_idx = (ring_buf.queue_notify_idx + 1) % ring_buf.queue_notify_step;
|
||||
|
|
@ -72,7 +72,7 @@ namespace rsxaudio_ringbuf_reader
|
|||
|
||||
static std::pair<bool /*entry_valid*/, u32 /*addr*/> get_addr(const rsxaudio_shmem::ringbuf_t& ring_buf)
|
||||
{
|
||||
const s32 read_idx = std::clamp<s32>(ring_buf.read_idx, 0, SYS_RSXAUDIO_RINGBUF_SZ);
|
||||
const s32 read_idx = std::clamp<s32>(ring_buf.read_idx, 0, SYS_RSXAUDIO_RINGBUF_SZ - 1);
|
||||
|
||||
if (ring_buf.entries[read_idx].valid & 1)
|
||||
{
|
||||
|
|
@ -1392,9 +1392,9 @@ void rsxaudio_backend_thread::operator()()
|
|||
return;
|
||||
}
|
||||
|
||||
static rsxaudio_state ra_state{};
|
||||
static emu_audio_cfg emu_cfg{};
|
||||
static bool backend_failed = false;
|
||||
rsxaudio_state ra_state{};
|
||||
emu_audio_cfg emu_cfg{};
|
||||
bool backend_failed = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
|
|
@ -2018,7 +2018,7 @@ void rsxaudio_periodic_tmr::cancel_timer_unlocked()
|
|||
{
|
||||
const u64 flag = 1;
|
||||
const auto wr_res = write(cancel_event, &flag, sizeof(flag));
|
||||
ensure(wr_res == sizeof(flag) || wr_res == -EAGAIN);
|
||||
ensure(wr_res == sizeof(flag) || errno == EAGAIN);
|
||||
}
|
||||
#elif defined(BSD) || defined(__APPLE__)
|
||||
handle[TIMER_ID].flags = (handle[TIMER_ID].flags & ~EV_ENABLE) | EV_DISABLE;
|
||||
|
|
|
|||
|
|
@ -441,6 +441,8 @@ error_code sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
|
|||
continue;
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
std::lock_guard lock(rwlock->mutex);
|
||||
|
||||
if (!rwlock->unqueue(rwlock->wq, &ppu))
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ error_code sys_semaphore_create(ppu_thread& ppu, vm::ptr<u32> sem_id, vm::ptr<sy
|
|||
return error;
|
||||
}
|
||||
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
ppu.check_state();
|
||||
|
||||
*sem_id = idm::last_id();
|
||||
return CELL_OK;
|
||||
|
|
@ -358,7 +358,7 @@ error_code sys_semaphore_get_value(ppu_thread& ppu, u32 sem_id, vm::ptr<s32> cou
|
|||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
ppu.check_state();
|
||||
|
||||
*count = sema.ret;
|
||||
return CELL_OK;
|
||||
|
|
|
|||
|
|
@ -437,7 +437,7 @@ struct spu_limits_t
|
|||
raw_spu_count += spu_thread::g_raw_spu_ctr;
|
||||
|
||||
// physical_spus_count >= spu_limit returns EBUSY, not EINVAL!
|
||||
if (spu_limit + raw_limit > 6 || raw_spu_count > raw_limit || physical_spus_count >= spu_limit || physical_spus_count > spu_limit || controllable_spu_count > spu_limit)
|
||||
if (spu_limit + raw_limit > 6 || raw_spu_count > raw_limit || physical_spus_count >= spu_limit || controllable_spu_count > spu_limit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ struct lv2_update_manager
|
|||
|
||||
// For example, 4.90 should be converted to 0x4900000000000
|
||||
std::erase(version_str, '.');
|
||||
if (std::from_chars(version_str.data(), version_str.data() + version_str.size(), system_sw_version, 16).ec != std::errc{})
|
||||
if (std::from_chars(version_str.data(), version_str.data() + version_str.size(), system_sw_version, 16).ec == std::errc{})
|
||||
system_sw_version <<= 40;
|
||||
else
|
||||
system_sw_version = 0;
|
||||
|
|
@ -79,6 +79,7 @@ struct lv2_update_manager
|
|||
|
||||
if (malloc_set.count(addr))
|
||||
{
|
||||
malloc_set.erase(addr);
|
||||
return vm::dealloc(addr, vm::main);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1531,9 +1531,11 @@ private:
|
|||
}
|
||||
}();
|
||||
|
||||
if (sce_idx == umax)
|
||||
return PS3AV_STATUS_INVALID_VIDEO_PARAM;
|
||||
|
||||
const video_sce_param &sce_param = sce_param_arr[sce_idx];
|
||||
if (sce_idx == umax ||
|
||||
video_head_cfg.video_head > PS3AV_HEAD_B_ANALOG ||
|
||||
if (video_head_cfg.video_head > PS3AV_HEAD_B_ANALOG ||
|
||||
video_head_cfg.video_order > 1 ||
|
||||
video_head_cfg.video_format > 16 ||
|
||||
video_head_cfg.video_out_format > 16 ||
|
||||
|
|
|
|||
|
|
@ -1173,11 +1173,15 @@ error_code sys_usbd_get_device_list(ppu_thread& ppu, u32 handle, vm::ptr<UsbInte
|
|||
return CELL_EINVAL;
|
||||
|
||||
// TODO: was std::min<s32>
|
||||
u32 i_tocopy = std::min<u32>(max_devices, ::size32(usbh.handled_devices));
|
||||
const u32 i_tocopy = std::min<u32>(max_devices, ::size32(usbh.handled_devices));
|
||||
u32 index = 0;
|
||||
|
||||
for (u32 index = 0; index < i_tocopy; index++)
|
||||
for (const auto& [_, device] : usbh.handled_devices)
|
||||
{
|
||||
device_list[index] = usbh.handled_devices[index].first;
|
||||
if (index == i_tocopy)
|
||||
break;
|
||||
|
||||
device_list[index++] = device.first;
|
||||
}
|
||||
|
||||
return not_an_error(i_tocopy);
|
||||
|
|
@ -1409,7 +1413,7 @@ error_code sys_usbd_receive_event(ppu_thread& ppu, u32 handle, vm::ptr<u64> arg1
|
|||
|
||||
if (is_stopped(state))
|
||||
{
|
||||
std::lock_guard lock(usbh.mutex);
|
||||
std::lock_guard lock(usbh.mutex_sq);
|
||||
|
||||
for (auto cpu = +usbh.sq; cpu; cpu = cpu->next_cpu)
|
||||
{
|
||||
|
|
@ -1587,7 +1591,7 @@ error_code sys_usbd_get_transfer_status(ppu_thread& ppu, u32 handle, u32 id_tran
|
|||
|
||||
std::lock_guard lock(usbh.mutex);
|
||||
|
||||
if (!usbh.is_init)
|
||||
if (!usbh.is_init || id_transfer >= MAX_SYS_USBD_TRANSFERS)
|
||||
return CELL_EINVAL;
|
||||
|
||||
const auto status = usbh.get_transfer_status(id_transfer);
|
||||
|
|
@ -1607,7 +1611,7 @@ error_code sys_usbd_get_isochronous_transfer_status(ppu_thread& ppu, u32 handle,
|
|||
|
||||
std::lock_guard lock(usbh.mutex);
|
||||
|
||||
if (!usbh.is_init)
|
||||
if (!usbh.is_init || id_transfer >= MAX_SYS_USBD_TRANSFERS)
|
||||
return CELL_EINVAL;
|
||||
|
||||
const auto status = usbh.get_isochronous_transfer_status(id_transfer);
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ error_code sys_vm_memory_map(ppu_thread& ppu, u64 vsize, u64 psize, u32 cid, u64
|
|||
// Look for unmapped space
|
||||
if (const auto area = vm::find_map(0x10000000, 0x10000000, 2 | (flag & SYS_MEMORY_PAGE_SIZE_MASK)))
|
||||
{
|
||||
sys_vm.warning("sys_vm_memory_map(): Found VM 0x%x area (vsize=0x%x)", addr, vsize);
|
||||
sys_vm.warning("sys_vm_memory_map(): Found VM 0x%x area (vsize=0x%x)", area->addr, vsize);
|
||||
|
||||
// Alloc all memory (shall not fail)
|
||||
ensure(area->alloc(static_cast<u32>(vsize)));
|
||||
|
|
|
|||
|
|
@ -532,8 +532,8 @@ namespace np
|
|||
|
||||
if (!mi.msg().empty())
|
||||
{
|
||||
sce_mi->msgLen = ::narrow<u32>(mi.msg().size());
|
||||
auto* ptr_msg_data = static_cast<u8*>(edata.allocate<void>(mi.msg().size(), sce_mi->msg));
|
||||
sce_mi->msgLen = ::size32(mi.msg());
|
||||
auto* ptr_msg_data = static_cast<u8*>(edata.allocate<void>(::size32(mi.msg()), sce_mi->msg));
|
||||
memcpy(ptr_msg_data, mi.msg().data(), mi.msg().size());
|
||||
}
|
||||
}
|
||||
|
|
@ -575,8 +575,8 @@ namespace np
|
|||
|
||||
if (!resp.opt().empty())
|
||||
{
|
||||
room_status->opt_len = ::narrow<u32>(resp.opt().size());
|
||||
u8* opt_data = static_cast<u8*>(edata.allocate<void>(resp.opt().size(), room_status->opt));
|
||||
room_status->opt_len = ::size32(resp.opt());
|
||||
u8* opt_data = static_cast<u8*>(edata.allocate<void>(::size32(resp.opt()), room_status->opt));
|
||||
|
||||
memcpy(opt_data, resp.opt().data(), resp.opt().size());
|
||||
}
|
||||
|
|
@ -604,8 +604,8 @@ namespace np
|
|||
cur_attr->id = attr.attr_id();
|
||||
if (!attr.data().empty())
|
||||
{
|
||||
cur_attr->value.data.size = ::narrow<u32>(attr.data().size());
|
||||
u8* data_ptr = static_cast<u8*>(edata.allocate<void>(attr.data().size(), cur_attr->value.data.ptr));
|
||||
cur_attr->value.data.size = ::size32(attr.data());
|
||||
u8* data_ptr = static_cast<u8*>(edata.allocate<void>(::size32(attr.data()), cur_attr->value.data.ptr));
|
||||
memcpy(data_ptr, attr.data().data(), attr.data().size());
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -847,6 +847,17 @@ namespace rsx
|
|||
}
|
||||
}
|
||||
|
||||
bool texture_format_ex::hw_SNORM_possible() const
|
||||
{
|
||||
return (texel_remap_control & SEXT_MASK) == (get_host_format_snorm_mask(format()) << SEXT_OFFSET);
|
||||
}
|
||||
|
||||
bool texture_format_ex::hw_SRGB_possible() const
|
||||
{
|
||||
return encoded_remap == RSX_TEXTURE_REMAP_IDENTITY &&
|
||||
(texel_remap_control & GAMMA_CTRL_MASK) == GAMMA_RGB_MASK;
|
||||
}
|
||||
|
||||
std::vector<rsx::subresource_layout> get_subresources_layout(const rsx::fragment_texture& texture)
|
||||
{
|
||||
return get_subresources_layout_impl(texture);
|
||||
|
|
@ -1255,6 +1266,32 @@ namespace rsx
|
|||
fmt::throw_exception("Unknown format 0x%x", texture_format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a channel mask in ARGB that can be SNORM-converted
|
||||
* Some formats have a hardcoded constant in one lane which we cannot SNORM-interpret in hardware.
|
||||
*/
|
||||
u32 get_host_format_snorm_mask(u32 format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_B8:
|
||||
case CELL_GCM_TEXTURE_R5G6B5:
|
||||
case CELL_GCM_TEXTURE_R6G5B5:
|
||||
case CELL_GCM_TEXTURE_D1R5G5B5:
|
||||
case CELL_GCM_TEXTURE_D8R8G8B8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8:
|
||||
// Hardcoded alpha formats
|
||||
return 0b1110;
|
||||
|
||||
case CELL_GCM_TEXTURE_X16:
|
||||
// This one is a mess. X and Z are hardcoded. Not supported.
|
||||
// Fall through instead of throw
|
||||
default:
|
||||
return 0b1111;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A texture is stored as an array of blocks, where a block is a pixel for standard texture
|
||||
* but is a structure containing several pixels for compressed format
|
||||
|
|
|
|||
|
|
@ -135,6 +135,12 @@ namespace rsx
|
|||
RSX_FORMAT_FEATURE_16BIT_CHANNELS = (1 << 3), // Complements RSX_FORMAT_FEATURE_SIGNED_COMPONENTS
|
||||
};
|
||||
|
||||
enum host_format_features : u8
|
||||
{
|
||||
RSX_HOST_FORMAT_FEATURE_SNORM = (1 << 0),
|
||||
RSX_HOST_FORMAT_FEATURE_SRGB = (1 << 1),
|
||||
};
|
||||
|
||||
using enum format_features;
|
||||
|
||||
struct texture_format_ex
|
||||
|
|
@ -147,10 +153,28 @@ namespace rsx
|
|||
bool valid() const { return format_bits != 0; }
|
||||
u32 format() const { return format_bits & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); }
|
||||
|
||||
bool hw_SNORM_possible() const;
|
||||
bool hw_SRGB_possible() const;
|
||||
|
||||
bool host_snorm_format_active() const { return host_features & RSX_HOST_FORMAT_FEATURE_SNORM; }
|
||||
bool host_srgb_format_active() const { return host_features & RSX_HOST_FORMAT_FEATURE_SRGB; }
|
||||
|
||||
operator bool() const { return valid(); }
|
||||
|
||||
bool operator == (const texture_format_ex& that) const
|
||||
{
|
||||
return this->format_bits == that.format_bits &&
|
||||
this->features == that.features &&
|
||||
this->host_features == that.host_features &&
|
||||
this->encoded_remap == that.encoded_remap;
|
||||
}
|
||||
|
||||
//private:
|
||||
u32 format_bits = 0;
|
||||
u32 features = 0;
|
||||
u32 encoded_remap = 0;
|
||||
u32 texel_remap_control = 0;
|
||||
u32 host_features = 0;
|
||||
};
|
||||
|
||||
// Sampled image descriptor
|
||||
|
|
@ -293,6 +317,12 @@ namespace rsx
|
|||
*/
|
||||
rsx::flags32_t get_format_features(u32 texture_format);
|
||||
|
||||
/**
|
||||
* Returns a channel mask in ARGB that can be SNORM-converted
|
||||
* Some formats have a hardcoded constant in one lane which we cannot SNORM-interpret in hardware.
|
||||
*/
|
||||
u32 get_host_format_snorm_mask(u32 format);
|
||||
|
||||
/**
|
||||
* Returns number of texel rows encoded in one pitch-length line of bytes
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -299,95 +299,155 @@ void GLGSRender::load_texture_env()
|
|||
for (u32 textures_ref = current_fp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fs_sampler_state[i])
|
||||
{
|
||||
fs_sampler_state[i] = std::make_unique<gl::texture_cache::sampled_image_descriptor>();
|
||||
}
|
||||
|
||||
auto sampler_state = static_cast<gl::texture_cache::sampled_image_descriptor*>(fs_sampler_state[i].get());
|
||||
const auto& tex = rsx::method_registers.fragment_textures[i];
|
||||
const auto previous_format_class = sampler_state->format_class;
|
||||
|
||||
if (m_samplers_dirty || m_textures_dirty[i] || m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state, tex))
|
||||
if (!m_samplers_dirty &&
|
||||
!m_textures_dirty[i] &&
|
||||
!m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state, tex))
|
||||
{
|
||||
if (tex.enabled())
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool is_sampler_dirty = m_textures_dirty[i];
|
||||
m_textures_dirty[i] = false;
|
||||
|
||||
if (!tex.enabled())
|
||||
{
|
||||
*sampler_state = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
*sampler_state = m_gl_texture_cache.upload_texture(cmd, tex, m_rtts);
|
||||
if (!sampler_state->validate())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_sampler_dirty)
|
||||
{
|
||||
if (sampler_state->format_class != previous_format_class)
|
||||
{
|
||||
*sampler_state = m_gl_texture_cache.upload_texture(cmd, tex, m_rtts);
|
||||
|
||||
if (sampler_state->validate())
|
||||
{
|
||||
sampler_state->format_ex = tex.format_ex();
|
||||
|
||||
if (m_textures_dirty[i])
|
||||
{
|
||||
m_fs_sampler_states[i].apply(tex, fs_sampler_state[i].get());
|
||||
}
|
||||
else if (sampler_state->format_class != previous_format_class)
|
||||
{
|
||||
m_graphics_state |= rsx::fragment_program_state_dirty;
|
||||
}
|
||||
|
||||
const auto texture_format = sampler_state->format_ex.format();
|
||||
// Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking.
|
||||
// If accurate graphics are desired, force a bitcast to COLOR as a workaround.
|
||||
const bool is_depth_reconstructed = sampler_state->format_class != rsx::classify_format(texture_format) &&
|
||||
(texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8);
|
||||
// SNORM conversion required in shader. Do not interpolate to avoid introducing discontinuities due to how negative numbers work
|
||||
const bool is_snorm = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) != 0;
|
||||
|
||||
if (is_depth_reconstructed || is_snorm)
|
||||
{
|
||||
// Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking.
|
||||
m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*sampler_state = {};
|
||||
// Host details changed but RSX is not aware
|
||||
m_graphics_state |= rsx::fragment_program_state_dirty;
|
||||
}
|
||||
|
||||
m_textures_dirty[i] = false;
|
||||
if (sampler_state->format_ex)
|
||||
{
|
||||
// Nothing to change, use cached sampler
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sampler_state->format_ex = tex.format_ex();
|
||||
|
||||
if (sampler_state->format_ex.texel_remap_control &&
|
||||
sampler_state->image_handle &&
|
||||
sampler_state->upload_context == rsx::texture_upload_context::shader_read &&
|
||||
(current_fp_metadata.bx2_texture_reads_mask & (1u << i)) == 0 &&
|
||||
!g_cfg.video.disable_hardware_texel_remapping) [[ unlikely ]]
|
||||
{
|
||||
// Check if we need to override the view format
|
||||
const auto gl_format = sampler_state->image_handle->view_format();
|
||||
GLenum format_override = gl_format;
|
||||
rsx::flags32_t flags_to_erase = 0u;
|
||||
rsx::flags32_t host_flags_to_set = 0u;
|
||||
|
||||
if (sampler_state->format_ex.hw_SNORM_possible())
|
||||
{
|
||||
format_override = gl::get_compatible_snorm_format(gl_format);
|
||||
flags_to_erase = rsx::texture_control_bits::SEXT_MASK;
|
||||
host_flags_to_set = rsx::RSX_HOST_FORMAT_FEATURE_SNORM;
|
||||
}
|
||||
else if (sampler_state->format_ex.hw_SRGB_possible())
|
||||
{
|
||||
format_override = gl::get_compatible_srgb_format(gl_format);
|
||||
flags_to_erase = rsx::texture_control_bits::GAMMA_CTRL_MASK;
|
||||
host_flags_to_set = rsx::RSX_HOST_FORMAT_FEATURE_SRGB;
|
||||
}
|
||||
|
||||
if (format_override != GL_NONE && format_override != gl_format)
|
||||
{
|
||||
sampler_state->image_handle = sampler_state->image_handle->as(format_override);
|
||||
sampler_state->format_ex.texel_remap_control &= (~flags_to_erase);
|
||||
sampler_state->format_ex.host_features |= host_flags_to_set;
|
||||
}
|
||||
}
|
||||
|
||||
m_fs_sampler_states[i].apply(tex, fs_sampler_state[i].get());
|
||||
|
||||
const auto texture_format = sampler_state->format_ex.format();
|
||||
// Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking.
|
||||
// If accurate graphics are desired, force a bitcast to COLOR as a workaround.
|
||||
const bool is_depth_reconstructed = sampler_state->format_class != rsx::classify_format(texture_format) &&
|
||||
(texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8);
|
||||
// SNORM conversion required in shader. Do not interpolate to avoid introducing discontinuities due to how negative numbers work
|
||||
const bool is_snorm = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) != 0;
|
||||
|
||||
if (is_depth_reconstructed || is_snorm)
|
||||
{
|
||||
// Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking.
|
||||
m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 textures_ref = current_vp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!vs_sampler_state[i])
|
||||
{
|
||||
vs_sampler_state[i] = std::make_unique<gl::texture_cache::sampled_image_descriptor>();
|
||||
}
|
||||
|
||||
auto sampler_state = static_cast<gl::texture_cache::sampled_image_descriptor*>(vs_sampler_state[i].get());
|
||||
const auto& tex = rsx::method_registers.vertex_textures[i];
|
||||
const auto previous_format_class = sampler_state->format_class;
|
||||
|
||||
if (m_samplers_dirty || m_vertex_textures_dirty[i] || m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state, tex))
|
||||
if (!m_samplers_dirty &&
|
||||
!m_vertex_textures_dirty[i] &&
|
||||
!m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state, tex))
|
||||
{
|
||||
if (rsx::method_registers.vertex_textures[i].enabled())
|
||||
{
|
||||
*sampler_state = m_gl_texture_cache.upload_texture(cmd, rsx::method_registers.vertex_textures[i], m_rtts);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sampler_state->validate())
|
||||
{
|
||||
if (m_vertex_textures_dirty[i])
|
||||
{
|
||||
m_vs_sampler_states[i].apply(tex, vs_sampler_state[i].get());
|
||||
}
|
||||
else if (sampler_state->format_class != previous_format_class)
|
||||
{
|
||||
m_graphics_state |= rsx::vertex_program_state_dirty;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*sampler_state = {};
|
||||
}
|
||||
const bool is_sampler_dirty = m_vertex_textures_dirty[i];
|
||||
m_vertex_textures_dirty[i] = false;
|
||||
|
||||
m_vertex_textures_dirty[i] = false;
|
||||
if (!tex.enabled())
|
||||
{
|
||||
*sampler_state = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
*sampler_state = m_gl_texture_cache.upload_texture(cmd, rsx::method_registers.vertex_textures[i], m_rtts);
|
||||
|
||||
if (!sampler_state->validate())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_sampler_dirty)
|
||||
{
|
||||
m_vs_sampler_states[i].apply(tex, vs_sampler_state[i].get());
|
||||
}
|
||||
else if (sampler_state->format_class != previous_format_class)
|
||||
{
|
||||
m_graphics_state |= rsx::vertex_program_state_dirty;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -402,7 +462,9 @@ void GLGSRender::bind_texture_env()
|
|||
for (u32 textures_ref = current_fp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
gl::texture_view* view = nullptr;
|
||||
auto sampler_state = static_cast<gl::texture_cache::sampled_image_descriptor*>(fs_sampler_state[i].get());
|
||||
|
|
@ -442,22 +504,26 @@ void GLGSRender::bind_texture_env()
|
|||
for (u32 textures_ref = current_vp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sampler_state = static_cast<gl::texture_cache::sampled_image_descriptor*>(vs_sampler_state[i].get());
|
||||
gl::texture_view* view = nullptr;
|
||||
|
||||
if (rsx::method_registers.vertex_textures[i].enabled() &&
|
||||
sampler_state->validate())
|
||||
{
|
||||
if (sampler_state->image_handle) [[likely]]
|
||||
if (view = sampler_state->image_handle; !view)
|
||||
{
|
||||
sampler_state->image_handle->bind(cmd, GL_VERTEX_TEXTURES_START + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_gl_texture_cache.create_temporary_subresource(cmd, sampler_state->external_subresource_desc)->bind(cmd, GL_VERTEX_TEXTURES_START + i);
|
||||
view = m_gl_texture_cache.create_temporary_subresource(cmd, sampler_state->external_subresource_desc);
|
||||
}
|
||||
}
|
||||
|
||||
if (view) [[likely]]
|
||||
{
|
||||
view->bind(cmd, GL_VERTEX_TEXTURES_START + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->bind_texture(GL_VERTEX_TEXTURES_START + i, GL_TEXTURE_2D, GL_NONE);
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@ void GLFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS)
|
|||
m_shader_props.require_tex3D_ops = properties.has_tex3D;
|
||||
m_shader_props.require_shadowProj_ops = properties.shadow_sampler_mask != 0 && properties.has_texShadowProj;
|
||||
m_shader_props.require_alpha_kill = !!(m_prog.ctrl & RSX_SHADER_CONTROL_TEXTURE_ALPHA_KILL);
|
||||
m_shader_props.require_color_format_convert = !!(m_prog.ctrl & RSX_SHADER_CONTROL_TEXTURE_FORMAT_CONVERT);
|
||||
|
||||
glsl::insert_glsl_legacy_function(OS, m_shader_props);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,6 +266,44 @@ namespace gl
|
|||
fmt::throw_exception("Unknown format 0x%x", texture_format);
|
||||
}
|
||||
|
||||
GLenum get_compatible_snorm_format(GLenum base_format)
|
||||
{
|
||||
switch (base_format)
|
||||
{
|
||||
case GL_R8:
|
||||
return GL_R8_SNORM;
|
||||
case GL_RG8:
|
||||
return GL_RG8_SNORM;
|
||||
case GL_RGBA8:
|
||||
return GL_RGBA8_SNORM;
|
||||
case GL_R16:
|
||||
return GL_R16_SNORM;
|
||||
case GL_RG16:
|
||||
return GL_RG16_SNORM;
|
||||
case GL_RGBA16:
|
||||
return GL_RGBA16_SNORM;
|
||||
default:
|
||||
return GL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum get_compatible_srgb_format(GLenum base_format)
|
||||
{
|
||||
switch (base_format)
|
||||
{
|
||||
case GL_RGBA8:
|
||||
return GL_SRGB8_ALPHA8_EXT;
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
|
||||
default:
|
||||
return GL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
cs_shuffle_base* get_trivial_transform_job(const pixel_buffer_layout& pack_info)
|
||||
{
|
||||
if (!pack_info.swap_bytes)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ namespace gl
|
|||
std::tuple<GLenum, GLenum> get_format_type(u32 texture_format);
|
||||
pixel_buffer_layout get_format_type(texture::internal_format format);
|
||||
std::array<GLenum, 4> get_swizzle_remap(u32 texture_format);
|
||||
GLenum get_compatible_snorm_format(GLenum base_format);
|
||||
GLenum get_compatible_srgb_format(GLenum base_format);
|
||||
|
||||
viewable_image* create_texture(u32 gcm_format, u16 width, u16 height, u16 depth, u16 mipmaps, rsx::texture_dimension_extended type);
|
||||
|
||||
|
|
|
|||
|
|
@ -318,6 +318,34 @@ namespace gl
|
|||
}
|
||||
}
|
||||
|
||||
texture_view* texture_view::as(GLenum format)
|
||||
{
|
||||
if (format == this->m_view_format)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
auto self = m_root_view ? m_root_view : this;
|
||||
if (auto found = self->m_subviews.find(format);
|
||||
found != self->m_subviews.end())
|
||||
{
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
GLenum swizzle_argb[4] =
|
||||
{
|
||||
component_swizzle[3],
|
||||
component_swizzle[0],
|
||||
component_swizzle[1],
|
||||
component_swizzle[2],
|
||||
};
|
||||
|
||||
auto view = std::make_unique<texture_view>(m_image_data, m_target, format, swizzle_argb, m_aspect_flags);
|
||||
auto ret = view.get();
|
||||
self->m_subviews.emplace(format, std::move(view));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void texture_view::bind(gl::command_context& cmd, GLuint layer) const
|
||||
{
|
||||
cmd->bind_texture(layer, m_target, m_id);
|
||||
|
|
|
|||
|
|
@ -353,7 +353,10 @@ namespace gl
|
|||
GLenum m_aspect_flags = 0;
|
||||
texture* m_image_data = nullptr;
|
||||
|
||||
GLenum component_swizzle[4] {};
|
||||
GLenum component_swizzle[4]{};
|
||||
|
||||
std::unordered_map<GLenum, std::unique_ptr<texture_view>> m_subviews;
|
||||
texture_view* m_root_view = nullptr;
|
||||
|
||||
texture_view() = default;
|
||||
|
||||
|
|
@ -395,6 +398,8 @@ namespace gl
|
|||
|
||||
virtual ~texture_view();
|
||||
|
||||
texture_view* as(GLenum format);
|
||||
|
||||
GLuint id() const
|
||||
{
|
||||
return m_id;
|
||||
|
|
|
|||
|
|
@ -82,11 +82,34 @@ namespace gl
|
|||
{
|
||||
// NOTE: In OpenGL, the border texels are processed by the pipeline and will be swizzled by the texture view.
|
||||
// Therefore, we pass the raw value here, and the texture view will handle the rest for us.
|
||||
const auto encoded_color = tex.border_color();
|
||||
if (get_parameteri(GL_TEXTURE_BORDER_COLOR) != encoded_color)
|
||||
const bool sext_conv_required = (sampled_image->format_ex.texel_remap_control & rsx::SEXT_MASK) != 0;
|
||||
const auto encoded_color = tex.border_color(sext_conv_required);
|
||||
const auto host_features = sampled_image->format_ex.host_features;
|
||||
|
||||
if (get_parameteri(GL_TEXTURE_BORDER_COLOR) != encoded_color ||
|
||||
get_parameteri(GL_TEXTURE_BORDER_VALUES_NV) != host_features)
|
||||
{
|
||||
m_propertiesi[GL_TEXTURE_BORDER_COLOR] = encoded_color;
|
||||
const auto border_color = rsx::decode_border_color(encoded_color);
|
||||
m_propertiesi[GL_TEXTURE_BORDER_VALUES_NV] = host_features;
|
||||
|
||||
auto border_color = rsx::decode_border_color(encoded_color);
|
||||
if (sampled_image->format_ex.host_snorm_format_active()) [[ unlikely ]]
|
||||
{
|
||||
// Hardware SNORM is active
|
||||
// Convert the border color in host space (2N - 1)
|
||||
// HW does the conversion in integer space as (x - 128) / 127 which introduces a biasing error.
|
||||
const float bias_v = 128.f / 255.f;
|
||||
const float scale_v = 255.f / 127.f;
|
||||
|
||||
color4f scale{ 1.f }, bias{ 0.f };
|
||||
const auto snorm_mask = tex.argb_signed();
|
||||
if (snorm_mask & 1) { scale.a = scale_v; bias.a = -bias_v; }
|
||||
if (snorm_mask & 2) { scale.r = scale_v; bias.r = -bias_v; }
|
||||
if (snorm_mask & 4) { scale.g = scale_v; bias.g = -bias_v; }
|
||||
if (snorm_mask & 8) { scale.b = scale_v; bias.b = -bias_v; }
|
||||
border_color = (border_color + bias) * scale;
|
||||
}
|
||||
|
||||
glSamplerParameterfv(sampler_handle, GL_TEXTURE_BORDER_COLOR, border_color.rgba);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
extern atomic_t<bool> g_user_asked_for_recording;
|
||||
|
||||
atomic_t<bool> g_user_asked_for_fullscreen = false;
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace overlays
|
||||
|
|
@ -80,7 +82,7 @@ namespace rsx
|
|||
if (btn != pad_button::cross) return page_navigation::stay;
|
||||
|
||||
rsx_log.notice("User selected trophies in home menu");
|
||||
Emu.CallFromMainThread([trop_name = std::move(trop_name)]()
|
||||
Emu.CallFromMainThread([trop_name]()
|
||||
{
|
||||
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
|
||||
{
|
||||
|
|
@ -110,6 +112,17 @@ namespace rsx
|
|||
return page_navigation::exit;
|
||||
});
|
||||
|
||||
std::unique_ptr<overlay_element> fullscreen = std::make_unique<home_menu_entry>(get_localized_string(localized_string_id::HOME_MENU_TOGGLE_FULLSCREEN));
|
||||
add_item(fullscreen, [](pad_button btn) -> page_navigation
|
||||
{
|
||||
if (btn != pad_button::cross)
|
||||
return page_navigation::stay;
|
||||
|
||||
rsx_log.notice("User selected toggle fullscreen in home menu");
|
||||
g_user_asked_for_fullscreen = true;
|
||||
return page_navigation::stay; // No need to exit
|
||||
});
|
||||
|
||||
add_page(std::make_shared<home_menu_savestate>(x, y, width, height, use_separators, this));
|
||||
|
||||
std::unique_ptr<overlay_element> restart = std::make_unique<home_menu_entry>(get_localized_string(localized_string_id::HOME_MENU_RESTART));
|
||||
|
|
|
|||
|
|
@ -215,12 +215,24 @@ namespace rsx
|
|||
case pad_button::dpad_up:
|
||||
case pad_button::ls_up:
|
||||
{
|
||||
if (!is_auto_repeat && get_selected_index() <= 0)
|
||||
{
|
||||
select_entry(get_elements_count() - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
select_previous();
|
||||
break;
|
||||
}
|
||||
case pad_button::dpad_down:
|
||||
case pad_button::ls_down:
|
||||
{
|
||||
if (!is_auto_repeat && get_selected_index() >= (get_elements_count() - 1))
|
||||
{
|
||||
select_entry(0);
|
||||
break;
|
||||
}
|
||||
|
||||
select_next();
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,12 @@ namespace rsx
|
|||
update_selection();
|
||||
}
|
||||
|
||||
int list_view::get_selected_index() const
|
||||
u16 list_view::get_elements_count() const
|
||||
{
|
||||
return m_elements_count;
|
||||
}
|
||||
|
||||
s32 list_view::get_selected_index() const
|
||||
{
|
||||
return m_selected_entry;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ namespace rsx
|
|||
|
||||
void add_entry(std::unique_ptr<overlay_element>& entry);
|
||||
|
||||
int get_selected_index() const;
|
||||
u16 get_elements_count() const;
|
||||
s32 get_selected_index() const;
|
||||
bool get_cancel_only() const;
|
||||
const overlay_element* get_selected_entry() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -408,6 +408,16 @@ namespace glsl
|
|||
enabled_options.push_back("_ENABLE_TEXTURE_ALPHA_KILL");
|
||||
}
|
||||
|
||||
if (props.require_color_format_convert)
|
||||
{
|
||||
enabled_options.push_back("_ENABLE_FORMAT_CONVERSION");
|
||||
}
|
||||
|
||||
if (props.require_depth_conversion)
|
||||
{
|
||||
enabled_options.push_back("_ENABLE_DEPTH_FORMAT_RECONSTRUCTION");
|
||||
}
|
||||
|
||||
program_common::define_glsl_switches(OS, enabled_options);
|
||||
enabled_options.clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,52 +4,12 @@
|
|||
|
||||
#include "GLSLTypes.h"
|
||||
#include "ShaderParam.h"
|
||||
#include "../color_utils.h"
|
||||
|
||||
struct RSXFragmentProgram;
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
// TODO: Move this somewhere else once more compilers are supported other than glsl
|
||||
enum texture_control_bits
|
||||
{
|
||||
GAMMA_A = 0,
|
||||
GAMMA_R,
|
||||
GAMMA_G,
|
||||
GAMMA_B,
|
||||
ALPHAKILL,
|
||||
RENORMALIZE,
|
||||
EXPAND_A,
|
||||
EXPAND_R,
|
||||
EXPAND_G,
|
||||
EXPAND_B,
|
||||
SEXT_A,
|
||||
SEXT_R,
|
||||
SEXT_G,
|
||||
SEXT_B,
|
||||
DEPTH_FLOAT,
|
||||
DEPTH_COMPARE_OP,
|
||||
DEPTH_COMPARE_1,
|
||||
DEPTH_COMPARE_2,
|
||||
FILTERED_MAG,
|
||||
FILTERED_MIN,
|
||||
UNNORMALIZED_COORDS,
|
||||
CLAMP_TEXCOORDS_BIT,
|
||||
WRAP_S,
|
||||
WRAP_T,
|
||||
WRAP_R,
|
||||
FF_SIGNED_BIT,
|
||||
FF_BIASED_RENORM_BIT,
|
||||
FF_GAMMA_BIT,
|
||||
FF_16BIT_CHANNELS_BIT,
|
||||
|
||||
GAMMA_CTRL_MASK = (1 << GAMMA_R) | (1 << GAMMA_G) | (1 << GAMMA_B) | (1 << GAMMA_A),
|
||||
EXPAND_MASK = (1 << EXPAND_R) | (1 << EXPAND_G) | (1 << EXPAND_B) | (1 << EXPAND_A),
|
||||
EXPAND_OFFSET = EXPAND_A,
|
||||
SEXT_MASK = (1 << SEXT_R) | (1 << SEXT_G) | (1 << SEXT_B) | (1 << SEXT_A),
|
||||
SEXT_OFFSET = SEXT_A,
|
||||
FORMAT_FEATURES_OFFSET = FF_SIGNED_BIT,
|
||||
};
|
||||
|
||||
enum ROP_control_bits : u32
|
||||
{
|
||||
// Commands. These trigger explicit action.
|
||||
|
|
|
|||
|
|
@ -46,8 +46,10 @@ R"(
|
|||
_texture_bx2_active = false; \
|
||||
} while (false)
|
||||
#define TEX_FLAGS(index) ((TEX_PARAM(index).flags & ~(_texture_flag_erase)) | _texture_flag_override)
|
||||
#else
|
||||
#elif defined(_ENABLE_TEXTURE_ALPHA_KILL) || defined(_ENABLE_FORMAT_CONVERSION) || defined(_ENABLE_DEPTH_FORMAT_RECONSTRUCTION)
|
||||
#define TEX_FLAGS(index) (TEX_PARAM(index).flags)
|
||||
#else
|
||||
#define TEX_FLAGS(index) 0
|
||||
#endif
|
||||
|
||||
#define TEX_NAME(index) tex##index
|
||||
|
|
@ -193,6 +195,8 @@ vec4 _texcoord_xform_shadow(const in vec4 coord4, const in sampler_info params)
|
|||
|
||||
#endif // _EMULATE_SHADOW
|
||||
|
||||
#ifdef _ENABLE_FORMAT_CONVERSION
|
||||
|
||||
vec4 _sext_unorm8x4(const in vec4 x)
|
||||
{
|
||||
// TODO: Handle clamped sign-extension
|
||||
|
|
@ -285,4 +289,27 @@ vec4 _process_texel(in vec4 rgba, const in uint control_bits)
|
|||
return rgba;
|
||||
}
|
||||
|
||||
#elif defined(_ENABLE_TEXTURE_ALPHA_KILL)
|
||||
|
||||
vec4 _process_texel(in vec4 rgba, const in uint control_bits)
|
||||
{
|
||||
if (_test_bit(control_bits, ALPHAKILL))
|
||||
{
|
||||
// Alphakill
|
||||
if (rgba.a < 0.000001)
|
||||
{
|
||||
_kill();
|
||||
return rgba;
|
||||
}
|
||||
}
|
||||
|
||||
return rgba;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define _process_texel(rgba, control) rgba
|
||||
|
||||
#endif // _ENABLE_FORMAT_CONVERSION
|
||||
|
||||
)"
|
||||
|
|
|
|||
|
|
@ -60,5 +60,6 @@ namespace glsl
|
|||
bool require_tex3D_ops : 1; // Include 3D texture stuff (including cubemap)
|
||||
bool require_shadowProj_ops : 1; // Include shadow2DProj projection textures (1D is unsupported anyway)
|
||||
bool require_alpha_kill : 1; // Include alpha kill checking code
|
||||
bool require_color_format_convert : 1; // Include colorspace conversion code
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -651,7 +651,7 @@ fragment_program_utils::fragment_program_metadata fragment_program_utils::analys
|
|||
case RSX_FP_OPCODE_TXB:
|
||||
case RSX_FP_OPCODE_TXL:
|
||||
result.referenced_textures_mask |= (1 << d0.tex_num);
|
||||
result.has_tex_bx2_conv |= !!d0.exp_tex;
|
||||
result.bx2_texture_reads_mask |= ((d0.exp_tex ? 1u : 0u) << d0.tex_num);
|
||||
break;
|
||||
case RSX_FP_OPCODE_PK4:
|
||||
case RSX_FP_OPCODE_UP4:
|
||||
|
|
|
|||
|
|
@ -56,10 +56,10 @@ namespace program_hash_util
|
|||
u32 program_ucode_length;
|
||||
u32 program_constants_buffer_length;
|
||||
u16 referenced_textures_mask;
|
||||
u16 bx2_texture_reads_mask;
|
||||
|
||||
bool has_pack_instructions;
|
||||
bool has_branch_instructions;
|
||||
bool has_tex_bx2_conv;
|
||||
bool is_nop_shader; // Does this affect Z-pass testing???
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ namespace rsx
|
|||
texture_format_ex result { format_bits };
|
||||
result.features = format_features;
|
||||
result.texel_remap_control = format_convert;
|
||||
result.encoded_remap = remap();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -364,11 +365,15 @@ namespace rsx
|
|||
return dimension() != rsx::texture_dimension::dimension1d ? ((registers[NV4097_SET_TEXTURE_IMAGE_RECT + (m_index * 8)]) & 0xffff) : 1;
|
||||
}
|
||||
|
||||
u32 fragment_texture::border_color() const
|
||||
u32 fragment_texture::border_color(bool apply_colorspace_remapping) const
|
||||
{
|
||||
const u32 raw = registers[NV4097_SET_TEXTURE_BORDER_COLOR + (m_index * 8)];
|
||||
const u32 sext = argb_signed();
|
||||
if (!apply_colorspace_remapping) [[ likely ]]
|
||||
{
|
||||
return raw;
|
||||
}
|
||||
|
||||
const u32 sext = argb_signed();
|
||||
if (!sext) [[ likely ]]
|
||||
{
|
||||
return raw;
|
||||
|
|
@ -430,9 +435,9 @@ namespace rsx
|
|||
return (conv & mask) | (raw & ~mask);
|
||||
}
|
||||
|
||||
color4f fragment_texture::remapped_border_color() const
|
||||
color4f fragment_texture::remapped_border_color(bool apply_colorspace_remapping) const
|
||||
{
|
||||
color4f base_color = rsx::decode_border_color(border_color());
|
||||
color4f base_color = rsx::decode_border_color(border_color(apply_colorspace_remapping));
|
||||
if (remap() == RSX_TEXTURE_REMAP_IDENTITY)
|
||||
{
|
||||
return base_color;
|
||||
|
|
@ -571,14 +576,14 @@ namespace rsx
|
|||
return dimension() != rsx::texture_dimension::dimension1d ? ((registers[NV4097_SET_VERTEX_TEXTURE_IMAGE_RECT + (m_index * 8)]) & 0xffff) : 1;
|
||||
}
|
||||
|
||||
u32 vertex_texture::border_color() const
|
||||
u32 vertex_texture::border_color(bool) const
|
||||
{
|
||||
return registers[NV4097_SET_VERTEX_TEXTURE_BORDER_COLOR + (m_index * 8)];
|
||||
}
|
||||
|
||||
color4f vertex_texture::remapped_border_color() const
|
||||
color4f vertex_texture::remapped_border_color(bool) const
|
||||
{
|
||||
return rsx::decode_border_color(border_color());
|
||||
return rsx::decode_border_color(border_color(false));
|
||||
}
|
||||
|
||||
u16 vertex_texture::depth() const
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ namespace rsx
|
|||
u16 height() const;
|
||||
|
||||
// Border Color
|
||||
u32 border_color() const;
|
||||
color4f remapped_border_color() const;
|
||||
u32 border_color(bool apply_colorspace_remapping = false) const;
|
||||
color4f remapped_border_color(bool apply_colorspace_remapping = false) const;
|
||||
|
||||
u16 depth() const;
|
||||
u32 pitch() const;
|
||||
|
|
@ -136,8 +136,8 @@ namespace rsx
|
|||
u16 height() const;
|
||||
|
||||
// Border Color
|
||||
u32 border_color() const;
|
||||
color4f remapped_border_color() const;
|
||||
u32 border_color(bool = false) const;
|
||||
color4f remapped_border_color(bool = false) const;
|
||||
|
||||
u16 depth() const;
|
||||
u32 pitch() const;
|
||||
|
|
|
|||
|
|
@ -2315,6 +2315,7 @@ namespace rsx
|
|||
case CELL_GCM_TEXTURE_R5G6B5:
|
||||
case CELL_GCM_TEXTURE_R6G5B5:
|
||||
texture_control |= (1 << texture_control_bits::RENORMALIZE);
|
||||
current_fragment_program.ctrl |= RSX_SHADER_CONTROL_TEXTURE_FORMAT_CONVERT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -2326,8 +2327,15 @@ namespace rsx
|
|||
texture_control |= format_ex.texel_remap_control;
|
||||
texture_control |= format_ex.features << texture_control_bits::FORMAT_FEATURES_OFFSET;
|
||||
|
||||
if (current_fp_metadata.has_tex_bx2_conv)
|
||||
if (format_ex.texel_remap_control)
|
||||
{
|
||||
current_fragment_program.ctrl |= RSX_SHADER_CONTROL_TEXTURE_FORMAT_CONVERT;
|
||||
}
|
||||
|
||||
if (current_fp_metadata.bx2_texture_reads_mask)
|
||||
{
|
||||
current_fragment_program.ctrl |= RSX_SHADER_CONTROL_TEXTURE_FORMAT_CONVERT;
|
||||
|
||||
const u32 remap_hi = tex.decoded_remap().shuffle_mask_bits(0xFu);
|
||||
current_fragment_program.texture_params[i].remap &= ~(0xFu << 16u);
|
||||
current_fragment_program.texture_params[i].remap |= (remap_hi << 16u);
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ void VKGSRender::load_texture_env()
|
|||
{
|
||||
// Load textures
|
||||
bool check_for_cyclic_refs = false;
|
||||
auto check_surface_cache_sampler = [&](auto descriptor, const auto& tex)
|
||||
auto check_surface_cache_sampler_valid = [&](auto descriptor, const auto& tex)
|
||||
{
|
||||
if (!m_texture_cache.test_if_descriptor_expired(*m_current_command_buffer, m_rtts, descriptor, tex))
|
||||
{
|
||||
|
|
@ -274,11 +274,11 @@ void VKGSRender::load_texture_env()
|
|||
return false;
|
||||
};
|
||||
|
||||
auto get_border_color = [&](const rsx::Texture auto& tex)
|
||||
auto get_border_color = [&](const rsx::Texture auto& tex, bool remap_colorspace)
|
||||
{
|
||||
return m_device->get_custom_border_color_support().require_border_color_remap
|
||||
? tex.remapped_border_color()
|
||||
: rsx::decode_border_color(tex.border_color());
|
||||
? tex.remapped_border_color(remap_colorspace)
|
||||
: rsx::decode_border_color(tex.border_color(remap_colorspace));
|
||||
};
|
||||
|
||||
std::lock_guard lock(m_sampler_mutex);
|
||||
|
|
@ -286,242 +286,319 @@ void VKGSRender::load_texture_env()
|
|||
for (u32 textures_ref = current_fp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fs_sampler_state[i])
|
||||
{
|
||||
fs_sampler_state[i] = std::make_unique<vk::texture_cache::sampled_image_descriptor>();
|
||||
}
|
||||
|
||||
auto sampler_state = static_cast<vk::texture_cache::sampled_image_descriptor*>(fs_sampler_state[i].get());
|
||||
const auto& tex = rsx::method_registers.fragment_textures[i];
|
||||
const auto previous_format_class = fs_sampler_state[i]->format_class;
|
||||
|
||||
if (m_samplers_dirty || m_textures_dirty[i] || !check_surface_cache_sampler(sampler_state, tex))
|
||||
if (!m_samplers_dirty &&
|
||||
!m_textures_dirty[i] &&
|
||||
check_surface_cache_sampler_valid(sampler_state, tex))
|
||||
{
|
||||
if (tex.enabled())
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool is_sampler_dirty = m_textures_dirty[i];
|
||||
m_textures_dirty[i] = false;
|
||||
|
||||
if (!tex.enabled())
|
||||
{
|
||||
*sampler_state = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
*sampler_state = m_texture_cache.upload_texture(*m_current_command_buffer, tex, m_rtts);
|
||||
if (!sampler_state->validate())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sampler_state->is_cyclic_reference)
|
||||
{
|
||||
check_for_cyclic_refs |= true;
|
||||
}
|
||||
|
||||
if (!is_sampler_dirty)
|
||||
{
|
||||
if (sampler_state->format_class != previous_format_class)
|
||||
{
|
||||
*sampler_state = m_texture_cache.upload_texture(*m_current_command_buffer, tex, m_rtts);
|
||||
// Host details changed but RSX is not aware
|
||||
m_graphics_state |= rsx::fragment_program_state_dirty;
|
||||
}
|
||||
|
||||
if (sampler_state->format_ex)
|
||||
{
|
||||
// Nothing to change, use cached sampler
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sampler_state->format_ex = tex.format_ex();
|
||||
|
||||
if (sampler_state->format_ex.texel_remap_control &&
|
||||
sampler_state->image_handle &&
|
||||
sampler_state->upload_context == rsx::texture_upload_context::shader_read &&
|
||||
(current_fp_metadata.bx2_texture_reads_mask & (1u << i)) == 0 &&
|
||||
!g_cfg.video.disable_hardware_texel_remapping) [[ unlikely ]]
|
||||
{
|
||||
// Check if we need to override the view format
|
||||
const auto vk_format = sampler_state->image_handle->format();
|
||||
VkFormat format_override = vk_format;;
|
||||
rsx::flags32_t flags_to_erase = 0u;
|
||||
rsx::flags32_t host_flags_to_set = 0u;
|
||||
|
||||
if (sampler_state->format_ex.hw_SNORM_possible())
|
||||
{
|
||||
format_override = vk::get_compatible_snorm_format(vk_format);
|
||||
flags_to_erase = rsx::texture_control_bits::SEXT_MASK;
|
||||
host_flags_to_set = rsx::RSX_HOST_FORMAT_FEATURE_SNORM;
|
||||
}
|
||||
else if (sampler_state->format_ex.hw_SRGB_possible())
|
||||
{
|
||||
format_override = vk::get_compatible_srgb_format(vk_format);
|
||||
flags_to_erase = rsx::texture_control_bits::GAMMA_CTRL_MASK;
|
||||
host_flags_to_set = rsx::RSX_HOST_FORMAT_FEATURE_SRGB;
|
||||
}
|
||||
|
||||
if (format_override != VK_FORMAT_UNDEFINED && format_override != vk_format)
|
||||
{
|
||||
sampler_state->image_handle = sampler_state->image_handle->as(format_override);
|
||||
sampler_state->format_ex.texel_remap_control &= (~flags_to_erase);
|
||||
sampler_state->format_ex.host_features |= host_flags_to_set;
|
||||
}
|
||||
}
|
||||
|
||||
VkFilter mag_filter;
|
||||
vk::minification_filter min_filter;
|
||||
f32 min_lod = 0.f, max_lod = 0.f;
|
||||
f32 lod_bias = 0.f;
|
||||
|
||||
const u32 texture_format = sampler_state->format_ex.format();
|
||||
VkBool32 compare_enabled = VK_FALSE;
|
||||
VkCompareOp depth_compare_mode = VK_COMPARE_OP_NEVER;
|
||||
|
||||
if (texture_format >= CELL_GCM_TEXTURE_DEPTH24_D8 && texture_format <= CELL_GCM_TEXTURE_DEPTH16_FLOAT)
|
||||
{
|
||||
compare_enabled = VK_TRUE;
|
||||
depth_compare_mode = vk::get_compare_func(tex.zfunc(), true);
|
||||
}
|
||||
|
||||
const f32 af_level = vk::max_aniso(tex.max_aniso());
|
||||
const auto wrap_s = vk::vk_wrap_mode(tex.wrap_s());
|
||||
const auto wrap_t = vk::vk_wrap_mode(tex.wrap_t());
|
||||
const auto wrap_r = vk::vk_wrap_mode(tex.wrap_r());
|
||||
|
||||
// NOTE: In vulkan, the border color can bypass the sample swizzle stage.
|
||||
// Check the device properties to determine whether to pre-swizzle the colors or not.
|
||||
const bool sext_conv_required = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) != 0;
|
||||
vk::border_color_t border_color(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
|
||||
|
||||
if (rsx::is_border_clamped_texture(tex))
|
||||
{
|
||||
auto color_value = get_border_color(tex, sext_conv_required);
|
||||
if (sampler_state->format_ex.host_snorm_format_active())
|
||||
{
|
||||
// Convert the border color in host space (2N - 1)
|
||||
// HW does the conversion in integer space as (x - 128) / 127 which introduces a biasing error.
|
||||
const float bias_v = 128.f / 255.f;
|
||||
const float scale_v = 255.f / 127.f;
|
||||
|
||||
color4f scale{ 1.f }, bias{ 0.f };
|
||||
const auto snorm_mask = tex.argb_signed();
|
||||
if (snorm_mask & 1) { scale.a = scale_v; bias.a = -bias_v; }
|
||||
if (snorm_mask & 2) { scale.r = scale_v; bias.r = -bias_v; }
|
||||
if (snorm_mask & 4) { scale.g = scale_v; bias.g = -bias_v; }
|
||||
if (snorm_mask & 8) { scale.b = scale_v; bias.b = -bias_v; }
|
||||
color_value = (color_value + bias) * scale;
|
||||
}
|
||||
|
||||
border_color = color_value;
|
||||
}
|
||||
|
||||
// Check if non-point filtering can even be used on this format
|
||||
bool can_sample_linear;
|
||||
if (sampler_state->format_class == RSX_FORMAT_CLASS_COLOR) [[likely]]
|
||||
{
|
||||
// Most PS3-like formats can be linearly filtered without problem
|
||||
// Exclude textures that require SNORM conversion however
|
||||
can_sample_linear = !sext_conv_required;
|
||||
}
|
||||
else if (sampler_state->format_class != rsx::classify_format(texture_format) &&
|
||||
(texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8))
|
||||
{
|
||||
// Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking
|
||||
can_sample_linear = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not all GPUs support linear filtering of depth formats
|
||||
const auto vk_format = sampler_state->image_handle ? sampler_state->image_handle->image()->format() :
|
||||
vk::get_compatible_sampler_format(m_device->get_formats_support(), sampler_state->external_subresource_desc.gcm_format);
|
||||
|
||||
can_sample_linear = m_device->get_format_properties(vk_format).optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
|
||||
}
|
||||
|
||||
const auto mipmap_count = tex.get_exact_mipmap_count();
|
||||
min_filter = vk::get_min_filter(tex.min_filter());
|
||||
|
||||
if (can_sample_linear)
|
||||
{
|
||||
mag_filter = vk::get_mag_filter(tex.mag_filter());
|
||||
}
|
||||
else
|
||||
{
|
||||
mag_filter = VK_FILTER_NEAREST;
|
||||
min_filter.filter = VK_FILTER_NEAREST;
|
||||
}
|
||||
|
||||
if (min_filter.sample_mipmaps && mipmap_count > 1)
|
||||
{
|
||||
f32 actual_mipmaps;
|
||||
if (sampler_state->upload_context == rsx::texture_upload_context::shader_read)
|
||||
{
|
||||
actual_mipmaps = static_cast<f32>(mipmap_count);
|
||||
}
|
||||
else if (sampler_state->external_subresource_desc.op == rsx::deferred_request_command::mipmap_gather)
|
||||
{
|
||||
// Clamp min and max lod
|
||||
actual_mipmaps = static_cast<f32>(sampler_state->external_subresource_desc.sections_to_copy.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
*sampler_state = {};
|
||||
actual_mipmaps = 1.f;
|
||||
}
|
||||
|
||||
if (sampler_state->validate())
|
||||
if (actual_mipmaps > 1.f)
|
||||
{
|
||||
sampler_state->format_ex = tex.format_ex();
|
||||
min_lod = tex.min_lod();
|
||||
max_lod = tex.max_lod();
|
||||
lod_bias = tex.bias();
|
||||
|
||||
if (sampler_state->is_cyclic_reference)
|
||||
min_lod = std::min(min_lod, actual_mipmaps - 1.f);
|
||||
max_lod = std::min(max_lod, actual_mipmaps - 1.f);
|
||||
|
||||
if (min_filter.mipmap_mode == VK_SAMPLER_MIPMAP_MODE_NEAREST)
|
||||
{
|
||||
check_for_cyclic_refs |= true;
|
||||
}
|
||||
|
||||
if (!m_textures_dirty[i] && sampler_state->format_class != previous_format_class)
|
||||
{
|
||||
// Host details changed but RSX is not aware
|
||||
m_graphics_state |= rsx::fragment_program_state_dirty;
|
||||
}
|
||||
|
||||
bool replace = !fs_sampler_handles[i];
|
||||
VkFilter mag_filter;
|
||||
vk::minification_filter min_filter;
|
||||
f32 min_lod = 0.f, max_lod = 0.f;
|
||||
f32 lod_bias = 0.f;
|
||||
|
||||
const u32 texture_format = sampler_state->format_ex.format();
|
||||
VkBool32 compare_enabled = VK_FALSE;
|
||||
VkCompareOp depth_compare_mode = VK_COMPARE_OP_NEVER;
|
||||
|
||||
if (texture_format >= CELL_GCM_TEXTURE_DEPTH24_D8 && texture_format <= CELL_GCM_TEXTURE_DEPTH16_FLOAT)
|
||||
{
|
||||
compare_enabled = VK_TRUE;
|
||||
depth_compare_mode = vk::get_compare_func(tex.zfunc(), true);
|
||||
}
|
||||
|
||||
const f32 af_level = vk::max_aniso(tex.max_aniso());
|
||||
const auto wrap_s = vk::vk_wrap_mode(tex.wrap_s());
|
||||
const auto wrap_t = vk::vk_wrap_mode(tex.wrap_t());
|
||||
const auto wrap_r = vk::vk_wrap_mode(tex.wrap_r());
|
||||
|
||||
// NOTE: In vulkan, the border color can bypass the sample swizzle stage.
|
||||
// Check the device properties to determine whether to pre-swizzle the colors or not.
|
||||
const auto border_color = rsx::is_border_clamped_texture(tex)
|
||||
? vk::border_color_t(get_border_color(tex))
|
||||
: vk::border_color_t(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
|
||||
|
||||
// Check if non-point filtering can even be used on this format
|
||||
bool can_sample_linear;
|
||||
if (sampler_state->format_class == RSX_FORMAT_CLASS_COLOR) [[likely]]
|
||||
{
|
||||
// Most PS3-like formats can be linearly filtered without problem
|
||||
// Exclude textures that require SNORM conversion however
|
||||
can_sample_linear = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) == 0;
|
||||
}
|
||||
else if (sampler_state->format_class != rsx::classify_format(texture_format) &&
|
||||
(texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8))
|
||||
{
|
||||
// Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking
|
||||
can_sample_linear = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not all GPUs support linear filtering of depth formats
|
||||
const auto vk_format = sampler_state->image_handle ? sampler_state->image_handle->image()->format() :
|
||||
vk::get_compatible_sampler_format(m_device->get_formats_support(), sampler_state->external_subresource_desc.gcm_format);
|
||||
|
||||
can_sample_linear = m_device->get_format_properties(vk_format).optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
|
||||
}
|
||||
|
||||
const auto mipmap_count = tex.get_exact_mipmap_count();
|
||||
min_filter = vk::get_min_filter(tex.min_filter());
|
||||
|
||||
if (can_sample_linear)
|
||||
{
|
||||
mag_filter = vk::get_mag_filter(tex.mag_filter());
|
||||
}
|
||||
else
|
||||
{
|
||||
mag_filter = VK_FILTER_NEAREST;
|
||||
min_filter.filter = VK_FILTER_NEAREST;
|
||||
}
|
||||
|
||||
if (min_filter.sample_mipmaps && mipmap_count > 1)
|
||||
{
|
||||
f32 actual_mipmaps;
|
||||
if (sampler_state->upload_context == rsx::texture_upload_context::shader_read)
|
||||
{
|
||||
actual_mipmaps = static_cast<f32>(mipmap_count);
|
||||
}
|
||||
else if (sampler_state->external_subresource_desc.op == rsx::deferred_request_command::mipmap_gather)
|
||||
{
|
||||
// Clamp min and max lod
|
||||
actual_mipmaps = static_cast<f32>(sampler_state->external_subresource_desc.sections_to_copy.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
actual_mipmaps = 1.f;
|
||||
}
|
||||
|
||||
if (actual_mipmaps > 1.f)
|
||||
{
|
||||
min_lod = tex.min_lod();
|
||||
max_lod = tex.max_lod();
|
||||
lod_bias = tex.bias();
|
||||
|
||||
min_lod = std::min(min_lod, actual_mipmaps - 1.f);
|
||||
max_lod = std::min(max_lod, actual_mipmaps - 1.f);
|
||||
|
||||
if (min_filter.mipmap_mode == VK_SAMPLER_MIPMAP_MODE_NEAREST)
|
||||
{
|
||||
// Round to nearest 0.5 to work around some broken games
|
||||
// Unlike openGL, sampler parameters cannot be dynamically changed on vulkan, leading to many permutations
|
||||
lod_bias = std::floor(lod_bias * 2.f + 0.5f) * 0.5f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
min_lod = max_lod = lod_bias = 0.f;
|
||||
min_filter.mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs_sampler_handles[i] && m_textures_dirty[i])
|
||||
{
|
||||
if (!fs_sampler_handles[i]->matches(wrap_s, wrap_t, wrap_r, false, lod_bias, af_level, min_lod, max_lod,
|
||||
min_filter.filter, mag_filter, min_filter.mipmap_mode, border_color, compare_enabled, depth_compare_mode))
|
||||
{
|
||||
replace = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (replace)
|
||||
{
|
||||
fs_sampler_handles[i] = vk::get_resource_manager()->get_sampler(
|
||||
*m_device,
|
||||
fs_sampler_handles[i],
|
||||
wrap_s, wrap_t, wrap_r,
|
||||
false,
|
||||
lod_bias, af_level, min_lod, max_lod,
|
||||
min_filter.filter, mag_filter, min_filter.mipmap_mode,
|
||||
border_color, compare_enabled, depth_compare_mode);
|
||||
// Round to nearest 0.5 to work around some broken games
|
||||
// Unlike openGL, sampler parameters cannot be dynamically changed on vulkan, leading to many permutations
|
||||
lod_bias = std::floor(lod_bias * 2.f + 0.5f) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
m_textures_dirty[i] = false;
|
||||
else
|
||||
{
|
||||
min_lod = max_lod = lod_bias = 0.f;
|
||||
min_filter.mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs_sampler_handles[i] &&
|
||||
fs_sampler_handles[i]->matches(wrap_s, wrap_t, wrap_r, false, lod_bias, af_level, min_lod, max_lod,
|
||||
min_filter.filter, mag_filter, min_filter.mipmap_mode, border_color, compare_enabled, depth_compare_mode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fs_sampler_handles[i] = vk::get_resource_manager()->get_sampler(
|
||||
*m_device,
|
||||
fs_sampler_handles[i],
|
||||
wrap_s, wrap_t, wrap_r,
|
||||
false,
|
||||
lod_bias, af_level, min_lod, max_lod,
|
||||
min_filter.filter, mag_filter, min_filter.mipmap_mode,
|
||||
border_color, compare_enabled, depth_compare_mode);
|
||||
}
|
||||
|
||||
for (u32 textures_ref = current_vp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!vs_sampler_state[i])
|
||||
{
|
||||
vs_sampler_state[i] = std::make_unique<vk::texture_cache::sampled_image_descriptor>();
|
||||
}
|
||||
|
||||
auto sampler_state = static_cast<vk::texture_cache::sampled_image_descriptor*>(vs_sampler_state[i].get());
|
||||
const auto& tex = rsx::method_registers.vertex_textures[i];
|
||||
const auto previous_format_class = sampler_state->format_class;
|
||||
|
||||
if (m_samplers_dirty || m_vertex_textures_dirty[i] || !check_surface_cache_sampler(sampler_state, tex))
|
||||
if (!m_samplers_dirty &&
|
||||
!m_vertex_textures_dirty[i] &&
|
||||
check_surface_cache_sampler_valid(sampler_state, tex))
|
||||
{
|
||||
if (rsx::method_registers.vertex_textures[i].enabled())
|
||||
{
|
||||
*sampler_state = m_texture_cache.upload_texture(*m_current_command_buffer, tex, m_rtts);
|
||||
}
|
||||
else
|
||||
{
|
||||
*sampler_state = {};
|
||||
}
|
||||
|
||||
if (sampler_state->validate())
|
||||
{
|
||||
if (sampler_state->is_cyclic_reference || sampler_state->external_subresource_desc.do_not_cache)
|
||||
{
|
||||
check_for_cyclic_refs |= true;
|
||||
}
|
||||
|
||||
if (!m_vertex_textures_dirty[i] && sampler_state->format_class != previous_format_class)
|
||||
{
|
||||
// Host details changed but RSX is not aware
|
||||
m_graphics_state |= rsx::vertex_program_state_dirty;
|
||||
}
|
||||
|
||||
bool replace = !vs_sampler_handles[i];
|
||||
const VkBool32 unnormalized_coords = !!(tex.format() & CELL_GCM_TEXTURE_UN);
|
||||
const auto min_lod = tex.min_lod();
|
||||
const auto max_lod = tex.max_lod();
|
||||
const auto wrap_s = vk::vk_wrap_mode(tex.wrap_s());
|
||||
const auto wrap_t = vk::vk_wrap_mode(tex.wrap_t());
|
||||
|
||||
// NOTE: In vulkan, the border color can bypass the sample swizzle stage.
|
||||
// Check the device properties to determine whether to pre-swizzle the colors or not.
|
||||
const auto border_color = is_border_clamped_texture(tex)
|
||||
? vk::border_color_t(get_border_color(tex))
|
||||
: vk::border_color_t(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
|
||||
|
||||
if (vs_sampler_handles[i])
|
||||
{
|
||||
if (!vs_sampler_handles[i]->matches(wrap_s, wrap_t, VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||
unnormalized_coords, 0.f, 1.f, min_lod, max_lod, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST, border_color))
|
||||
{
|
||||
replace = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (replace)
|
||||
{
|
||||
vs_sampler_handles[i] = vk::get_resource_manager()->get_sampler(
|
||||
*m_device,
|
||||
vs_sampler_handles[i],
|
||||
wrap_s, wrap_t, VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||
unnormalized_coords,
|
||||
0.f, 1.f, min_lod, max_lod,
|
||||
VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST, border_color);
|
||||
}
|
||||
}
|
||||
|
||||
m_vertex_textures_dirty[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool is_sampler_dirty = m_vertex_textures_dirty[i];
|
||||
m_vertex_textures_dirty[i] = false;
|
||||
|
||||
if (!rsx::method_registers.vertex_textures[i].enabled())
|
||||
{
|
||||
*sampler_state = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
*sampler_state = m_texture_cache.upload_texture(*m_current_command_buffer, tex, m_rtts);
|
||||
if (!sampler_state->validate())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sampler_state->is_cyclic_reference || sampler_state->external_subresource_desc.do_not_cache)
|
||||
{
|
||||
check_for_cyclic_refs |= true;
|
||||
}
|
||||
|
||||
if (!is_sampler_dirty)
|
||||
{
|
||||
if (sampler_state->format_class != previous_format_class)
|
||||
{
|
||||
// Host details changed but RSX is not aware
|
||||
m_graphics_state |= rsx::vertex_program_state_dirty;
|
||||
}
|
||||
|
||||
if (vs_sampler_handles[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const VkBool32 unnormalized_coords = !!(tex.format() & CELL_GCM_TEXTURE_UN);
|
||||
const auto min_lod = tex.min_lod();
|
||||
const auto max_lod = tex.max_lod();
|
||||
const auto wrap_s = vk::vk_wrap_mode(tex.wrap_s());
|
||||
const auto wrap_t = vk::vk_wrap_mode(tex.wrap_t());
|
||||
|
||||
// NOTE: In vulkan, the border color can bypass the sample swizzle stage.
|
||||
// Check the device properties to determine whether to pre-swizzle the colors or not.
|
||||
const auto border_color = is_border_clamped_texture(tex)
|
||||
? vk::border_color_t(get_border_color(tex, false))
|
||||
: vk::border_color_t(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
|
||||
|
||||
if (vs_sampler_handles[i] &&
|
||||
vs_sampler_handles[i]->matches(wrap_s, wrap_t, VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||
unnormalized_coords, 0.f, 1.f, min_lod, max_lod, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST, border_color))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
vs_sampler_handles[i] = vk::get_resource_manager()->get_sampler(
|
||||
*m_device,
|
||||
vs_sampler_handles[i],
|
||||
wrap_s, wrap_t, VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||
unnormalized_coords,
|
||||
0.f, 1.f, min_lod, max_lod,
|
||||
VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST, border_color);
|
||||
}
|
||||
|
||||
m_samplers_dirty.store(false);
|
||||
|
|
@ -557,7 +634,9 @@ bool VKGSRender::bind_texture_env()
|
|||
for (u32 textures_ref = current_fp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
vk::image_view* view = nullptr;
|
||||
auto sampler_state = static_cast<vk::texture_cache::sampled_image_descriptor*>(fs_sampler_state[i].get());
|
||||
|
|
@ -627,7 +706,9 @@ bool VKGSRender::bind_texture_env()
|
|||
for (u32 textures_ref = current_vp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rsx::method_registers.vertex_textures[i].enabled())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -244,8 +244,16 @@ namespace vk
|
|||
{
|
||||
switch (rgb_format)
|
||||
{
|
||||
// 8-bit
|
||||
case VK_FORMAT_R8_UNORM:
|
||||
return VK_FORMAT_R8_SRGB;
|
||||
case VK_FORMAT_R8G8_UNORM:
|
||||
return VK_FORMAT_R8G8_SRGB;
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
return VK_FORMAT_R8G8B8A8_SRGB;
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
return VK_FORMAT_B8G8R8A8_SRGB;
|
||||
// DXT
|
||||
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
|
||||
return VK_FORMAT_BC1_RGBA_SRGB_BLOCK;
|
||||
case VK_FORMAT_BC2_UNORM_BLOCK:
|
||||
|
|
@ -253,7 +261,30 @@ namespace vk
|
|||
case VK_FORMAT_BC3_UNORM_BLOCK:
|
||||
return VK_FORMAT_BC3_SRGB_BLOCK;
|
||||
default:
|
||||
return rgb_format;
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
VkFormat get_compatible_snorm_format(VkFormat rgb_format)
|
||||
{
|
||||
switch (rgb_format)
|
||||
{
|
||||
// 8-bit
|
||||
case VK_FORMAT_R8_UNORM:
|
||||
return VK_FORMAT_R8_SNORM;
|
||||
case VK_FORMAT_R8G8_UNORM:
|
||||
return VK_FORMAT_R8G8_SNORM;
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
return VK_FORMAT_R8G8B8A8_SNORM;
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
return VK_FORMAT_B8G8R8A8_SNORM;
|
||||
// 16-bit
|
||||
case VK_FORMAT_R16_UNORM:
|
||||
return VK_FORMAT_R16_SNORM;
|
||||
case VK_FORMAT_R16G16_UNORM:
|
||||
return VK_FORMAT_R16G16_SNORM;
|
||||
default:
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ namespace vk
|
|||
VkFormat get_compatible_depth_surface_format(const gpu_formats_support& support, rsx::surface_depth_format2 format);
|
||||
VkFormat get_compatible_sampler_format(const gpu_formats_support& support, u32 format);
|
||||
VkFormat get_compatible_srgb_format(VkFormat rgb_format);
|
||||
VkFormat get_compatible_snorm_format(VkFormat rgb_format);
|
||||
u8 get_format_texel_width(VkFormat format);
|
||||
std::pair<u8, u8> get_format_element_size(VkFormat format);
|
||||
std::pair<bool, u32> get_format_convert_flags(VkFormat format);
|
||||
|
|
|
|||
|
|
@ -336,6 +336,7 @@ void VKFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS)
|
|||
m_shader_props.require_tex3D_ops = properties.has_tex3D;
|
||||
m_shader_props.require_shadowProj_ops = properties.shadow_sampler_mask != 0 && properties.has_texShadowProj;
|
||||
m_shader_props.require_alpha_kill = !!(m_prog.ctrl & RSX_SHADER_CONTROL_TEXTURE_ALPHA_KILL);
|
||||
m_shader_props.require_color_format_convert = !!(m_prog.ctrl & RSX_SHADER_CONTROL_TEXTURE_FORMAT_CONVERT);
|
||||
|
||||
// Declare global constants
|
||||
if (m_shader_props.require_fog_read)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ namespace vk
|
|||
for (const auto& e : image_list)
|
||||
{
|
||||
const VkImageSubresourceRange subres = { e->aspect(), 0, 1, 0, 1 };
|
||||
image_views.push_back(std::make_unique<vk::image_view>(dev, e, VK_IMAGE_VIEW_TYPE_2D, vk::default_component_map, subres));
|
||||
image_views.push_back(std::make_unique<vk::image_view>(dev, e, e->format(), VK_IMAGE_VIEW_TYPE_2D, vk::default_component_map, subres));
|
||||
}
|
||||
|
||||
auto value = std::make_unique<vk::framebuffer_holder>(dev, renderpass, width, height, std::move(image_views));
|
||||
|
|
|
|||
|
|
@ -1025,6 +1025,29 @@ namespace vk
|
|||
const VkFormat vk_format = get_compatible_sampler_format(m_formats_support, gcm_format);
|
||||
VkImageCreateFlags create_flags = is_cubemap ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0;
|
||||
VkSharingMode sharing_mode = (flags & texture_create_flags::shareable) ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE;
|
||||
rsx::simple_array<VkFormat> mutable_format_list;
|
||||
|
||||
if (flags & texture_create_flags::mutable_format)
|
||||
{
|
||||
const VkFormat snorm_fmt = get_compatible_snorm_format(vk_format);
|
||||
const VkFormat srgb_fmt = get_compatible_srgb_format(vk_format);
|
||||
|
||||
if (snorm_fmt != VK_FORMAT_UNDEFINED)
|
||||
{
|
||||
mutable_format_list.push_back(snorm_fmt);
|
||||
}
|
||||
|
||||
if (srgb_fmt != VK_FORMAT_UNDEFINED)
|
||||
{
|
||||
mutable_format_list.push_back(srgb_fmt);
|
||||
}
|
||||
|
||||
if (!mutable_format_list.empty())
|
||||
{
|
||||
create_flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
mutable_format_list.push_back(vk_format);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto found = find_cached_image(vk_format, width, height, depth, mipmaps, image_type, create_flags, usage_flags, sharing_mode))
|
||||
{
|
||||
|
|
@ -1037,9 +1060,16 @@ namespace vk
|
|||
create_flags |= VK_IMAGE_CREATE_SHAREABLE_RPCS3;
|
||||
}
|
||||
|
||||
VkFormatEx create_format = vk_format;
|
||||
if (!mutable_format_list.empty())
|
||||
{
|
||||
create_format.pViewFormats = mutable_format_list.data();
|
||||
create_format.viewFormatCount = mutable_format_list.size();
|
||||
}
|
||||
|
||||
image = new vk::viewable_image(*m_device,
|
||||
m_memory_types.device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
image_type, vk_format,
|
||||
image_type, create_format,
|
||||
width, height, depth, mipmaps, layer, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_TILING_OPTIMAL, usage_flags, create_flags,
|
||||
VMM_ALLOCATION_POOL_TEXTURE_CACHE, rsx::classify_format(gcm_format));
|
||||
|
|
@ -1123,6 +1153,12 @@ namespace vk
|
|||
const bool upload_async = rsx::get_current_renderer()->get_backend_config().supports_asynchronous_compute;
|
||||
rsx::flags32_t create_flags = 0;
|
||||
|
||||
if (context == rsx::texture_upload_context::shader_read &&
|
||||
!g_cfg.video.disable_hardware_texel_remapping)
|
||||
{
|
||||
create_flags |= texture_create_flags::mutable_format;
|
||||
}
|
||||
|
||||
if (upload_async && ensure(g_fxo->try_get<AsyncTaskScheduler>())->is_host_mode())
|
||||
{
|
||||
create_flags |= texture_create_flags::do_not_reuse;
|
||||
|
|
|
|||
|
|
@ -418,7 +418,8 @@ namespace vk
|
|||
{
|
||||
initialize_image_contents = 1,
|
||||
do_not_reuse = 2,
|
||||
shareable = 4
|
||||
shareable = 4,
|
||||
mutable_format = 8
|
||||
};
|
||||
|
||||
void on_section_destroyed(cached_texture_section& tex) override;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,22 @@ namespace vk
|
|||
VkDescriptorBufferInfoEx() = default;
|
||||
VkDescriptorBufferInfoEx(const vk::buffer& buffer, u64 offset, u64 range);
|
||||
};
|
||||
|
||||
struct VkFormatEx
|
||||
{
|
||||
VkFormat baseFormat = VK_FORMAT_UNDEFINED;
|
||||
VkFormat* pViewFormats = nullptr;
|
||||
u32 viewFormatCount = 0u;
|
||||
|
||||
VkFormatEx() = default;
|
||||
|
||||
VkFormatEx(VkFormat format)
|
||||
: baseFormat(format)
|
||||
{}
|
||||
|
||||
operator VkFormat() const { return baseFormat; }
|
||||
bool is_mutable() const { return viewFormatCount != 0; }
|
||||
};
|
||||
}
|
||||
|
||||
// Re-export
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace vk
|
|||
break;
|
||||
case VK_IMAGE_TYPE_2D:
|
||||
longest_dim = std::max(info.extent.width, info.extent.height);
|
||||
dim_limit = (info.flags == VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) ? gpu_limits.maxImageDimensionCube : gpu_limits.maxImageDimension2D;
|
||||
dim_limit = (info.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) ? gpu_limits.maxImageDimensionCube : gpu_limits.maxImageDimension2D;
|
||||
break;
|
||||
case VK_IMAGE_TYPE_3D:
|
||||
longest_dim = std::max({ info.extent.width, info.extent.height, info.extent.depth });
|
||||
|
|
@ -47,7 +47,7 @@ namespace vk
|
|||
const memory_type_info& memory_type,
|
||||
u32 access_flags,
|
||||
VkImageType image_type,
|
||||
VkFormat format,
|
||||
const VkFormatEx& format,
|
||||
u32 width, u32 height, u32 depth,
|
||||
u32 mipmaps, u32 layers,
|
||||
VkSampleCountFlagBits samples,
|
||||
|
|
@ -59,7 +59,6 @@ namespace vk
|
|||
rsx::format_class format_class)
|
||||
: m_device(dev)
|
||||
{
|
||||
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
info.imageType = image_type;
|
||||
info.format = format;
|
||||
info.extent = { width, height, depth };
|
||||
|
|
@ -84,6 +83,16 @@ namespace vk
|
|||
info.pQueueFamilyIndices = concurrency_queue_families.data();
|
||||
}
|
||||
|
||||
VkImageFormatListCreateInfo format_list = { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO };
|
||||
if (format.is_mutable())
|
||||
{
|
||||
info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
|
||||
format_list.pViewFormats = format.pViewFormats;
|
||||
format_list.viewFormatCount = format.viewFormatCount;
|
||||
info.pNext = &format_list;
|
||||
}
|
||||
|
||||
create_impl(dev, access_flags, memory_type, allocation_pool);
|
||||
m_storage_aspect = get_aspect_flags(format);
|
||||
|
||||
|
|
@ -100,6 +109,7 @@ namespace vk
|
|||
}
|
||||
|
||||
m_format_class = format_class;
|
||||
info.pNext = nullptr;
|
||||
}
|
||||
|
||||
// TODO: Ctor that uses a provided memory heap
|
||||
|
|
@ -333,10 +343,10 @@ namespace vk
|
|||
create_impl();
|
||||
}
|
||||
|
||||
image_view::image_view(VkDevice dev, vk::image* resource, VkImageViewType view_type, const VkComponentMapping& mapping, const VkImageSubresourceRange& range)
|
||||
image_view::image_view(VkDevice dev, vk::image* resource, VkFormat format, VkImageViewType view_type, const VkComponentMapping& mapping, const VkImageSubresourceRange& range)
|
||||
: m_device(dev), m_resource(resource)
|
||||
{
|
||||
info.format = resource->info.format;
|
||||
info.format = format == VK_FORMAT_UNDEFINED ? resource->format() : format;
|
||||
info.image = resource->value;
|
||||
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
info.components = mapping;
|
||||
|
|
@ -350,7 +360,7 @@ namespace vk
|
|||
info.viewType = VK_IMAGE_VIEW_TYPE_1D;
|
||||
break;
|
||||
case VK_IMAGE_TYPE_2D:
|
||||
if (resource->info.flags == VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)
|
||||
if (resource->info.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)
|
||||
info.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
else if (resource->info.arrayLayers == 1)
|
||||
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
|
|
@ -379,6 +389,33 @@ namespace vk
|
|||
vkDestroyImageView(m_device, value, nullptr);
|
||||
}
|
||||
|
||||
image_view* image_view::as(VkFormat format)
|
||||
{
|
||||
if (this->format() == format)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
auto self = this->m_root_view
|
||||
? this->m_root_view
|
||||
: this;
|
||||
|
||||
if (auto found = self->m_subviews.find(format);
|
||||
found != self->m_subviews.end())
|
||||
{
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
// Create a derived
|
||||
auto view = std::make_unique<image_view>(m_device, info.image, info.viewType, format, info.components, info.subresourceRange);
|
||||
view->m_resource = self->m_resource;
|
||||
view->m_root_view = self;
|
||||
|
||||
auto ret = view.get();
|
||||
self->m_subviews.emplace(format, std::move(view));
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 image_view::encoded_component_map() const
|
||||
{
|
||||
#if (VK_DISABLE_COMPONENT_SWIZZLE)
|
||||
|
|
@ -479,7 +516,7 @@ namespace vk
|
|||
const VkImageSubresourceRange range = { aspect() & mask, 0, info.mipLevels, 0, info.arrayLayers };
|
||||
ensure(range.aspectMask);
|
||||
|
||||
auto view = std::make_unique<vk::image_view>(*g_render_device, this, VK_IMAGE_VIEW_TYPE_MAX_ENUM, real_mapping, range);
|
||||
auto view = std::make_unique<vk::image_view>(*g_render_device, this, format(), VK_IMAGE_VIEW_TYPE_MAX_ENUM, real_mapping, range);
|
||||
auto result = view.get();
|
||||
views.emplace(storage_key, std::move(view));
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "commands.h"
|
||||
#include "device.h"
|
||||
#include "ex.h"
|
||||
#include "memory.h"
|
||||
#include "unique_resource.h"
|
||||
|
||||
|
|
@ -46,14 +47,14 @@ namespace vk
|
|||
VkComponentMapping native_component_map = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
|
||||
VkImageLayout current_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
u32 current_queue_family = VK_QUEUE_FAMILY_IGNORED;
|
||||
VkImageCreateInfo info = {};
|
||||
VkImageCreateInfo info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
std::shared_ptr<vk::memory_block> memory;
|
||||
|
||||
image(const vk::render_device& dev,
|
||||
const memory_type_info& memory_type,
|
||||
u32 access_flags,
|
||||
VkImageType image_type,
|
||||
VkFormat format,
|
||||
const VkFormatEx& format,
|
||||
u32 width, u32 height, u32 depth,
|
||||
u32 mipmaps, u32 layers,
|
||||
VkSampleCountFlagBits samples,
|
||||
|
|
@ -112,21 +113,29 @@ namespace vk
|
|||
image_view(VkDevice dev, VkImageViewCreateInfo create_info);
|
||||
|
||||
image_view(VkDevice dev, vk::image* resource,
|
||||
VkFormat format = VK_FORMAT_UNDEFINED,
|
||||
VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_MAX_ENUM,
|
||||
const VkComponentMapping& mapping = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A },
|
||||
const VkImageSubresourceRange& range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
|
||||
|
||||
~image_view();
|
||||
|
||||
vk::image_view* as(VkFormat new_format);
|
||||
|
||||
u32 encoded_component_map() const;
|
||||
vk::image* image() const;
|
||||
|
||||
image_view(const image_view&) = delete;
|
||||
image_view(image_view&&) = delete;
|
||||
|
||||
VkFormat format() const { return info.format; }
|
||||
|
||||
private:
|
||||
std::unordered_map<VkFormat, std::unique_ptr<vk::image_view>> m_subviews;
|
||||
|
||||
VkDevice m_device;
|
||||
vk::image* m_resource = nullptr;
|
||||
vk::image_view* m_root_view = nullptr;
|
||||
|
||||
void create_impl();
|
||||
void set_debug_name(std::string_view name);
|
||||
|
|
@ -141,7 +150,8 @@ namespace vk
|
|||
public:
|
||||
using image::image;
|
||||
|
||||
virtual image_view* get_view(const rsx::texture_channel_remap_t& remap,
|
||||
virtual image_view* get_view(
|
||||
const rsx::texture_channel_remap_t& remap,
|
||||
VkImageAspectFlags mask = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
|
||||
void set_native_component_layout(VkComponentMapping new_layout);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,52 @@
|
|||
|
||||
namespace rsx
|
||||
{
|
||||
enum texture_control_bits : u32
|
||||
{
|
||||
GAMMA_A = 0,
|
||||
GAMMA_R,
|
||||
GAMMA_G,
|
||||
GAMMA_B,
|
||||
ALPHAKILL,
|
||||
RENORMALIZE,
|
||||
EXPAND_A,
|
||||
EXPAND_R,
|
||||
EXPAND_G,
|
||||
EXPAND_B,
|
||||
SEXT_A,
|
||||
SEXT_R,
|
||||
SEXT_G,
|
||||
SEXT_B,
|
||||
DEPTH_FLOAT,
|
||||
DEPTH_COMPARE_OP,
|
||||
DEPTH_COMPARE_1,
|
||||
DEPTH_COMPARE_2,
|
||||
FILTERED_MAG,
|
||||
FILTERED_MIN,
|
||||
UNNORMALIZED_COORDS,
|
||||
CLAMP_TEXCOORDS_BIT,
|
||||
WRAP_S,
|
||||
WRAP_T,
|
||||
WRAP_R,
|
||||
FF_SIGNED_BIT,
|
||||
FF_BIASED_RENORM_BIT,
|
||||
FF_GAMMA_BIT,
|
||||
FF_16BIT_CHANNELS_BIT,
|
||||
|
||||
// Meta
|
||||
GAMMA_CTRL_MASK = (1 << GAMMA_R) | (1 << GAMMA_G) | (1 << GAMMA_B) | (1 << GAMMA_A),
|
||||
GAMMA_RGB_MASK = (1 << GAMMA_R) | (1 << GAMMA_G) | (1 << GAMMA_B),
|
||||
GAMMA_OFFSET = GAMMA_A,
|
||||
|
||||
EXPAND_MASK = (1 << EXPAND_R) | (1 << EXPAND_G) | (1 << EXPAND_B) | (1 << EXPAND_A),
|
||||
EXPAND_OFFSET = EXPAND_A,
|
||||
|
||||
SEXT_MASK = (1 << SEXT_R) | (1 << SEXT_G) | (1 << SEXT_B) | (1 << SEXT_A),
|
||||
SEXT_OFFSET = SEXT_A,
|
||||
|
||||
FORMAT_FEATURES_OFFSET = FF_SIGNED_BIT,
|
||||
};
|
||||
|
||||
struct texture_channel_remap_t
|
||||
{
|
||||
u32 encoded = 0xDEAD;
|
||||
|
|
|
|||
|
|
@ -468,6 +468,8 @@ namespace gcm
|
|||
|
||||
RSX_SHADER_CONTROL_DISABLE_EARLY_Z = 0x2000000, // Do not allow early-Z optimizations on this shader
|
||||
|
||||
RSX_SHADER_CONTROL_TEXTURE_FORMAT_CONVERT = 0x4000000, // Allow format conversions (BX2, SNORM, SRGB, RENORM)
|
||||
|
||||
// Meta
|
||||
RSX_SHADER_CONTROL_META_USES_DISCARD = (RSX_SHADER_CONTROL_USES_KIL | RSX_SHADER_CONTROL_TEXTURE_ALPHA_KILL | RSX_SHADER_CONTROL_ALPHA_TEST | RSX_SHADER_CONTROL_POLYGON_STIPPLE | RSX_SHADER_CONTROL_ALPHA_TO_COVERAGE)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ enum class localized_string_id
|
|||
HOME_MENU_RELOAD_SECOND_SAVESTATE,
|
||||
HOME_MENU_RELOAD_THIRD_SAVESTATE,
|
||||
HOME_MENU_RELOAD_FOURTH_SAVESTATE,
|
||||
HOME_MENU_TOGGLE_FULLSCREEN,
|
||||
HOME_MENU_RECORDING,
|
||||
HOME_MENU_TROPHIES,
|
||||
HOME_MENU_TROPHY_LIST_TITLE,
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ struct cfg_root : cfg::node
|
|||
cfg::_bool disable_async_host_memory_manager{ this, "Disable Asynchronous Memory Manager", false, true };
|
||||
cfg::_enum<output_scaling_mode> output_scaling{ this, "Output Scaling Mode", output_scaling_mode::bilinear, true };
|
||||
cfg::_bool record_with_overlays{ this, "Record With Overlays", true, true };
|
||||
cfg::_bool disable_hardware_texel_remapping{ this, "Disable Hardware ColorSpace Remapping", false, true };
|
||||
|
||||
struct node_vk : cfg::node
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace rpcs3
|
|||
std::string title_id;
|
||||
std::string renderer;
|
||||
std::string vulkan_adapter;
|
||||
double fps = .0;
|
||||
double fps = 0.0;
|
||||
};
|
||||
|
||||
std::string get_formatted_title(const title_format_data& title_data);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "stdafx.h"
|
||||
#include "mouse_gyro_handler.h"
|
||||
|
||||
#include <QEvent>
|
||||
|
|
@ -7,53 +8,57 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
LOG_CHANNEL(gui_log, "GUI");
|
||||
|
||||
void mouse_gyro_handler::clear()
|
||||
{
|
||||
active = false;
|
||||
reset = false;
|
||||
gyro_x = DEFAULT_MOTION_X;
|
||||
gyro_y = DEFAULT_MOTION_Y;
|
||||
gyro_z = DEFAULT_MOTION_Z;
|
||||
m_active = false;
|
||||
m_reset = false;
|
||||
m_gyro_x = DEFAULT_MOTION_X;
|
||||
m_gyro_y = DEFAULT_MOTION_Y;
|
||||
m_gyro_z = DEFAULT_MOTION_Z;
|
||||
}
|
||||
|
||||
bool mouse_gyro_handler::toggle_enabled()
|
||||
{
|
||||
enabled = !enabled;
|
||||
m_enabled = !m_enabled;
|
||||
clear();
|
||||
return enabled;
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_active()
|
||||
{
|
||||
active = true;
|
||||
gui_log.notice("Mouse-based gyro activated");
|
||||
m_active = true;
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_reset()
|
||||
{
|
||||
active = false;
|
||||
reset = true;
|
||||
gui_log.notice("Mouse-based gyro deactivated");
|
||||
m_active = false;
|
||||
m_reset = true;
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_xz(s32 off_x, s32 off_y)
|
||||
{
|
||||
if (!active)
|
||||
if (!m_active)
|
||||
return;
|
||||
|
||||
gyro_x = static_cast<u16>(std::clamp(off_x, 0, DEFAULT_MOTION_X * 2 - 1));
|
||||
gyro_z = static_cast<u16>(std::clamp(off_y, 0, DEFAULT_MOTION_Z * 2 - 1));
|
||||
m_gyro_x = static_cast<u16>(std::clamp(off_x, 0, DEFAULT_MOTION_X * 2 - 1));
|
||||
m_gyro_z = static_cast<u16>(std::clamp(off_y, 0, DEFAULT_MOTION_Z * 2 - 1));
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_y(s32 steps)
|
||||
{
|
||||
if (!active)
|
||||
if (!m_active)
|
||||
return;
|
||||
|
||||
gyro_y = static_cast<u16>(std::clamp(gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1));
|
||||
m_gyro_y = static_cast<u16>(std::clamp(m_gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1));
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::handle_event(QEvent* ev, const QWindow& win)
|
||||
{
|
||||
if (!enabled)
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
// Mouse-based motion input.
|
||||
|
|
@ -119,15 +124,12 @@ void mouse_gyro_handler::handle_event(QEvent* ev, const QWindow& win)
|
|||
|
||||
void mouse_gyro_handler::apply_gyro(const std::shared_ptr<Pad>& pad)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
if (!pad || !pad->is_connected())
|
||||
if (!m_enabled || !pad || !pad->is_connected())
|
||||
return;
|
||||
|
||||
// Inject mouse-based motion sensor values into pad sensors for gyro emulation.
|
||||
// The Qt frontend maps cursor offset and wheel input to absolute motion values while RMB is held.
|
||||
if (reset)
|
||||
if (m_reset)
|
||||
{
|
||||
// RMB released → reset motion
|
||||
pad->m_sensors[0].m_value = DEFAULT_MOTION_X;
|
||||
|
|
@ -139,8 +141,8 @@ void mouse_gyro_handler::apply_gyro(const std::shared_ptr<Pad>& pad)
|
|||
{
|
||||
// RMB held → accumulate motion
|
||||
// Axes have been chosen as tested in Sly 4 minigames. Top-down view motion uses X/Z axes.
|
||||
pad->m_sensors[0].m_value = gyro_x; // Mouse X → Motion X
|
||||
pad->m_sensors[1].m_value = gyro_y; // Mouse Wheel → Motion Y
|
||||
pad->m_sensors[2].m_value = gyro_z; // Mouse Y → Motion Z
|
||||
pad->m_sensors[0].m_value = m_gyro_x; // Mouse X → Motion X
|
||||
pad->m_sensors[1].m_value = m_gyro_y; // Mouse Wheel → Motion Y
|
||||
pad->m_sensors[2].m_value = m_gyro_z; // Mouse Y → Motion Z
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,24 +10,23 @@ class QWindow;
|
|||
// Mouse-based motion sensor emulation state.
|
||||
class mouse_gyro_handler
|
||||
{
|
||||
private:
|
||||
atomic_t<bool> enabled = false; // Whether mouse-based gyro emulation mode has been enabled by using the associated hotkey
|
||||
|
||||
atomic_t<bool> active = false; // Whether right mouse button is currently held (gyro active)
|
||||
atomic_t<bool> reset = false; // One-shot reset request on right mouse button release
|
||||
atomic_t<s32> gyro_x = DEFAULT_MOTION_X; // Accumulated from mouse X position relative to center
|
||||
atomic_t<s32> gyro_y = DEFAULT_MOTION_Y; // Accumulated from mouse wheel delta
|
||||
atomic_t<s32> gyro_z = DEFAULT_MOTION_Z; // Accumulated from mouse Y position relative to center
|
||||
|
||||
void set_gyro_active();
|
||||
void set_gyro_reset();
|
||||
void set_gyro_xz(s32 off_x, s32 off_y);
|
||||
void set_gyro_y(s32 steps);
|
||||
|
||||
public:
|
||||
void clear();
|
||||
bool toggle_enabled();
|
||||
|
||||
void handle_event(QEvent* ev, const QWindow& win);
|
||||
void apply_gyro(const std::shared_ptr<Pad>& pad);
|
||||
|
||||
private:
|
||||
atomic_t<bool> m_enabled = false; // Whether mouse-based gyro emulation mode has been enabled by using the associated hotkey
|
||||
atomic_t<bool> m_active = false; // Whether right mouse button is currently held (gyro active)
|
||||
atomic_t<bool> m_reset = false; // One-shot reset request on right mouse button release
|
||||
atomic_t<s32> m_gyro_x = DEFAULT_MOTION_X; // Accumulated from mouse X position relative to center
|
||||
atomic_t<s32> m_gyro_y = DEFAULT_MOTION_Y; // Accumulated from mouse wheel delta
|
||||
atomic_t<s32> m_gyro_z = DEFAULT_MOTION_Z; // Accumulated from mouse Y position relative to center
|
||||
|
||||
void set_gyro_active();
|
||||
void set_gyro_reset();
|
||||
void set_gyro_xz(s32 off_x, s32 off_y);
|
||||
void set_gyro_y(s32 steps);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ enum class emu_settings_type
|
|||
DisableAsyncHostMM,
|
||||
UseReBAR,
|
||||
RecordWithOverlays,
|
||||
DisableHWTexelRemapping,
|
||||
|
||||
// Performance Overlay
|
||||
PerfOverlayEnabled,
|
||||
|
|
@ -312,6 +313,7 @@ inline static const std::map<emu_settings_type, cfg_location> settings_location
|
|||
{ emu_settings_type::ForceHwMSAAResolve, { "Video", "Force Hardware MSAA Resolve"}},
|
||||
{ emu_settings_type::DisableAsyncHostMM, { "Video", "Disable Asynchronous Memory Manager"}},
|
||||
{ emu_settings_type::RecordWithOverlays, { "Video", "Record With Overlays"}},
|
||||
{ emu_settings_type::DisableHWTexelRemapping, { "Video", "Disable Hardware ColorSpace Remapping"}},
|
||||
|
||||
// Vulkan
|
||||
{ emu_settings_type::VulkanAsyncTextureUploads, { "Video", "Vulkan", "Asynchronous Texture Streaming 2"}},
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ LOG_CHANNEL(screenshot_log, "SCREENSHOT");
|
|||
LOG_CHANNEL(mark_log, "MARK");
|
||||
LOG_CHANNEL(gui_log, "GUI");
|
||||
|
||||
extern atomic_t<bool> g_user_asked_for_fullscreen;
|
||||
extern atomic_t<bool> g_user_asked_for_recording;
|
||||
extern atomic_t<bool> g_user_asked_for_screenshot;
|
||||
extern atomic_t<bool> g_user_asked_for_frame_capture;
|
||||
|
|
@ -150,6 +151,9 @@ gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon,
|
|||
gs_frame::~gs_frame()
|
||||
{
|
||||
g_user_asked_for_screenshot = false;
|
||||
g_user_asked_for_recording = false;
|
||||
g_user_asked_for_frame_capture = false;
|
||||
g_user_asked_for_fullscreen = false;
|
||||
pad::g_home_menu_requested = false;
|
||||
|
||||
// Save active screen to gui settings
|
||||
|
|
@ -201,12 +205,12 @@ void gs_frame::update_shortcuts()
|
|||
}
|
||||
}
|
||||
|
||||
void gs_frame::paintEvent(QPaintEvent *event)
|
||||
void gs_frame::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
}
|
||||
|
||||
void gs_frame::showEvent(QShowEvent *event)
|
||||
void gs_frame::showEvent(QShowEvent* event)
|
||||
{
|
||||
// We have to calculate new window positions, since the frame is only known once the window was created.
|
||||
// We will try to find the originally requested dimensions if possible by moving the frame.
|
||||
|
|
@ -801,7 +805,7 @@ f64 gs_frame::client_display_rate()
|
|||
return rate;
|
||||
}
|
||||
|
||||
void gs_frame::flip(draw_context_t, bool /*skip_frame*/)
|
||||
void gs_frame::flip(draw_context_t /*context*/, bool /*skip_frame*/)
|
||||
{
|
||||
static Timer fps_t;
|
||||
|
||||
|
|
@ -840,6 +844,14 @@ void gs_frame::flip(draw_context_t, bool /*skip_frame*/)
|
|||
toggle_recording();
|
||||
});
|
||||
}
|
||||
|
||||
if (g_user_asked_for_fullscreen.exchange(false))
|
||||
{
|
||||
Emu.CallFromMainThread([this]()
|
||||
{
|
||||
toggle_fullscreen();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool gs_frame::can_consume_frame() const
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@ public:
|
|||
protected:
|
||||
video_renderer m_renderer;
|
||||
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
void close() override;
|
||||
void reset() override;
|
||||
|
|
|
|||
|
|
@ -298,6 +298,7 @@ private:
|
|||
case localized_string_id::HOME_MENU_RELOAD_SECOND_SAVESTATE: return tr("Reload Second-To-Last Emulation State");
|
||||
case localized_string_id::HOME_MENU_RELOAD_THIRD_SAVESTATE: return tr("Reload Third-To-Last Emulation State");
|
||||
case localized_string_id::HOME_MENU_RELOAD_FOURTH_SAVESTATE: return tr("Reload Fourth-To-Last Emulation State");
|
||||
case localized_string_id::HOME_MENU_TOGGLE_FULLSCREEN: return tr("Toggle Fullscreen");
|
||||
case localized_string_id::HOME_MENU_RECORDING: return tr("Start/Stop Recording");
|
||||
case localized_string_id::HOME_MENU_TROPHIES: return tr("Trophies");
|
||||
case localized_string_id::HOME_MENU_TROPHY_LIST_TITLE: return tr("Trophy Progress: %0").arg(std::forward<Args>(args)...);
|
||||
|
|
|
|||
|
|
@ -1555,6 +1555,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
|||
m_emu_settings->EnhanceCheckBox(ui->enableSpuEventsBusyLoop, emu_settings_type::EnabledSPUEventsBusyLoop);
|
||||
SubscribeTooltip(ui->enableSpuEventsBusyLoop, tooltips.settings.enable_spu_events_busy_loop);
|
||||
|
||||
m_emu_settings->EnhanceCheckBox(ui->disableHardwareTexelRemapping, emu_settings_type::DisableHWTexelRemapping);
|
||||
SubscribeTooltip(ui->disableHardwareTexelRemapping, tooltips.settings.disable_hw_texel_remapping);
|
||||
|
||||
// Comboboxes
|
||||
|
||||
m_emu_settings->EnhanceComboBox(ui->maxSPURSThreads, emu_settings_type::MaxSPURSThreads, true);
|
||||
|
|
|
|||
|
|
@ -4316,6 +4316,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="disableHardwareTexelRemapping">
|
||||
<property name="text">
|
||||
<string>Disable Hardware ColorSpace Remapping</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacerDebugGPU">
|
||||
<property name="orientation">
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ public:
|
|||
const QString accurate_ppu_128_loop = tr("When enabled, PPU atomic operations will operate on entire cache line data, as opposed to a single 64bit block of memory when disabled.\nNumerical values control whether or not to enable the accurate version based on the atomic operation's length.");
|
||||
const QString enable_performance_report = tr("Measure certain events and print a chart after the emulator is stopped. Don't enable if not asked to.");
|
||||
const QString num_ppu_threads = tr("Affects maximum amount of PPU threads running concurrently, the value of 1 has very low compatibility with games.\n2 is the default, if unsure do not modify this setting.");
|
||||
const QString disable_hw_texel_remapping = tr("Disables use of hardware-native color-space remapping formats such as _sRGB and _SNORM suffixes.\nDisabling this option increases accuracy compared to PS3 but can also introduce some noise due to how the software emulation works.");
|
||||
|
||||
// emulator
|
||||
|
||||
|
|
|
|||
|
|
@ -22,3 +22,7 @@
|
|||
#include <unordered_map> // IWYU pragma: export
|
||||
#include <algorithm> // IWYU pragma: export
|
||||
#include <string_view> // IWYU pragma: export
|
||||
|
||||
#if defined(__INTELLISENSE__) && !defined(LLVM_AVAILABLE)
|
||||
#define LLVM_AVAILABLE
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue