1
0
mirror of https://github.com/RPCS3/rpcs3 synced 2024-07-05 17:18:51 +00:00

Compare commits

...

14 Commits

Author SHA1 Message Date
Megamouse
aefbeb217e
Merge 13620136fe into 71524271e9 2024-06-28 17:20:58 +03:00
kd-11
71524271e9 rsx: Fix codegen when depth-conversion is enabled 2024-06-28 12:13:33 +02:00
Megamouse
d5923ef808 Fix weird new gcc namespace warning 2024-06-28 00:24:07 +02:00
kd-11
ef136acb6c Whitespace fix 2024-06-27 22:55:56 +02:00
kd-11
a5956cfa82 rsx: Fix wrapped/clamped MSAA sampling behavior with dynamic flags 2024-06-27 22:55:56 +02:00
Megamouse
703de01ebf input: actually initialize midi drum notes and combos 2024-06-27 22:14:35 +02:00
Megamouse
8343e35146 cellCamera: improve error logging 2024-06-27 19:12:34 +02:00
Megamouse
0679b502f2 input: allow dynamic change of midi drum config 2024-06-27 19:12:34 +02:00
Megamouse
e790842007 input: allow dynamic change of mouse configs 2024-06-27 19:12:34 +02:00
Elad Ashkenazi
a9d53e98de SPU LLVM: Fix LSA masking for PUTLLC16, disable RTIME checks 2024-06-27 16:58:23 +03:00
Elad Ashkenazi
908082e7c3 Silence some cellGem logging 2024-06-27 16:58:23 +03:00
Elad Ashkenazi
8ec6187dc7 SPU Analyzer: Fix support for multi-block value merge 2024-06-27 16:58:23 +03:00
Megamouse
13620136fe Qt: check microphone permissions 2024-03-28 09:55:57 +01:00
Megamouse
1240921e7e Qt: ask for camera permissions 2024-03-28 09:55:57 +01:00
38 changed files with 586 additions and 237 deletions

View File

@ -1679,9 +1679,9 @@ void camera_context::operator()()
data3 = 0; // unused data3 = 0; // unused
} }
if (queue->send(evt_data.source, CELL_CAMERA_FRAME_UPDATE, data2, data3) != 0) [[unlikely]] if (CellError err = queue->send(evt_data.source, CELL_CAMERA_FRAME_UPDATE, data2, data3)) [[unlikely]]
{ {
cellCamera.warning("Failed to send frame update event"); cellCamera.warning("Failed to send frame update event (error=0x%x)", err);
} }
frame_update_event_sent = true; frame_update_event_sent = true;
@ -1819,9 +1819,9 @@ void camera_context::send_attach_state(bool attached)
{ {
if (auto queue = lv2_event_queue::find(key)) if (auto queue = lv2_event_queue::find(key))
{ {
if (queue->send(evt_data.source, attached ? CELL_CAMERA_ATTACH : CELL_CAMERA_DETACH, 0, 0) != 0) [[unlikely]] if (CellError err = queue->send(evt_data.source, attached ? CELL_CAMERA_ATTACH : CELL_CAMERA_DETACH, 0, 0)) [[unlikely]]
{ {
cellCamera.warning("Failed to send attach event (attached=%d)", attached); cellCamera.warning("Failed to send attach event (attached=%d, error=0x%x)", attached, err);
} }
} }
} }

View File

@ -1759,7 +1759,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
error_code cellGemGetStatusFlags(u32 gem_num, vm::ptr<u64> flags) error_code cellGemGetStatusFlags(u32 gem_num, vm::ptr<u64> flags)
{ {
cellGem.todo("cellGemGetStatusFlags(gem_num=%d, flags=*0x%x)", gem_num, flags); cellGem.trace("cellGemGetStatusFlags(gem_num=%d, flags=*0x%x)", gem_num, flags);
auto& gem = g_fxo->get<gem_config>(); auto& gem = g_fxo->get<gem_config>();
@ -2094,7 +2094,7 @@ error_code cellGemReset(u32 gem_num)
error_code cellGemSetRumble(u32 gem_num, u8 rumble) error_code cellGemSetRumble(u32 gem_num, u8 rumble)
{ {
cellGem.warning("cellGemSetRumble(gem_num=%d, rumble=0x%x)", gem_num, rumble); cellGem.trace("cellGemSetRumble(gem_num=%d, rumble=0x%x)", gem_num, rumble);
auto& gem = g_fxo->get<gem_config>(); auto& gem = g_fxo->get<gem_config>();

View File

@ -2627,18 +2627,18 @@ reg_state_t reg_state_t::downgrade() const
{ {
if (flag & vf::is_const) if (flag & vf::is_const)
{ {
return reg_state_t{vf::is_mask, 0, umax, this->value, ~this->value}; return reg_state_t{vf::is_mask, 0, umax, this->value, ~this->value, this->origin};
} }
if (!(flag - vf::is_null)) if (!(flag - vf::is_null))
{ {
return reg_state_t{vf::is_mask, 0, this->tag, 0, 0}; return reg_state_t{vf::is_mask, 0, this->tag, 0, 0, this->origin};
} }
return *this; return *this;
} }
reg_state_t reg_state_t::merge(const reg_state_t& rhs) const reg_state_t reg_state_t::merge(const reg_state_t& rhs, u32 current_pc) const
{ {
if (rhs == *this) if (rhs == *this)
{ {
@ -2661,12 +2661,13 @@ reg_state_t reg_state_t::merge(const reg_state_t& rhs) const
{ {
// Success (create new value tag) // Success (create new value tag)
res.tag = reg_state_t::alloc_tag(); res.tag = reg_state_t::alloc_tag();
res.origin = current_pc;
return res; return res;
} }
} }
} }
return make_unknown(); return make_unknown(current_pc);
} }
reg_state_t reg_state_t::build_on_top_of(const reg_state_t& rhs) const reg_state_t reg_state_t::build_on_top_of(const reg_state_t& rhs) const
@ -2728,9 +2729,17 @@ u32 reg_state_t::alloc_tag(bool reset) noexcept
return ++g_tls_tag; return ++g_tls_tag;
} }
void reg_state_t::invalidate_if_created(u32 current_pc)
{
if (!is_const() && origin == current_pc)
{
tag = reg_state_t::alloc_tag();
}
}
// Converge 2 register states to the same flow in execution // Converge 2 register states to the same flow in execution
template <usz N> template <usz N>
static void merge(std::array<reg_state_t, N>& result, const std::array<reg_state_t, N>& lhs, const std::array<reg_state_t, N>& rhs) static void merge(std::array<reg_state_t, N>& result, const std::array<reg_state_t, N>& lhs, const std::array<reg_state_t, N>& rhs, u32 current_pc)
{ {
usz index = umax; usz index = umax;
@ -2738,7 +2747,7 @@ static void merge(std::array<reg_state_t, N>& result, const std::array<reg_state
{ {
index++; index++;
state = lhs[index].merge(rhs[index]); state = lhs[index].merge(rhs[index], current_pc);
} }
} }
@ -2777,7 +2786,7 @@ struct block_reg_info
static std::unique_ptr<block_reg_info> create(u32 pc) noexcept static std::unique_ptr<block_reg_info> create(u32 pc) noexcept
{ {
auto ptr = new block_reg_info{ pc, reg_state_t::make_unknown<s_reg_max>() }; auto ptr = new block_reg_info{ pc, reg_state_t::make_unknown<s_reg_max>(pc) };
for (reg_state_t& f : ptr->local_state) for (reg_state_t& f : ptr->local_state)
{ {
@ -4882,7 +4891,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
const bool should_search_patterns = target_count < 300u; const bool should_search_patterns = target_count < 300u;
// Treat start of function as an unknown value with tag (because it is) // Treat start of function as an unknown value with tag (because it is)
const reg_state_t start_program_count = reg_state_t::make_unknown(); const reg_state_t start_program_count = reg_state_t::make_unknown(entry_point - 1);
// Initialize // Initialize
reg_state_it.emplace_back(entry_point); reg_state_it.emplace_back(entry_point);
@ -5375,10 +5384,20 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
vregs[reg] = reg_state_t::from_value(value); vregs[reg] = reg_state_t::from_value(value);
}; };
const auto inherit_const_value = [&](u32 reg, bs_t<vf> flag, u32 value) const auto inherit_const_value = [&](u32 reg, const reg_state_t& ra, const reg_state_t& rb, u32 value, u32 pos)
{ {
flag -= vf::is_null; if (ra.origin != rb.origin)
vregs[reg] = reg_state_t{flag, value, flag & vf::is_const ? u32{umax} : reg_state_t::alloc_tag()}; {
pos = reg_state_it[wi].pc;
}
else
{
pos = ra.origin;
}
const bs_t<vf> flag = (ra.flag & rb.flag) - vf::is_null;
vregs[reg] = reg_state_t{flag, value, flag & vf::is_const ? u32{umax} : reg_state_t::alloc_tag(), 0, 0, pos};
}; };
const auto inherit_const_mask_value = [&](u32 reg, reg_state_t state, u32 mask_ones, u32 mask_zeroes) const auto inherit_const_mask_value = [&](u32 reg, reg_state_t state, u32 mask_ones, u32 mask_zeroes)
@ -5407,12 +5426,12 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
ensure(state.tag != umax); ensure(state.tag != umax);
vregs[reg] = reg_state_t{vf::is_mask, 0, state.tag, ones, zeroes}; vregs[reg] = reg_state_t{vf::is_mask, 0, state.tag, ones, zeroes, state.origin};
}; };
const auto unconst = [&](u32 reg) const auto unconst = [&](u32 reg, u32 pc)
{ {
vregs[reg] = {{}, {}, reg_state_t::alloc_tag()}; vregs[reg] = reg_state_t::make_unknown(pc);
}; };
const auto add_block = [&](u32 target) const auto add_block = [&](u32 target)
@ -5467,6 +5486,14 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
} }
if (atomic16.active)
{
for (auto state : {&atomic16.lsa, &atomic16.ls, &atomic16.ls_offs})
{
state->invalidate_if_created(pos);
}
}
const u32 data = std::bit_cast<be_t<u32>>(::at32(result.data, (pos - lsa) / 4)); const u32 data = std::bit_cast<be_t<u32>>(::at32(result.data, (pos - lsa) / 4));
const auto op = spu_opcode_t{data}; const auto op = spu_opcode_t{data};
const auto type = g_spu_itype.decode(data); const auto type = g_spu_itype.decode(data);
@ -5650,7 +5677,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
case MFC_Cmd: case MFC_Cmd:
{ {
const auto [af, av, atagg, _3, _5] = get_reg(op.rt); const auto [af, av, atagg, _3, _5, apc] = get_reg(op.rt);
if (!is_pattern_match) if (!is_pattern_match)
{ {
@ -5662,9 +5689,15 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
{ {
case MFC_GETLLAR_CMD: case MFC_GETLLAR_CMD:
{ {
// Get LSA and apply mask for GETLLAR
// TODO: Simplify this to be a value returning function
auto old_lsa = get_reg(s_reg_mfc_lsa);
inherit_const_mask_value(s_reg_mfc_lsa, old_lsa, 0, ~SPU_LS_MASK_128);
// Restore LSA
auto lsa = get_reg(s_reg_mfc_lsa); auto lsa = get_reg(s_reg_mfc_lsa);
inherit_const_mask_value(s_reg_mfc_lsa, lsa, 0, ~SPU_LS_MASK_128); vregs[s_reg_mfc_lsa] = old_lsa;
lsa = get_reg(s_reg_mfc_lsa);
const u32 lsa_pc = atomic16.lsa_last_pc == SPU_LS_SIZE ? bpc : atomic16.lsa_last_pc; const u32 lsa_pc = atomic16.lsa_last_pc == SPU_LS_SIZE ? bpc : atomic16.lsa_last_pc;
if (atomic16.active) if (atomic16.active)
@ -5716,7 +5749,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
continue; continue;
} }
if (vregs[s_reg_mfc_lsa].compare_with_mask_indifference(*val, SPU_LS_MASK_1)) if (vregs[s_reg_mfc_lsa].compare_with_mask_indifference(*val, SPU_LS_MASK_16))
{ {
regs[reg_it] = s_reg_mfc_lsa; regs[reg_it] = s_reg_mfc_lsa;
continue; continue;
@ -5726,7 +5759,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
{ {
const auto& _reg = vregs[i]; const auto& _reg = vregs[i];
if (_reg == *val) if (_reg.compare_with_mask_indifference(*val, SPU_LS_MASK_16))
{ {
regs[reg_it] = i; regs[reg_it] = i;
break; break;
@ -5908,14 +5941,25 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
// Merge pattern attributes between different code paths, may cause detection of failures // Merge pattern attributes between different code paths, may cause detection of failures
atomic16_t& existing = it->second; atomic16_t& existing = it->second;
if (existing.lsa_pc != atomic16.lsa_pc || existing.put_pc != atomic16.put_pc || existing.lsa != atomic16.lsa) auto compare_tag_and_reg = [](std::pair<const reg_state_t*, u32> a, std::pair<const reg_state_t*, u32> b)
{
if (b.first->is_const() && a.first->is_const())
{
return a.first->compare_with_mask_indifference(*b.first, SPU_LS_MASK_1);
}
// Compare register source
return a.second == b.second;
};
if (existing.lsa_pc != atomic16.lsa_pc || existing.put_pc != atomic16.put_pc || !existing.lsa.compare_with_mask_indifference(atomic16.lsa, SPU_LS_MASK_128))
{ {
// Register twice // Register twice
break_putllc16(22, atomic16.discard()); break_putllc16(22, atomic16.discard());
break_putllc16(22, existing.discard()); break_putllc16(22, existing.discard());
} }
if (existing.active && existing.ls_access && atomic16.ls_access && (!existing.ls.compare_with_mask_indifference(atomic16.ls, SPU_LS_MASK_1) || existing.ls_offs != atomic16.ls_offs)) if (existing.active && existing.ls_access && atomic16.ls_access && (!compare_tag_and_reg({&existing.ls, existing.reg}, {&atomic16.ls, atomic16.reg}) || existing.ls_offs != atomic16.ls_offs || existing.reg2 != atomic16.reg2))
{ {
// Conflicting loads with stores in more than one code path // Conflicting loads with stores in more than one code path
break_putllc16(27, atomic16.set_invalid_ls(existing.ls_write || atomic16.ls_write)); break_putllc16(27, atomic16.set_invalid_ls(existing.ls_write || atomic16.ls_write));
@ -5938,6 +5982,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
{ {
// Propagate LS access // Propagate LS access
existing.ls = atomic16.ls; existing.ls = atomic16.ls;
existing.reg = atomic16.reg;
existing.reg2 = atomic16.reg2;
existing.ls_offs = atomic16.ls_offs; existing.ls_offs = atomic16.ls_offs;
} }
@ -5989,7 +6035,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (invalidate) if (invalidate)
{ {
unconst(op.rt); unconst(op.rt, pos);
} }
break; break;
@ -6068,7 +6114,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
// Unconst // Unconst
unconst(op.rt); unconst(op.rt, pos);
break; break;
} }
@ -6237,7 +6283,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
// Unconst // Unconst
unconst(op.rt); unconst(op.rt, pos);
break; break;
} }
case spu_itype::STQA: case spu_itype::STQA:
@ -6291,7 +6337,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
// Unconst // Unconst
unconst(op.rt); unconst(op.rt, pos);
break; break;
} }
@ -6371,14 +6417,14 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
// Unconst // Unconst
unconst(op.rt); unconst(op.rt, pos);
break; break;
} }
case spu_itype::HBR: case spu_itype::HBR:
{ {
hbr_loc = spu_branch_target(pos, op.roh << 7 | op.rt); hbr_loc = spu_branch_target(pos, op.roh << 7 | op.rt);
const auto [af, av, at, ao, az] = get_reg(op.ra); const auto [af, av, at, ao, az, apc] = get_reg(op.ra);
hbr_tg = af & vf::is_const && !op.c ? av & 0x3fffc : -1; hbr_tg = af & vf::is_const && !op.c ? av & 0x3fffc : -1;
break; break;
} }
@ -6443,9 +6489,13 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break; break;
} }
const auto [af, av, at, ao, az] = get_reg(op.ra); const auto ra = get_reg(op.ra);
const auto [bf, bv, _2, _4, _6] = get_reg(op.rb); const auto rb = get_reg(op.rb);
inherit_const_value(op.rt, af & bf, bv | av);
const auto [af, av, at, ao, az, apc] = ra;
const auto [bf, bv, bt, bo, bz, bpc] = rb;
inherit_const_value(op.rt, ra, rb, av | bv, pos);
break; break;
} }
case spu_itype::XORI: case spu_itype::XORI:
@ -6456,8 +6506,11 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break; break;
} }
const auto [af, av, at, ao, az] = get_reg(op.ra); const auto ra = get_reg(op.ra);
inherit_const_value(op.rt, af, av ^ op.si10);
const auto [af, av, at, ao, az, apc] = ra;
inherit_const_value(op.rt, ra, ra, av ^ op.si10, pos);
break; break;
} }
case spu_itype::XOR: case spu_itype::XOR:
@ -6468,16 +6521,24 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break; break;
} }
const auto [af, av, at, ao, az] = get_reg(op.ra); const auto ra = get_reg(op.ra);
const auto [bf, bv, _2, _4, _6] = get_reg(op.rb); const auto rb = get_reg(op.rb);
inherit_const_value(op.rt, af & bf, bv ^ av);
const auto [af, av, at, ao, az, apc] = ra;
const auto [bf, bv, bt, bo, bz, bpc] = rb;
inherit_const_value(op.rt, ra, rb, bv ^ av, pos);
break; break;
} }
case spu_itype::NOR: case spu_itype::NOR:
{ {
const auto [af, av, at, ao, az] = get_reg(op.ra); const auto ra = get_reg(op.ra);
const auto [bf, bv, _2, _4, _6] = get_reg(op.rb); const auto rb = get_reg(op.rb);
inherit_const_value(op.rt, af & bf, ~(bv | av));
const auto [af, av, at, ao, az, apc] = ra;
const auto [bf, bv, bt, bo, bz, bpc] = rb;
inherit_const_value(op.rt, ra, rb, ~(bv | av), pos);
break; break;
} }
case spu_itype::ANDI: case spu_itype::ANDI:
@ -6494,9 +6555,13 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break; break;
} }
const auto [af, av, at, ao, az] = get_reg(op.ra); const auto ra = get_reg(op.ra);
const auto [bf, bv, _2, _4, _6] = get_reg(op.rb); const auto rb = get_reg(op.rb);
inherit_const_value(op.rt, af & bf, bv & av);
const auto [af, av, at, ao, az, apc] = ra;
const auto [bf, bv, bt, bo, bz, bpc] = rb;
inherit_const_value(op.rt, ra, rb, bv & av, pos);
break; break;
} }
case spu_itype::AI: case spu_itype::AI:
@ -6508,9 +6573,9 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
const auto ra = get_reg(op.ra); const auto ra = get_reg(op.ra);
const auto [af, av, at, ao, az] = ra; const auto [af, av, at, ao, az, apc] = ra;
inherit_const_value(op.rt, af, av + op.si10); inherit_const_value(op.rt, ra, ra, av + op.si10, pos);
if (u32 mask = ra.get_known_zeroes() & ~op.si10; mask & 1) if (u32 mask = ra.get_known_zeroes() & ~op.si10; mask & 1)
{ {
@ -6525,10 +6590,10 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
const auto ra = get_reg(op.ra); const auto ra = get_reg(op.ra);
const auto rb = get_reg(op.rb); const auto rb = get_reg(op.rb);
const auto [af, av, at, ao, az] = ra; const auto [af, av, at, ao, az, apc] = ra;
const auto [bf, bv, bt, bo, bz] = rb; const auto [bf, bv, bt, bo, bz, bpc] = rb;
inherit_const_value(op.rt, af & bf, bv + av); inherit_const_value(op.rt, ra, rb, bv + av, pos);
if (u32 mask = ra.get_known_zeroes() & rb.get_known_zeroes(); mask & 1) if (u32 mask = ra.get_known_zeroes() & rb.get_known_zeroes(); mask & 1)
{ {
@ -6540,8 +6605,10 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
case spu_itype::SFI: case spu_itype::SFI:
{ {
const auto [af, av, at, ao, az] = get_reg(op.ra); const auto ra = get_reg(op.ra);
inherit_const_value(op.rt, af, op.si10 - av); const auto [af, av, at, ao, az, apc] = get_reg(op.ra);
inherit_const_value(op.rt, ra, ra, op.si10 - av, pos);
break; break;
} }
case spu_itype::SF: case spu_itype::SF:
@ -6549,10 +6616,10 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
const auto ra = get_reg(op.ra); const auto ra = get_reg(op.ra);
const auto rb = get_reg(op.rb); const auto rb = get_reg(op.rb);
const auto [af, av, at, ao, az] = ra; const auto [af, av, at, ao, az, apc] = ra;
const auto [bf, bv, bt, bo, bz] = rb; const auto [bf, bv, bt, bo, bz, bpc] = rb;
inherit_const_value(op.rt, af & bf, bv - av); inherit_const_value(op.rt, ra, rb, bv - av, pos);
if (u32 mask = ra.get_known_zeroes() & rb.get_known_zeroes(); mask & 1) if (u32 mask = ra.get_known_zeroes() & rb.get_known_zeroes(); mask & 1)
{ {
@ -6588,8 +6655,10 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break; break;
} }
const auto [af, av, at, ao, az] = get_reg(op.ra); const auto ra = get_reg(op.ra);
inherit_const_value(op.rt, af, av >> ((0 - op.i7) & 0x1f)); const auto [af, av, at, ao, az, apc] = get_reg(op.ra);
inherit_const_value(op.rt, ra, ra, av >> ((0 - op.i7) & 0x1f), pos);
break; break;
} }
case spu_itype::SHLI: case spu_itype::SHLI:
@ -6606,8 +6675,10 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break; break;
} }
const auto [af, av, at, ao, az] = get_reg(op.ra); const auto ra = get_reg(op.ra);
inherit_const_value(op.rt, af, av << (op.i7 & 0x1f)); const auto [af, av, at, ao, az, apc] = ra;
inherit_const_value(op.rt, ra, ra, av << (op.i7 & 0x1f), pos);
break; break;
} }
case spu_itype::SELB: case spu_itype::SELB:
@ -6616,7 +6687,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
const auto rb = get_reg(op.rb); const auto rb = get_reg(op.rb);
// Ignore RC, perform a value merge which also respect bitwise information // Ignore RC, perform a value merge which also respect bitwise information
vregs[op.rt4] = ra.merge(rb); vregs[op.rt4] = ra.merge(rb, pos);
break; break;
} }
case spu_itype::SHLQBYI: case spu_itype::SHLQBYI:
@ -6641,7 +6712,49 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (!(type & spu_itype::zregmod)) if (!(type & spu_itype::zregmod))
{ {
const u32 op_rt = type & spu_itype::_quadrop ? +op.rt4 : +op.rt; const u32 op_rt = type & spu_itype::_quadrop ? +op.rt4 : +op.rt;
unconst(op_rt);
u32 ra = s_reg_max, rb = s_reg_max, rc = s_reg_max;
if (m_use_ra.test(pos / 4))
{
ra = op.ra;
}
if (m_use_rb.test(pos / 4))
{
rb = op.rb;
}
if (type & spu_itype::_quadrop && m_use_rc.test(pos / 4))
{
rc = op.rc;
}
u32 reg_pos = SPU_LS_SIZE;
for (u32 reg : {ra, rb, rc})
{
if (reg != s_reg_max)
{
if (reg_pos == SPU_LS_SIZE)
{
reg = vregs[reg].origin;
}
else if (reg_pos != vregs[reg].origin)
{
const u32 block_start = reg_state_it[wi].pc;
// if (vregs[reg].origin >= block_start && vregs[reg].origin <= pos)
// {
// reg_pos = std::max<u32>(vregs[reg].origin, reg_pos);
// }
reg_pos = block_start;
break;
}
}
}
unconst(op_rt, reg_pos == SPU_LS_SIZE ? pos : reg_pos);
} }
break; break;
@ -7714,7 +7827,7 @@ std::array<reg_state_t, s_reg_max>& block_reg_info::evaluate_start_state(const s
} }
else else
{ {
merge(res_state, res_state, *arg_state); merge(res_state, res_state, *arg_state, it->block_pc);
} }
} }

View File

@ -1292,7 +1292,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
const auto diff = m_ir->CreateZExt(m_ir->CreateSub(dest, _lsa), get_type<u64>()); const auto diff = m_ir->CreateZExt(m_ir->CreateSub(dest, _lsa), get_type<u64>());
const auto _new = m_ir->CreateAlignedLoad(get_type<u128>(), _ptr<u128>(m_lsptr, dest), llvm::MaybeAlign{16}); const auto _new = m_ir->CreateAlignedLoad(get_type<u128>(), _ptr<u128>(m_lsptr, dest), llvm::MaybeAlign{16});
const auto _rdata = m_ir->CreateAlignedLoad(get_type<u128>(), _ptr<u128>(spu_ptr<u8>(&spu_thread::rdata), m_ir->CreateAnd(diff, 0x7f)), llvm::MaybeAlign{16}); const auto _rdata = m_ir->CreateAlignedLoad(get_type<u128>(), _ptr<u128>(spu_ptr<u8>(&spu_thread::rdata), m_ir->CreateAnd(diff, 0x70)), llvm::MaybeAlign{16});
const bool is_accurate_op = !!g_cfg.core.spu_accurate_reservations; const bool is_accurate_op = !!g_cfg.core.spu_accurate_reservations;
@ -1360,7 +1360,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
llvm::Value* old_val{}; llvm::Value* old_val{};
if (is_accurate_op) if (true || is_accurate_op)
{ {
old_val = m_ir->CreateLoad(get_type<u64>(), spu_ptr<u64>(&spu_thread::rtime)); old_val = m_ir->CreateLoad(get_type<u64>(), spu_ptr<u64>(&spu_thread::rtime));
} }
@ -1373,7 +1373,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
const auto cmp_res2 = m_ir->CreateAtomicCmpXchg(rptr2, old_val, m_ir->CreateAdd(old_val, m_ir->getInt64(128)), llvm::MaybeAlign{16}, llvm::AtomicOrdering::SequentiallyConsistent, llvm::AtomicOrdering::SequentiallyConsistent); const auto cmp_res2 = m_ir->CreateAtomicCmpXchg(rptr2, old_val, m_ir->CreateAdd(old_val, m_ir->getInt64(128)), llvm::MaybeAlign{16}, llvm::AtomicOrdering::SequentiallyConsistent, llvm::AtomicOrdering::SequentiallyConsistent);
if (is_accurate_op) if (true || is_accurate_op)
{ {
m_ir->CreateCondBr(m_ir->CreateExtractValue(cmp_res2, 1), _success, _fail); m_ir->CreateCondBr(m_ir->CreateExtractValue(cmp_res2, 1), _success, _fail);
} }

View File

@ -208,6 +208,7 @@ public:
u32 tag = umax; u32 tag = umax;
u32 known_ones{}; u32 known_ones{};
u32 known_zeroes{}; u32 known_zeroes{};
u32 origin = SPU_LS_SIZE;
bool is_const() const; bool is_const() const;
@ -222,21 +223,33 @@ public:
bool compare_with_mask_indifference(u32 imm, u32 mask_bits) const; bool compare_with_mask_indifference(u32 imm, u32 mask_bits) const;
bool unequal_with_mask_indifference(const reg_state_t& r, u32 mask_bits) const; bool unequal_with_mask_indifference(const reg_state_t& r, u32 mask_bits) const;
// Convert constant-based value to mask-based value
reg_state_t downgrade() const; reg_state_t downgrade() const;
reg_state_t merge(const reg_state_t& rhs) const;
// Connect two register states between different blocks
reg_state_t merge(const reg_state_t& rhs, u32 current_pc) const;
// Override value with newer value if needed
reg_state_t build_on_top_of(const reg_state_t& rhs) const; reg_state_t build_on_top_of(const reg_state_t& rhs) const;
// Get known zeroes mask
u32 get_known_zeroes() const; u32 get_known_zeroes() const;
// Get known ones mask
u32 get_known_ones() const; u32 get_known_ones() const;
// Invalidate value if non-constant and reached the point in history of its creation
void invalidate_if_created(u32 current_pc);
template <usz Count = 1> template <usz Count = 1>
static std::conditional_t<Count == 1, reg_state_t, std::array<reg_state_t, Count>> make_unknown() noexcept static std::conditional_t<Count == 1, reg_state_t, std::array<reg_state_t, Count>> make_unknown(u32 pc) noexcept
{ {
if constexpr (Count == 1) if constexpr (Count == 1)
{ {
reg_state_t v{}; reg_state_t v{};
v.tag = alloc_tag(); v.tag = alloc_tag();
v.flag = {}; v.flag = {};
v.origin = pc;
return v; return v;
} }
else else
@ -245,7 +258,7 @@ public:
for (reg_state_t& state : result) for (reg_state_t& state : result)
{ {
state = make_unknown<1>(); state = make_unknown<1>(pc);
} }
return result; return result;

View File

@ -9,9 +9,6 @@ using namespace std::chrono_literals;
LOG_CHANNEL(rb3_midi_drums_log); LOG_CHANNEL(rb3_midi_drums_log);
namespace
{
namespace controller namespace controller
{ {
@ -158,67 +155,6 @@ u8 min_velocity()
return g_cfg_rb3drums.minimum_velocity; return g_cfg_rb3drums.minimum_velocity;
} }
enum class Id : u8
{
// Each 'Note' can be triggered by multiple different numbers.
// Keeping them flattened in an enum for simplicity / switch statement usage.
// These follow the rockband 3 midi pro adapter support.
Snare0 = 38,
Snare1 = 31,
Snare2 = 34,
Snare3 = 37,
Snare4 = 39,
HiTom0 = 48,
HiTom1 = 50,
LowTom0 = 45,
LowTom1 = 47,
FloorTom0 = 41,
FloorTom1 = 43,
Hihat0 = 22,
Hihat1 = 26,
Hihat2 = 42,
Hihat3 = 54,
Ride0 = 51,
Ride1 = 53,
Ride2 = 56,
Ride3 = 59,
Crash0 = 49,
Crash1 = 52,
Crash2 = 55,
Crash3 = 57,
Kick0 = 33,
Kick1 = 35,
Kick2 = 36,
HihatPedal = 44,
// These are from alesis nitro mesh max. ymmv.
SnareRim = 40, // midi pro adapter counts this as snare.
HihatWithPedalUp = 46, // The midi pro adapter considers this a normal hihat hit.
HihatPedalPartial = 23, // If pedal is not 100% down, this will be sent instead of a normal hihat hit.
// Internal value used for converting midi CC.
// Values past 127 are not used in midi notes.
MidiCC = 255,
};
// Intermediate mapping regardless of which midi ids triggered it.
enum class Note : u8
{
Invalid,
Kick,
HihatPedal,
Snare,
SnareRim,
HiTom,
LowTom,
FloorTom,
HihatWithPedalUp,
Hihat,
Ride,
Crash,
};
Note str_to_note(const std::string_view name) Note str_to_note(const std::string_view name)
{ {
static const std::unordered_map<std::string_view, Note> mapping{ static const std::unordered_map<std::string_view, Note> mapping{
@ -298,27 +234,21 @@ std::unordered_map<Id, Note> create_id_to_note_mapping()
{Id::Crash2, Note::Crash}, {Id::Crash2, Note::Crash},
{Id::Crash3, Note::Crash}, {Id::Crash3, Note::Crash},
}; };
// Apply configured overrides. // Apply configured overrides.
auto split = fmt::split(g_cfg_rb3drums.midi_overrides.to_string(), {","}); const std::vector<std::string> segments = fmt::split(g_cfg_rb3drums.midi_overrides.to_string(), {","});
for (const auto& segment : split) for (const std::string& segment : segments)
{ {
if (auto midi_override = parse_midi_override(segment)) if (const auto midi_override = parse_midi_override(segment))
{ {
auto id = midi_override->first; const auto id = midi_override->first;
auto note = midi_override->second; const auto note = midi_override->second;
mapping[id] = note; mapping[id] = note;
} }
} }
return mapping; return mapping;
} }
Note id_to_note(Id id)
{
static auto mapping = create_id_to_note_mapping();
auto it = mapping.find(id);
return it != std::end(mapping) ? it->second : Note::Invalid;
}
namespace combo namespace combo
{ {
@ -345,39 +275,18 @@ std::vector<u8> parse_combo(const std::string_view name, const std::string_view
return notes; return notes;
} }
struct Definition
{
std::string name;
std::vector<u8> notes;
std::function<rb3drums::KitState()> create_state;
Definition(std::string name, const std::string_view csv, const std::function<rb3drums::KitState()> create_state)
: name{std::move(name)}
, notes{parse_combo(this->name, csv)}
, create_state{create_state}
{}
};
std::chrono::milliseconds window() std::chrono::milliseconds window()
{ {
return std::chrono::milliseconds{g_cfg_rb3drums.combo_window_ms}; return std::chrono::milliseconds{g_cfg_rb3drums.combo_window_ms};
} }
const std::vector<Definition>& definitions()
{
// Only parse once and cache.
static const std::vector<Definition> defs{
{"start", g_cfg_rb3drums.combo_start.to_string(), []{ return drum::start_state(); }},
{"select", g_cfg_rb3drums.combo_select.to_string(), []{ return drum::select_state(); }},
{"hold kick", g_cfg_rb3drums.combo_toggle_hold_kick.to_string(), []{ return drum::toggle_hold_kick_state(); }}
};
return defs;
}
} }
} // namespace midi } // namespace midi
namespace
{
void set_flag(u8* buf, [[maybe_unused]] std::string_view name, const controller::FlagByIndex& fbi) void set_flag(u8* buf, [[maybe_unused]] std::string_view name, const controller::FlagByIndex& fbi)
{ {
auto i = fbi[drum::INDEX]; auto i = fbi[drum::INDEX];
@ -397,9 +306,18 @@ void set_flag_if_any(u8* buf, std::string_view name, const controller::FlagByInd
} }
usb_device_rb3_midi_drums::Definition::Definition(std::string name, const std::string_view csv, const std::function<rb3drums::KitState()> create_state)
: name{std::move(name)}
, notes{midi::combo::parse_combo(this->name, csv)}
, create_state{create_state}
{}
usb_device_rb3_midi_drums::usb_device_rb3_midi_drums(const std::array<u8, 7>& location, const std::string& device_name) usb_device_rb3_midi_drums::usb_device_rb3_midi_drums(const std::array<u8, 7>& location, const std::string& device_name)
: usb_device_emulated(location) : usb_device_emulated(location)
{ {
m_id_to_note_mapping = midi::create_id_to_note_mapping();
combo.reload_definitions();
UsbDeviceDescriptor descriptor{}; UsbDeviceDescriptor descriptor{};
descriptor.bcdDevice = 0x0200; descriptor.bcdDevice = 0x0200;
descriptor.bDeviceClass = 0x00; descriptor.bDeviceClass = 0x00;
@ -603,6 +521,12 @@ void usb_device_rb3_midi_drums::interrupt_transfer(u32 buf_size, u8* buf, u32 /*
} }
memcpy(buf, bytes.data(), bytes.size()); memcpy(buf, bytes.data(), bytes.size());
if (g_cfg_rb3drums.reload_requested)
{
m_id_to_note_mapping = midi::create_id_to_note_mapping();
combo.reload_definitions();
}
while (true) while (true)
{ {
u8 midi_msg[32]; u8 midi_msg[32];
@ -712,6 +636,12 @@ rb3drums::KitState usb_device_rb3_midi_drums::parse_midi_message(u8* msg, usz si
return rb3drums::KitState{}; return rb3drums::KitState{};
} }
midi::Note usb_device_rb3_midi_drums::id_to_note(midi::Id id)
{
const auto it = m_id_to_note_mapping.find(id);
return it != m_id_to_note_mapping.cend() ? it->second : midi::Note::Invalid;
}
rb3drums::KitState usb_device_rb3_midi_drums::parse_midi_note(const u8 id, const u8 velocity) rb3drums::KitState usb_device_rb3_midi_drums::parse_midi_note(const u8 id, const u8 velocity)
{ {
if (velocity < midi::min_velocity()) if (velocity < midi::min_velocity())
@ -722,7 +652,7 @@ rb3drums::KitState usb_device_rb3_midi_drums::parse_midi_note(const u8 id, const
rb3drums::KitState kit_state{}; rb3drums::KitState kit_state{};
kit_state.expiry = std::chrono::steady_clock::now() + drum::hit_duration(); kit_state.expiry = std::chrono::steady_clock::now() + drum::hit_duration();
auto note = midi::id_to_note(static_cast<midi::Id>(id)); const midi::Note note = id_to_note(static_cast<midi::Id>(id));
switch (note) switch (note)
{ {
case midi::Note::Kick: kit_state.kick_pedal = velocity; break; case midi::Note::Kick: kit_state.kick_pedal = velocity; break;
@ -751,7 +681,8 @@ bool usb_device_rb3_midi_drums::is_midi_cc(const u8 id, const u8 value)
{ {
return false; return false;
} }
auto is_past_threshold = [](u8 value)
const auto is_past_threshold = [](u8 value)
{ {
const u8 threshold = g_cfg_rb3drums.midi_cc_threshold; const u8 threshold = g_cfg_rb3drums.midi_cc_threshold;
return g_cfg_rb3drums.midi_cc_invert_threshold return g_cfg_rb3drums.midi_cc_invert_threshold
@ -834,6 +765,15 @@ bool rb3drums::KitState::is_drum() const
return std::max({snare, hi_tom, low_tom, floor_tom}) >= midi::min_velocity(); return std::max({snare, hi_tom, low_tom, floor_tom}) >= midi::min_velocity();
} }
void usb_device_rb3_midi_drums::ComboTracker::reload_definitions()
{
m_definitions = {
{"start", g_cfg_rb3drums.combo_start.to_string(), []{ return drum::start_state(); }},
{"select", g_cfg_rb3drums.combo_select.to_string(), []{ return drum::select_state(); }},
{"hold kick", g_cfg_rb3drums.combo_toggle_hold_kick.to_string(), []{ return drum::toggle_hold_kick_state(); }}
};
}
void usb_device_rb3_midi_drums::ComboTracker::add(u8 note) void usb_device_rb3_midi_drums::ComboTracker::add(u8 note)
{ {
if (!midi_notes.empty() && std::chrono::steady_clock::now() >= expiry) if (!midi_notes.empty() && std::chrono::steady_clock::now() >= expiry)
@ -843,9 +783,8 @@ void usb_device_rb3_midi_drums::ComboTracker::add(u8 note)
} }
const usz i = midi_notes.size(); const usz i = midi_notes.size();
const auto& defs = midi::combo::definitions();
bool is_in_combo = false; bool is_in_combo = false;
for (const auto& def : defs) for (const auto& def : m_definitions)
{ {
if (i < def.notes.size() && note == def.notes[i]) if (i < def.notes.size() && note == def.notes[i])
{ {
@ -879,7 +818,7 @@ std::optional<rb3drums::KitState> usb_device_rb3_midi_drums::ComboTracker::take_
{ {
return {}; return {};
} }
for (const auto& combo : midi::combo::definitions()) for (const auto& combo : m_definitions)
{ {
if (midi_notes == combo.notes) if (midi_notes == combo.notes)
{ {

View File

@ -38,10 +38,83 @@ struct KitState
bool is_drum() const; bool is_drum() const;
}; };
}; // namespace rb3drums } // namespace rb3drums
namespace midi
{
enum class Id : u8
{
// Each 'Note' can be triggered by multiple different numbers.
// Keeping them flattened in an enum for simplicity / switch statement usage.
// These follow the rockband 3 midi pro adapter support.
Snare0 = 38,
Snare1 = 31,
Snare2 = 34,
Snare3 = 37,
Snare4 = 39,
HiTom0 = 48,
HiTom1 = 50,
LowTom0 = 45,
LowTom1 = 47,
FloorTom0 = 41,
FloorTom1 = 43,
Hihat0 = 22,
Hihat1 = 26,
Hihat2 = 42,
Hihat3 = 54,
Ride0 = 51,
Ride1 = 53,
Ride2 = 56,
Ride3 = 59,
Crash0 = 49,
Crash1 = 52,
Crash2 = 55,
Crash3 = 57,
Kick0 = 33,
Kick1 = 35,
Kick2 = 36,
HihatPedal = 44,
// These are from alesis nitro mesh max. ymmv.
SnareRim = 40, // midi pro adapter counts this as snare.
HihatWithPedalUp = 46, // The midi pro adapter considers this a normal hihat hit.
HihatPedalPartial = 23, // If pedal is not 100% down, this will be sent instead of a normal hihat hit.
// Internal value used for converting midi CC.
// Values past 127 are not used in midi notes.
MidiCC = 255,
};
// Intermediate mapping regardless of which midi ids triggered it.
enum class Note : u8
{
Invalid,
Kick,
HihatPedal,
Snare,
SnareRim,
HiTom,
LowTom,
FloorTom,
HihatWithPedalUp,
Hihat,
Ride,
Crash,
};
}
class usb_device_rb3_midi_drums : public usb_device_emulated class usb_device_rb3_midi_drums : public usb_device_emulated
{ {
public:
usb_device_rb3_midi_drums(const std::array<u8, 7>& location, const std::string& device_name);
~usb_device_rb3_midi_drums();
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
private: private:
usz response_pos{}; usz response_pos{};
bool buttons_enabled{}; bool buttons_enabled{};
@ -50,9 +123,19 @@ private:
bool hold_kick{}; bool hold_kick{};
bool midi_cc_triggered{}; bool midi_cc_triggered{};
struct Definition
{
std::string name;
std::vector<u8> notes;
std::function<rb3drums::KitState()> create_state;
Definition(std::string name, const std::string_view csv, const std::function<rb3drums::KitState()> create_state);
};
class ComboTracker class ComboTracker
{ {
public: public:
void reload_definitions();
void add(u8 note); void add(u8 note);
void reset(); void reset();
std::optional<rb3drums::KitState> take_state(); std::optional<rb3drums::KitState> take_state();
@ -60,18 +143,15 @@ private:
private: private:
std::chrono::steady_clock::time_point expiry; std::chrono::steady_clock::time_point expiry;
std::vector<u8> midi_notes; std::vector<u8> midi_notes;
std::vector<Definition> m_definitions;
}; };
ComboTracker combo; ComboTracker combo;
std::unordered_map<midi::Id, midi::Note> m_id_to_note_mapping;
midi::Note id_to_note(midi::Id id);
rb3drums::KitState parse_midi_message(u8* msg, usz size); rb3drums::KitState parse_midi_message(u8* msg, usz size);
rb3drums::KitState parse_midi_note(u8 id, u8 velocity); rb3drums::KitState parse_midi_note(u8 id, u8 velocity);
bool is_midi_cc(u8 id, u8 value); bool is_midi_cc(u8 id, u8 value);
void write_state(u8* buf, const rb3drums::KitState&); void write_state(u8* buf, const rb3drums::KitState&);
public:
usb_device_rb3_midi_drums(const std::array<u8, 7>& location, const std::string& device_name);
~usb_device_rb3_midi_drums();
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
}; };

View File

@ -27,7 +27,7 @@ bool mouse_config::load()
return false; return false;
} }
void mouse_config::save() const void mouse_config::save()
{ {
fs::pending_file file(cfg_name); fs::pending_file file(cfg_name);
@ -36,6 +36,8 @@ void mouse_config::save() const
file.file.write(to_string()); file.file.write(to_string());
file.commit(); file.commit();
} }
reload_requested = true;
} }
cfg::string& mouse_config::get_button(int code) cfg::string& mouse_config::get_button(int code)

View File

@ -9,18 +9,20 @@ struct mouse_config final : cfg::node
const std::string cfg_name; const std::string cfg_name;
cfg::string mouse_button_1{this, "Button 1", "Mouse Left"}; cfg::string mouse_button_1{ this, "Button 1", "Mouse Left", true };
cfg::string mouse_button_2{this, "Button 2", "Mouse Right"}; cfg::string mouse_button_2{ this, "Button 2", "Mouse Right", true };
cfg::string mouse_button_3{this, "Button 3", "Mouse Middle"}; cfg::string mouse_button_3{ this, "Button 3", "Mouse Middle", true };
cfg::string mouse_button_4{this, "Button 4", ""}; cfg::string mouse_button_4{ this, "Button 4", "", true };
cfg::string mouse_button_5{this, "Button 5", ""}; cfg::string mouse_button_5{ this, "Button 5", "", true };
cfg::string mouse_button_6{this, "Button 6", ""}; cfg::string mouse_button_6{ this, "Button 6", "", true };
cfg::string mouse_button_7{this, "Button 7", ""}; cfg::string mouse_button_7{ this, "Button 7", "", true };
cfg::string mouse_button_8{this, "Button 8", ""}; cfg::string mouse_button_8{ this, "Button 8", "", true };
atomic_t<bool> reload_requested = true;
bool exist() const; bool exist() const;
bool load(); bool load();
void save() const; void save();
cfg::string& get_button(int code); cfg::string& get_button(int code);
}; };

View File

@ -33,7 +33,7 @@ bool cfg_rb3drums::load()
return false; return false;
} }
void cfg_rb3drums::save() const void cfg_rb3drums::save()
{ {
cfg_log.notice("Saving rb3drums config to '%s'", path); cfg_log.notice("Saving rb3drums config to '%s'", path);
@ -41,4 +41,6 @@ void cfg_rb3drums::save() const
{ {
cfg_log.error("Failed to save rb3drums config to '%s' (error=%s)", path, fs::g_tls_error); cfg_log.error("Failed to save rb3drums config to '%s' (error=%s)", path, fs::g_tls_error);
} }
reload_requested = true;
} }

View File

@ -6,22 +6,24 @@ struct cfg_rb3drums final : cfg::node
{ {
cfg_rb3drums(); cfg_rb3drums();
bool load(); bool load();
void save() const; void save();
cfg::uint<1, 100> pulse_ms{this, "Pulse width ms", 30, true}; cfg::uint<1, 100> pulse_ms{this, "Pulse width ms", 30, true};
cfg::uint<1, 127> minimum_velocity{this, "Minimum velocity", 10, true}; cfg::uint<1, 127> minimum_velocity{this, "Minimum velocity", 10, true};
cfg::uint<1, 5000> combo_window_ms{this, "Combo window in milliseconds", 2000, true}; cfg::uint<1, 5000> combo_window_ms{this, "Combo window in milliseconds", 2000, true};
cfg::_bool stagger_cymbals{this, "Stagger cymbal hits", true, true}; cfg::_bool stagger_cymbals{this, "Stagger cymbal hits", true, true};
cfg::string midi_overrides{this, "Midi id to note override", ""}; cfg::string midi_overrides{this, "Midi id to note override", "", true};
cfg::string combo_start{this, "Combo Start", "HihatPedal,HihatPedal,HihatPedal,Snare"}; cfg::string combo_start{this, "Combo Start", "HihatPedal,HihatPedal,HihatPedal,Snare", true};
cfg::string combo_select{this, "Combo Select", "HihatPedal,HihatPedal,HihatPedal,SnareRim"}; cfg::string combo_select{this, "Combo Select", "HihatPedal,HihatPedal,HihatPedal,SnareRim", true};
cfg::string combo_toggle_hold_kick{this, "Combo Toggle Hold Kick", "HihatPedal,HihatPedal,HihatPedal,Kick"}; cfg::string combo_toggle_hold_kick{this, "Combo Toggle Hold Kick", "HihatPedal,HihatPedal,HihatPedal,Kick", true};
cfg::uint<0, 255> midi_cc_status{this, "Midi CC status", 0xB0, true}; cfg::uint<0, 255> midi_cc_status{this, "Midi CC status", 0xB0, true};
cfg::uint<0, 127> midi_cc_number{this, "Midi CC control number", 4, true}; cfg::uint<0, 127> midi_cc_number{this, "Midi CC control number", 4, true};
cfg::uint<0, 127> midi_cc_threshold{this, "Midi CC threshold", 64, true}; cfg::uint<0, 127> midi_cc_threshold{this, "Midi CC threshold", 64, true};
cfg::_bool midi_cc_invert_threshold{this, "Midi CC invert threshold", false, true}; cfg::_bool midi_cc_invert_threshold{this, "Midi CC invert threshold", false, true};
const std::string path; const std::string path;
atomic_t<bool> reload_requested = false;
}; };
extern cfg_rb3drums g_cfg_rb3drums; extern cfg_rb3drums g_cfg_rb3drums;

View File

@ -1549,4 +1549,26 @@ namespace rsx
{ {
return get_format_block_size_in_bytes(format) == 2 ? 0xFFFF : 0xFFFFFF; return get_format_block_size_in_bytes(format) == 2 ? 0xFFFF : 0xFFFFFF;
} }
bool is_texcoord_wrapping_mode(rsx::texture_wrap_mode mode)
{
switch (mode)
{
// Clamping modes
default:
rsx_log.error("Unknown texture wrap mode: %d", static_cast<int>(mode));
[[ fallthrough ]];
case rsx::texture_wrap_mode::border:
case rsx::texture_wrap_mode::clamp:
case rsx::texture_wrap_mode::clamp_to_edge:
case rsx::texture_wrap_mode::mirror_once_clamp_to_edge:
case rsx::texture_wrap_mode::mirror_once_border:
case rsx::texture_wrap_mode::mirror_once_clamp:
return false;
// Wrapping modes
case rsx::texture_wrap_mode::wrap:
case rsx::texture_wrap_mode::mirror:
return true;
}
}
} }

View File

@ -286,4 +286,6 @@ namespace rsx
format_class classify_format(rsx::surface_depth_format2 format); format_class classify_format(rsx::surface_depth_format2 format);
format_class classify_format(u32 gcm_format); format_class classify_format(u32 gcm_format);
bool is_texcoord_wrapping_mode(rsx::texture_wrap_mode mode);
} }

View File

@ -332,6 +332,9 @@ namespace glsl
{ "SEXT_G_BIT" , rsx::texture_control_bits::SEXT_G }, { "SEXT_G_BIT" , rsx::texture_control_bits::SEXT_G },
{ "SEXT_B_BIT" , rsx::texture_control_bits::SEXT_B }, { "SEXT_B_BIT" , rsx::texture_control_bits::SEXT_B },
{ "SEXT_A_BIT" , rsx::texture_control_bits::SEXT_A }, { "SEXT_A_BIT" , rsx::texture_control_bits::SEXT_A },
{ "WRAP_S_BIT", rsx::texture_control_bits::WRAP_S },
{ "WRAP_T_BIT", rsx::texture_control_bits::WRAP_T },
{ "WRAP_R_BIT", rsx::texture_control_bits::WRAP_R },
{ "ALPHAKILL ", rsx::texture_control_bits::ALPHAKILL }, { "ALPHAKILL ", rsx::texture_control_bits::ALPHAKILL },
{ "RENORMALIZE ", rsx::texture_control_bits::RENORMALIZE }, { "RENORMALIZE ", rsx::texture_control_bits::RENORMALIZE },

View File

@ -34,6 +34,9 @@ namespace rsx
FILTERED_MIN, FILTERED_MIN,
UNNORMALIZED_COORDS, UNNORMALIZED_COORDS,
CLAMP_TEXCOORDS_BIT, CLAMP_TEXCOORDS_BIT,
WRAP_S,
WRAP_T,
WRAP_R,
GAMMA_CTRL_MASK = (1 << GAMMA_R) | (1 << GAMMA_G) | (1 << GAMMA_B) | (1 << GAMMA_A), 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_MASK = (1 << EXPAND_R) | (1 << EXPAND_G) | (1 << EXPAND_B) | (1 << EXPAND_A),

View File

@ -27,4 +27,16 @@ vec3 compute2x2DownsampleWeights(const in float coord, const in float uv_step, c
return vec3(1.0 - (computed_weights.x + computed_weights.y), computed_weights.xy); return vec3(1.0 - (computed_weights.x + computed_weights.y), computed_weights.xy);
} }
vec2 texture2DMSCoord(const in vec2 coords, const in uint flags)
{
if (0u == (flags & (WRAP_S_MASK | WRAP_T_MASK)))
{
return coords;
}
const vec2 wrapped_coords = mod(coords, vec2(1.0));
const bvec2 wrap_control_mask = bvec2(uvec2(flags) & uvec2(WRAP_S_MASK, WRAP_T_MASK));
return _select(coords, wrapped_coords, wrap_control_mask);
}
)" )"

View File

@ -12,7 +12,7 @@ vec4 sampleTexture2DMS(in _MSAA_SAMPLER_TYPE_ tex, const in vec2 coords, const i
{ {
const uint flags = TEX_FLAGS(index); const uint flags = TEX_FLAGS(index);
const vec2 scaled_coords = COORD_SCALE2(index, coords); const vec2 scaled_coords = COORD_SCALE2(index, coords);
const vec2 normalized_coords = mod(scaled_coords, vec2(1.0)); const vec2 normalized_coords = texture2DMSCoord(scaled_coords, flags);
const vec2 sample_count = vec2(2., textureSamples(tex) * 0.5); const vec2 sample_count = vec2(2., textureSamples(tex) * 0.5);
const vec2 image_size = textureSize(tex) * sample_count; const vec2 image_size = textureSize(tex) * sample_count;
const ivec2 icoords = ivec2(normalized_coords * image_size); const ivec2 icoords = ivec2(normalized_coords * image_size);

View File

@ -11,6 +11,9 @@ R"(
#define SEXT_G_MASK (1 << SEXT_G_BIT) #define SEXT_G_MASK (1 << SEXT_G_BIT)
#define SEXT_B_MASK (1 << SEXT_B_BIT) #define SEXT_B_MASK (1 << SEXT_B_BIT)
#define SEXT_A_MASK (1 << SEXT_A_BIT) #define SEXT_A_MASK (1 << SEXT_A_BIT)
#define WRAP_S_MASK (1 << WRAP_S_BIT)
#define WRAP_T_MASK (1 << WRAP_T_BIT)
#define WRAP_R_MASK (1 << WRAP_R_BIT)
#define GAMMA_CTRL_MASK (GAMMA_R_MASK | GAMMA_G_MASK | GAMMA_B_MASK | GAMMA_A_MASK) #define GAMMA_CTRL_MASK (GAMMA_R_MASK | GAMMA_G_MASK | GAMMA_B_MASK | GAMMA_A_MASK)
#define SIGN_EXPAND_MASK (EXPAND_R_MASK | EXPAND_G_MASK | EXPAND_B_MASK | EXPAND_A_MASK) #define SIGN_EXPAND_MASK (EXPAND_R_MASK | EXPAND_G_MASK | EXPAND_B_MASK | EXPAND_A_MASK)

View File

@ -2525,14 +2525,28 @@ namespace rsx
} }
} }
if (backend_config.supports_hw_msaa && if (backend_config.supports_hw_msaa && sampler_descriptors[i]->samples > 1)
sampler_descriptors[i]->samples > 1)
{ {
current_fp_texture_state.multisampled_textures |= (1 << i); current_fp_texture_state.multisampled_textures |= (1 << i);
texture_control |= (static_cast<u32>(tex.zfunc()) << texture_control_bits::DEPTH_COMPARE_OP); texture_control |= (static_cast<u32>(tex.zfunc()) << texture_control_bits::DEPTH_COMPARE_OP);
texture_control |= (static_cast<u32>(tex.mag_filter() != rsx::texture_magnify_filter::nearest) << texture_control_bits::FILTERED_MAG); texture_control |= (static_cast<u32>(tex.mag_filter() != rsx::texture_magnify_filter::nearest) << texture_control_bits::FILTERED_MAG);
texture_control |= (static_cast<u32>(tex.min_filter() != rsx::texture_minify_filter::nearest) << texture_control_bits::FILTERED_MIN); texture_control |= (static_cast<u32>(tex.min_filter() != rsx::texture_minify_filter::nearest) << texture_control_bits::FILTERED_MIN);
texture_control |= (((tex.format() & CELL_GCM_TEXTURE_UN) >> 6) << texture_control_bits::UNNORMALIZED_COORDS); texture_control |= (((tex.format() & CELL_GCM_TEXTURE_UN) >> 6) << texture_control_bits::UNNORMALIZED_COORDS);
if (rsx::is_texcoord_wrapping_mode(tex.wrap_s()))
{
texture_control |= (1 << texture_control_bits::WRAP_S);
}
if (rsx::is_texcoord_wrapping_mode(tex.wrap_t()))
{
texture_control |= (1 << texture_control_bits::WRAP_T);
}
if (rsx::is_texcoord_wrapping_mode(tex.wrap_r()))
{
texture_control |= (1 << texture_control_bits::WRAP_R);
}
} }
if (sampler_descriptors[i]->format_class != RSX_FORMAT_CLASS_COLOR) if (sampler_descriptors[i]->format_class != RSX_FORMAT_CLASS_COLOR)

View File

@ -87,6 +87,7 @@ extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const
extern bool ppu_load_rel_exec(const ppu_rel_object&); extern bool ppu_load_rel_exec(const ppu_rel_object&);
extern void send_close_home_menu_cmds(); extern void send_close_home_menu_cmds();
extern void check_microphone_permissions();
extern void signal_system_cache_can_stay(); extern void signal_system_cache_can_stay();
@ -1720,6 +1721,15 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
return game_boot_result::no_errors; return game_boot_result::no_errors;
} }
// Check microphone permissions
if (g_cfg.audio.microphone_type != microphone_handler::null)
{
if (const std::vector<std::string> device_list = fmt::split(g_cfg.audio.microphone_devices.to_string(), {"@@@"}); !device_list.empty())
{
check_microphone_permissions();
}
}
// Detect boot location // Detect boot location
const std::string hdd0_game = vfs::get("/dev_hdd0/game/"); const std::string hdd0_game = vfs::get("/dev_hdd0/game/");
const bool from_hdd0_game = IsPathInsideDir(m_path, hdd0_game); const bool from_hdd0_game = IsPathInsideDir(m_path, hdd0_game);

View File

@ -23,14 +23,7 @@ void basic_mouse_handler::Init(const u32 max_connect)
g_cfg_mouse.from_default(); g_cfg_mouse.from_default();
g_cfg_mouse.load(); g_cfg_mouse.load();
m_buttons[CELL_MOUSE_BUTTON_1] = get_mouse_button(g_cfg_mouse.mouse_button_1); reload_config();
m_buttons[CELL_MOUSE_BUTTON_2] = get_mouse_button(g_cfg_mouse.mouse_button_2);
m_buttons[CELL_MOUSE_BUTTON_3] = get_mouse_button(g_cfg_mouse.mouse_button_3);
m_buttons[CELL_MOUSE_BUTTON_4] = get_mouse_button(g_cfg_mouse.mouse_button_4);
m_buttons[CELL_MOUSE_BUTTON_5] = get_mouse_button(g_cfg_mouse.mouse_button_5);
m_buttons[CELL_MOUSE_BUTTON_6] = get_mouse_button(g_cfg_mouse.mouse_button_6);
m_buttons[CELL_MOUSE_BUTTON_7] = get_mouse_button(g_cfg_mouse.mouse_button_7);
m_buttons[CELL_MOUSE_BUTTON_8] = get_mouse_button(g_cfg_mouse.mouse_button_8);
m_mice.clear(); m_mice.clear();
m_mice.emplace_back(Mouse()); m_mice.emplace_back(Mouse());
@ -52,6 +45,18 @@ void basic_mouse_handler::Init(const u32 max_connect)
type = mouse_handler::basic; type = mouse_handler::basic;
} }
void basic_mouse_handler::reload_config()
{
m_buttons[CELL_MOUSE_BUTTON_1] = get_mouse_button(g_cfg_mouse.mouse_button_1);
m_buttons[CELL_MOUSE_BUTTON_2] = get_mouse_button(g_cfg_mouse.mouse_button_2);
m_buttons[CELL_MOUSE_BUTTON_3] = get_mouse_button(g_cfg_mouse.mouse_button_3);
m_buttons[CELL_MOUSE_BUTTON_4] = get_mouse_button(g_cfg_mouse.mouse_button_4);
m_buttons[CELL_MOUSE_BUTTON_5] = get_mouse_button(g_cfg_mouse.mouse_button_5);
m_buttons[CELL_MOUSE_BUTTON_6] = get_mouse_button(g_cfg_mouse.mouse_button_6);
m_buttons[CELL_MOUSE_BUTTON_7] = get_mouse_button(g_cfg_mouse.mouse_button_7);
m_buttons[CELL_MOUSE_BUTTON_8] = get_mouse_button(g_cfg_mouse.mouse_button_8);
}
/* Sets the target window for the event handler, and also installs an event filter on the target. */ /* Sets the target window for the event handler, and also installs an event filter on the target. */
void basic_mouse_handler::SetTargetWindow(QWindow* target) void basic_mouse_handler::SetTargetWindow(QWindow* target)
{ {
@ -80,6 +85,11 @@ bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev)
// !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target) // !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target)
if (!m_target || !m_target->isVisible() || target == m_target) if (!m_target || !m_target->isVisible() || target == m_target)
{ {
if (g_cfg_mouse.reload_requested.exchange(false))
{
reload_config();
}
switch (ev->type()) switch (ev->type())
{ {
case QEvent::MouseButtonPress: case QEvent::MouseButtonPress:

View File

@ -27,9 +27,10 @@ public:
bool eventFilter(QObject* obj, QEvent* ev) override; bool eventFilter(QObject* obj, QEvent* ev) override;
private: private:
QWindow* m_target = nullptr; void reload_config();
bool get_mouse_lock_state() const; bool get_mouse_lock_state() const;
static int get_mouse_button(const cfg::string& button); static int get_mouse_button(const cfg::string& button);
QWindow* m_target = nullptr;
std::map<u8, int> m_buttons; std::map<u8, int> m_buttons;
}; };

View File

@ -6,6 +6,8 @@
LOG_CHANNEL(ds3_log, "DS3"); LOG_CHANNEL(ds3_log, "DS3");
using namespace reports;
constexpr std::array<u8, 6> battery_capacity = {0, 1, 25, 50, 75, 100}; constexpr std::array<u8, 6> battery_capacity = {0, 1, 25, 50, 75, 100};
constexpr id_pair SONY_DS3_ID_0 = {0x054C, 0x0268}; constexpr id_pair SONY_DS3_ID_0 = {0x054C, 0x0268};

View File

@ -4,7 +4,7 @@
#include <unordered_map> #include <unordered_map>
namespace namespace reports
{ {
struct ds3_rumble struct ds3_rumble
{ {
@ -67,7 +67,7 @@ public:
#ifdef _WIN32 #ifdef _WIN32
u8 report_id = 0; u8 report_id = 0;
#endif #endif
ds3_input_report report{}; reports::ds3_input_report report{};
}; };
class ds3_pad_handler final : public hid_pad_handler<ds3_device> class ds3_pad_handler final : public hid_pad_handler<ds3_device>

View File

@ -6,6 +6,8 @@
LOG_CHANNEL(ds4_log, "DS4"); LOG_CHANNEL(ds4_log, "DS4");
using namespace reports;
constexpr id_pair SONY_DS4_ID_0 = {0x054C, 0x0BA0}; // Dongle constexpr id_pair SONY_DS4_ID_0 = {0x054C, 0x0BA0}; // Dongle
constexpr id_pair SONY_DS4_ID_1 = {0x054C, 0x05C4}; // CUH-ZCT1x constexpr id_pair SONY_DS4_ID_1 = {0x054C, 0x05C4}; // CUH-ZCT1x
constexpr id_pair SONY_DS4_ID_2 = {0x054C, 0x09CC}; // CUH-ZCT2x constexpr id_pair SONY_DS4_ID_2 = {0x054C, 0x09CC}; // CUH-ZCT2x

View File

@ -4,7 +4,7 @@
#include <unordered_map> #include <unordered_map>
namespace namespace reports
{ {
constexpr u32 DS4_ACC_RES_PER_G = 8192; constexpr u32 DS4_ACC_RES_PER_G = 8192;
constexpr u32 DS4_GYRO_RES_PER_DEG_S = 86; // technically this could be 1024, but keeping it at 86 keeps us within 16 bits of precision constexpr u32 DS4_GYRO_RES_PER_DEG_S = 86; // technically this could be 1024, but keeping it at 86 keeps us within 16 bits of precision
@ -119,8 +119,8 @@ public:
bool bt_controller{false}; bool bt_controller{false};
bool has_calib_data{false}; bool has_calib_data{false};
std::array<CalibData, CalibIndex::COUNT> calib_data{}; std::array<CalibData, CalibIndex::COUNT> calib_data{};
ds4_input_report_usb report_usb{}; reports::ds4_input_report_usb report_usb{};
ds4_input_report_bt report_bt{}; reports::ds4_input_report_bt report_bt{};
}; };
class ds4_pad_handler final : public hid_pad_handler<DS4Device> class ds4_pad_handler final : public hid_pad_handler<DS4Device>

View File

@ -6,6 +6,8 @@
LOG_CHANNEL(dualsense_log, "DualSense"); LOG_CHANNEL(dualsense_log, "DualSense");
using namespace reports;
template <> template <>
void fmt_class_string<DualSenseDevice::DualSenseDataMode>::format(std::string& out, u64 arg) void fmt_class_string<DualSenseDevice::DualSenseDataMode>::format(std::string& out, u64 arg)
{ {

View File

@ -4,7 +4,7 @@
#include <unordered_map> #include <unordered_map>
namespace namespace reports
{ {
constexpr u32 DUALSENSE_ACC_RES_PER_G = 8192; constexpr u32 DUALSENSE_ACC_RES_PER_G = 8192;
constexpr u32 DUALSENSE_GYRO_RES_PER_DEG_S = 86; // technically this could be 1024, but keeping it at 86 keeps us within 16 bits of precision constexpr u32 DUALSENSE_GYRO_RES_PER_DEG_S = 86; // technically this could be 1024, but keeping it at 86 keeps us within 16 bits of precision
@ -172,7 +172,7 @@ public:
u8 bt_sequence{0}; u8 bt_sequence{0};
bool has_calib_data{false}; bool has_calib_data{false};
std::array<CalibData, CalibIndex::COUNT> calib_data{}; std::array<CalibData, CalibIndex::COUNT> calib_data{};
dualsense_input_report_common report{}; // No need to have separate reports for usb and bluetooth reports::dualsense_input_report_common report{}; // No need to have separate reports for usb and bluetooth
DualSenseDataMode data_mode{DualSenseDataMode::Simple}; DualSenseDataMode data_mode{DualSenseDataMode::Simple};
DualSenseFeatureSet feature_set{DualSenseFeatureSet::Normal}; DualSenseFeatureSet feature_set{DualSenseFeatureSet::Normal};
bool init_lightbar{true}; bool init_lightbar{true};

View File

@ -102,4 +102,6 @@ void raw_mice_config::save()
{ {
cfg_log.error("Failed to save %s config to '%s' (error=%s)", cfg_id, cfg_name, fs::g_tls_error); cfg_log.error("Failed to save %s config to '%s' (error=%s)", cfg_id, cfg_name, fs::g_tls_error);
} }
reload_requested = true;
} }

View File

@ -18,14 +18,14 @@ public:
cfg::_float<10, 1000> mouse_acceleration{ this, "Mouse Acceleration", 100.0f, true }; cfg::_float<10, 1000> mouse_acceleration{ this, "Mouse Acceleration", 100.0f, true };
cfg::string mouse_button_1{this, "Button 1", "Button 1"}; cfg::string mouse_button_1{ this, "Button 1", "Button 1", true };
cfg::string mouse_button_2{this, "Button 2", "Button 2"}; cfg::string mouse_button_2{ this, "Button 2", "Button 2", true };
cfg::string mouse_button_3{this, "Button 3", "Button 3"}; cfg::string mouse_button_3{ this, "Button 3", "Button 3", true };
cfg::string mouse_button_4{this, "Button 4", "Button 4"}; cfg::string mouse_button_4{ this, "Button 4", "Button 4", true };
cfg::string mouse_button_5{this, "Button 5", "Button 5"}; cfg::string mouse_button_5{ this, "Button 5", "Button 5", true };
cfg::string mouse_button_6{this, "Button 6", ""}; cfg::string mouse_button_6{ this, "Button 6", "", true };
cfg::string mouse_button_7{this, "Button 7", ""}; cfg::string mouse_button_7{ this, "Button 7", "", true };
cfg::string mouse_button_8{this, "Button 8", ""}; cfg::string mouse_button_8{ this, "Button 8", "", true };
cfg::string& get_button_by_index(int index); cfg::string& get_button_by_index(int index);
cfg::string& get_button(int code); cfg::string& get_button(int code);
@ -38,6 +38,7 @@ struct raw_mice_config : cfg::node
shared_mutex m_mutex; shared_mutex m_mutex;
static constexpr std::string_view cfg_id = "raw_mouse"; static constexpr std::string_view cfg_id = "raw_mouse";
std::array<std::shared_ptr<raw_mouse_config>, 4> players; std::array<std::shared_ptr<raw_mouse_config>, 4> players;
atomic_t<bool> reload_requested = false;
bool load(); bool load();
void save(); void save();

View File

@ -35,6 +35,15 @@ u32 g_registered_handlers = 0;
raw_mouse::raw_mouse(u32 index, const std::string& device_name, void* handle, raw_mouse_handler* handler) raw_mouse::raw_mouse(u32 index, const std::string& device_name, void* handle, raw_mouse_handler* handler)
: m_index(index), m_device_name(device_name), m_handle(handle), m_handler(handler) : m_index(index), m_device_name(device_name), m_handle(handle), m_handler(handler)
{
reload_config();
}
raw_mouse::~raw_mouse()
{
}
void raw_mouse::reload_config()
{ {
if (m_index < ::size32(g_cfg_raw_mouse.players)) if (m_index < ::size32(g_cfg_raw_mouse.players))
{ {
@ -54,10 +63,6 @@ raw_mouse::raw_mouse(u32 index, const std::string& device_name, void* handle, ra
} }
} }
raw_mouse::~raw_mouse()
{
}
std::pair<int, int> raw_mouse::get_mouse_button(const cfg::string& button) std::pair<int, int> raw_mouse::get_mouse_button(const cfg::string& button)
{ {
const std::string value = button.to_string(); const std::string value = button.to_string();
@ -119,6 +124,11 @@ void raw_mouse::update_values(const RAWMOUSE& state)
// Update window handle and size // Update window handle and size
update_window_handle(); update_window_handle();
if (std::exchange(reload_requested, false))
{
reload_config();
}
const auto get_button_pressed = [this](u8 button, int button_flags) const auto get_button_pressed = [this](u8 button, int button_flags)
{ {
const auto& [down, up] = ::at32(m_buttons, button); const auto& [down, up] = ::at32(m_buttons, button);
@ -142,6 +152,9 @@ void raw_mouse::update_values(const RAWMOUSE& state)
get_button_pressed(CELL_MOUSE_BUTTON_3, state.usButtonFlags); get_button_pressed(CELL_MOUSE_BUTTON_3, state.usButtonFlags);
get_button_pressed(CELL_MOUSE_BUTTON_4, state.usButtonFlags); get_button_pressed(CELL_MOUSE_BUTTON_4, state.usButtonFlags);
get_button_pressed(CELL_MOUSE_BUTTON_5, state.usButtonFlags); get_button_pressed(CELL_MOUSE_BUTTON_5, state.usButtonFlags);
get_button_pressed(CELL_MOUSE_BUTTON_6, state.usButtonFlags);
get_button_pressed(CELL_MOUSE_BUTTON_7, state.usButtonFlags);
get_button_pressed(CELL_MOUSE_BUTTON_8, state.usButtonFlags);
// Get mouse wheel // Get mouse wheel
if ((state.usButtonFlags & RI_MOUSE_WHEEL)) if ((state.usButtonFlags & RI_MOUSE_WHEEL))
@ -556,6 +569,14 @@ void raw_mouse_handler::handle_native_event(const MSG& msg)
{ {
std::lock_guard lock(m_raw_mutex); std::lock_guard lock(m_raw_mutex);
if (g_cfg_raw_mouse.reload_requested.exchange(false))
{
for (auto& [handle, mouse] : m_raw_mice)
{
mouse.request_reload();
}
}
if (auto it = m_raw_mice.find(raw_input.header.hDevice); it != m_raw_mice.end()) if (auto it = m_raw_mice.find(raw_input.header.hDevice); it != m_raw_mice.end())
{ {
it->second.update_values(raw_input.data.mouse); it->second.update_values(raw_input.data.mouse);

View File

@ -43,8 +43,10 @@ public:
const std::string& device_name() const { return m_device_name; } const std::string& device_name() const { return m_device_name; }
u32 index() const { return m_index; } u32 index() const { return m_index; }
void set_index(u32 index) { m_index = index; } void set_index(u32 index) { m_index = index; }
void request_reload() { reload_requested = true; }
private: private:
void reload_config();
static std::pair<int, int> get_mouse_button(const cfg::string& button); static std::pair<int, int> get_mouse_button(const cfg::string& button);
u32 m_index = 0; u32 m_index = 0;
@ -60,6 +62,7 @@ private:
float m_mouse_acceleration = 1.0f; float m_mouse_acceleration = 1.0f;
raw_mouse_handler* m_handler{}; raw_mouse_handler* m_handler{};
std::map<u8, std::pair<int, int>> m_buttons; std::map<u8, std::pair<int, int>> m_buttons;
bool reload_requested = false;
}; };
class raw_mouse_handler final : public MouseHandlerBase class raw_mouse_handler final : public MouseHandlerBase

View File

@ -4,6 +4,8 @@
LOG_CHANNEL(skateboard_log, "Skateboard"); LOG_CHANNEL(skateboard_log, "Skateboard");
using namespace reports;
namespace namespace
{ {
constexpr id_pair SKATEBOARD_ID_0 = {0x12BA, 0x0400}; // Tony Hawk RIDE Skateboard constexpr id_pair SKATEBOARD_ID_0 = {0x12BA, 0x0400}; // Tony Hawk RIDE Skateboard

View File

@ -5,7 +5,7 @@
#include <array> #include <array>
#include <unordered_map> #include <unordered_map>
namespace namespace reports
{ {
// Descriptor // Descriptor
// 0x09, 0x05, // Usage (0x05) // 0x09, 0x05, // Usage (0x05)
@ -143,7 +143,7 @@ class skateboard_device : public HidDevice
{ {
public: public:
bool skateboard_is_on = false; bool skateboard_is_on = false;
skateboard_input_report report{}; reports::skateboard_input_report report{};
}; };
class skateboard_pad_handler final : public hid_pad_handler<skateboard_device> class skateboard_pad_handler final : public hid_pad_handler<skateboard_device>

View File

@ -8,6 +8,10 @@
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#if QT_CONFIG(permissions)
#include <QPermissions>
#endif
LOG_CHANNEL(camera_log, "Camera"); LOG_CHANNEL(camera_log, "Camera");
template <> template <>
@ -226,6 +230,27 @@ void camera_settings_dialog::handle_settings_change(int index)
return; return;
} }
#if QT_CONFIG(permissions)
QCameraPermission permission;
switch (qApp->checkPermission(permission))
{
case Qt::PermissionStatus::Undetermined:
camera_log.notice("Requesting camera permission");
qApp->requestPermission(permission, this, [this, index]()
{
handle_settings_change(index);
});
return;
case Qt::PermissionStatus::Denied:
camera_log.error("RPCS3 has no permissions to access cameras on this device.");
QMessageBox::warning(this, tr("Camera permissions denied!"), tr("RPCS3 has no permissions to access cameras on this device."));
return;
case Qt::PermissionStatus::Granted:
camera_log.notice("Camera permission granted");
break;
}
#endif
if (index >= 0 && ui->combo_settings->itemData(index).canConvert<QCameraFormat>() && ui->combo_camera->currentData().canConvert<QCameraDevice>()) if (index >= 0 && ui->combo_settings->itemData(index).canConvert<QCameraFormat>() && ui->combo_camera->currentData().canConvert<QCameraDevice>())
{ {
const QCameraFormat setting = ui->combo_settings->itemData(index).value<QCameraFormat>(); const QCameraFormat setting = ui->combo_settings->itemData(index).value<QCameraFormat>();

View File

@ -79,6 +79,11 @@
#include "ui_main_window.h" #include "ui_main_window.h"
#if QT_CONFIG(permissions)
#include <QGuiApplication>
#include <QPermissions>
#endif
LOG_CHANNEL(gui_log, "GUI"); LOG_CHANNEL(gui_log, "GUI");
extern atomic_t<bool> g_user_asked_for_frame_capture; extern atomic_t<bool> g_user_asked_for_frame_capture;
@ -100,6 +105,32 @@ extern void process_qt_events()
} }
} }
extern void check_microphone_permissions()
{
#if QT_CONFIG(permissions)
Emu.BlockingCallFromMainThread([]()
{
QMicrophonePermission permission;
switch (qApp->checkPermission(permission))
{
case Qt::PermissionStatus::Undetermined:
gui_log.notice("Requesting microphone permission");
qApp->requestPermission(permission, []()
{
check_microphone_permissions();
});
break;
case Qt::PermissionStatus::Denied:
gui_log.error("RPCS3 has no permissions to access microphones on this device.");
break;
case Qt::PermissionStatus::Granted:
gui_log.notice("Microphone permission granted");
break;
}
});
#endif
}
main_window::main_window(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, std::shared_ptr<persistent_settings> persistent_settings, QWidget *parent) main_window::main_window(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, std::shared_ptr<persistent_settings> persistent_settings, QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
, ui(new Ui::main_window) , ui(new Ui::main_window)

View File

@ -6,6 +6,11 @@
#include <QMediaDevices> #include <QMediaDevices>
#if QT_CONFIG(permissions)
#include <QGuiApplication>
#include <QPermissions>
#endif
LOG_CHANNEL(camera_log, "Camera"); LOG_CHANNEL(camera_log, "Camera");
qt_camera_handler::qt_camera_handler() : camera_handler_base() qt_camera_handler::qt_camera_handler() : camera_handler_base()
@ -180,6 +185,26 @@ void qt_camera_handler::start_camera()
return; return;
} }
#if QT_CONFIG(permissions)
QCameraPermission permission;
switch (qApp->checkPermission(permission))
{
case Qt::PermissionStatus::Undetermined:
camera_log.notice("Requesting camera permission");
qApp->requestPermission(permission, [this]()
{
start_camera();
});
return;
case Qt::PermissionStatus::Denied:
camera_log.error("RPCS3 has no permissions to access cameras on this device.");
return;
case Qt::PermissionStatus::Granted:
camera_log.notice("Camera permission granted");
break;
}
#endif
// Start camera. We will start receiving frames now. // Start camera. We will start receiving frames now.
m_camera->start(); m_camera->start();
} }

View File

@ -748,9 +748,9 @@ skylander_dialog* skylander_dialog::get_dlg(QWidget* parent)
void skylander_dialog::clear_skylander(u8 slot) void skylander_dialog::clear_skylander(u8 slot)
{ {
if (auto slot_infos = sky_slots[slot]) if (const auto& slot_infos = sky_slots[slot])
{ {
auto [cur_slot, id, var] = slot_infos.value(); const auto& [cur_slot, id, var] = slot_infos.value();
g_skyportal.remove_skylander(cur_slot); g_skyportal.remove_skylander(cur_slot);
sky_slots[slot] = {}; sky_slots[slot] = {};
update_edits(); update_edits();
@ -811,10 +811,10 @@ void skylander_dialog::update_edits()
for (auto i = 0; i < UI_SKY_NUM; i++) for (auto i = 0; i < UI_SKY_NUM; i++)
{ {
QString display_string; QString display_string;
if (auto sd = sky_slots[i]) if (const auto& sd = sky_slots[i])
{ {
auto [portal_slot, sky_id, sky_var] = sd.value(); const auto& [portal_slot, sky_id, sky_var] = sd.value();
auto found_sky = list_skylanders.find(std::make_pair(sky_id, sky_var)); const auto found_sky = list_skylanders.find(std::make_pair(sky_id, sky_var));
if (found_sky != list_skylanders.end()) if (found_sky != list_skylanders.end())
{ {
display_string = QString::fromStdString(found_sky->second); display_string = QString::fromStdString(found_sky->second);