mirror of
https://github.com/RPCS3/rpcs3
synced 2024-07-01 07:04:22 +00:00
Compare commits
17 Commits
08c4c2b2ba
...
bec8258321
Author | SHA1 | Date | |
---|---|---|---|
|
bec8258321 | ||
|
71524271e9 | ||
|
d5923ef808 | ||
|
ef136acb6c | ||
|
a5956cfa82 | ||
|
703de01ebf | ||
|
8343e35146 | ||
|
0679b502f2 | ||
|
e790842007 | ||
|
2b96a3e4ef | ||
|
54ce1343cf | ||
|
ef5cffdead | ||
|
2979c618de | ||
|
a4fae6885e | ||
|
1578d938eb | ||
|
f1399f65ce | ||
|
2c243895c8 |
|
@ -1679,9 +1679,9 @@ void camera_context::operator()()
|
|||
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;
|
||||
|
@ -1819,9 +1819,9 @@ void camera_context::send_attach_state(bool attached)
|
|||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6271,9 +6271,9 @@ public:
|
|||
// Presumably 1/x might result in Zero/NaN when a/x doesn't
|
||||
if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::relaxed)
|
||||
{
|
||||
auto full_fm_accurate = [&](const auto& a, const auto& div)
|
||||
auto full_fm_accurate = [&](const auto& num, const auto& div)
|
||||
{
|
||||
const auto div_result = a / div;
|
||||
const auto div_result = num / div;
|
||||
const auto result_and = bitcast<u32[4]>(div_result) & 0x7fffffffu;
|
||||
const auto result_cmp_inf = sext<s32[4]>(result_and == splat<u32[4]>(0x7F800000u));
|
||||
const auto result_cmp_nan = sext<s32[4]>(result_and <= splat<u32[4]>(0x7F800000u));
|
||||
|
@ -6284,18 +6284,18 @@ public:
|
|||
};
|
||||
|
||||
// FM(a, re_accurate(div))
|
||||
if (const auto [ok_re_acc, div, one] = match_expr(b, re_accurate(match<f32[4]>(), match<f32[4]>())); ok_re_acc)
|
||||
if (const auto [ok_re_acc, div] = match_expr(b, re_accurate(match<f32[4]>())); ok_re_acc)
|
||||
{
|
||||
full_fm_accurate(a, div);
|
||||
erase_stores(one, b);
|
||||
erase_stores(b);
|
||||
return;
|
||||
}
|
||||
|
||||
// FM(re_accurate(div), b)
|
||||
if (const auto [ok_re_acc, div, one] = match_expr(a, re_accurate(match<f32[4]>(), match<f32[4]>())); ok_re_acc)
|
||||
if (const auto [ok_re_acc, div] = match_expr(a, re_accurate(match<f32[4]>())); ok_re_acc)
|
||||
{
|
||||
full_fm_accurate(b, div);
|
||||
erase_stores(one, a);
|
||||
erase_stores(a);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -6599,10 +6599,10 @@ public:
|
|||
return llvm_calli<f32[4], T, U, V>{"spu_fma", {std::forward<T>(a), std::forward<U>(b), std::forward<V>(c)}}.set_order_equality_hint(1, 1, 0);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
static llvm_calli<f32[4], T, U> re_accurate(T&& a, U&& b)
|
||||
template <typename T>
|
||||
static llvm_calli<f32[4], T> re_accurate(T&& a)
|
||||
{
|
||||
return {"spu_re_acc", {std::forward<T>(a), std::forward<U>(b)}};
|
||||
return {"spu_re_acc", {std::forward<T>(a)}};
|
||||
}
|
||||
|
||||
void FMA(spu_opcode_t op)
|
||||
|
@ -6621,198 +6621,159 @@ public:
|
|||
const auto b = value<f32[4]>(ci->getOperand(1));
|
||||
const auto c = value<f32[4]>(ci->getOperand(2));
|
||||
|
||||
if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate)
|
||||
{
|
||||
const auto ma = sext<s32[4]>(fcmp_uno(a != fsplat<f32[4]>(0.)));
|
||||
const auto mb = sext<s32[4]>(fcmp_uno(b != fsplat<f32[4]>(0.)));
|
||||
const auto ca = bitcast<f32[4]>(bitcast<s32[4]>(a) & mb);
|
||||
const auto cb = bitcast<f32[4]>(bitcast<s32[4]>(b) & ma);
|
||||
return fma32x4(eval(ca), eval(cb), c);
|
||||
}
|
||||
else
|
||||
{
|
||||
return fma32x4(a, b, c);
|
||||
}
|
||||
const auto ma = sext<s32[4]>(fcmp_uno(a != fsplat<f32[4]>(0.)));
|
||||
const auto mb = sext<s32[4]>(fcmp_uno(b != fsplat<f32[4]>(0.)));
|
||||
const auto ca = bitcast<f32[4]>(bitcast<s32[4]>(a) & mb);
|
||||
const auto cb = bitcast<f32[4]>(bitcast<s32[4]>(b) & ma);
|
||||
|
||||
return fma32x4(eval(ca), eval(cb), c);
|
||||
});
|
||||
|
||||
register_intrinsic("spu_re_acc", [&](llvm::CallInst* ci)
|
||||
{
|
||||
const auto div = value<f32[4]>(ci->getOperand(0));
|
||||
const auto the_one = value<f32[4]>(ci->getOperand(1));
|
||||
const auto div_result = fsplat<f32[4]>(1.0f) / div;
|
||||
|
||||
const auto div_result = the_one / div;
|
||||
|
||||
// from ps3 hardware testing: Inf => NaN and NaN => Zero
|
||||
const auto result_and = bitcast<u32[4]>(div_result) & 0x7fffffffu;
|
||||
// From ps3 hardware testing: Inf => NaN and NaN => Zero, Signed Zero => Zero
|
||||
// This results in full accuracy within 1ulp(Currently x86 seems to be rounding up?)
|
||||
const auto result_and = bitcast<u32[4]>(div_result) & 0x7FFFFFFFu;
|
||||
const auto result_cmp_inf = sext<s32[4]>(result_and == splat<u32[4]>(0x7F800000u));
|
||||
const auto result_cmp_nan = sext<s32[4]>(result_and <= splat<u32[4]>(0x7F800000u));
|
||||
|
||||
const auto and_mask_zero = bitcast<u32[4]>(sext<s32[4]>(result_and != splat<u32[4]>(0u)));
|
||||
const auto and_mask = bitcast<u32[4]>(result_cmp_nan) & splat<u32[4]>(0xFFFFFFFFu);
|
||||
const auto or_mask = bitcast<u32[4]>(result_cmp_inf) & splat<u32[4]>(0xFFFFFFFu);
|
||||
|
||||
return bitcast<f32[4]>((bitcast<u32[4]>(div_result) & and_mask) | or_mask);
|
||||
return bitcast<f32[4]>(((bitcast<u32[4]>(div_result) & and_mask) & and_mask_zero) | or_mask);
|
||||
});
|
||||
|
||||
constexpr f32 ONEISH = std::bit_cast<f32>(std::bit_cast<u32>(1.0f) + 1);
|
||||
|
||||
const auto [a, b, c] = get_vrs<f32[4]>(op.ra, op.rb, op.rc);
|
||||
static const auto MT = match<f32[4]>();
|
||||
const auto full_expr = fma(a, b, c);
|
||||
|
||||
auto check_sqrt_pattern_for_float = [&](f32 float_value) -> bool
|
||||
// eval_sqrt = x * spu_resqrt(x)
|
||||
// eval_sqrt + (1 - (spu_resqrt(x) * eval_sqrt)) * (0.5 * eval_sqrt)
|
||||
// FMA(FNMS(spu_resqrt(x) <*> eval_sqrt, float_value) <*> FM(0.5f, eval_sqrt), eval_sqrt)
|
||||
if (auto [ok_fm_c, x, maybe_x] = match_expr(c, fm(MT, spu_rsqrte(MT))); ok_fm_c && x.eq(maybe_x))
|
||||
{
|
||||
auto match_fnms = [&](f32 float_value)
|
||||
auto check_sqrt_pattern_for_float = [&](f32 float_value) -> bool
|
||||
{
|
||||
auto res = match_expr(a, fnms(MT, MT, fsplat<f32[4]>(float_value)));
|
||||
if (std::get<0>(res))
|
||||
return res;
|
||||
|
||||
return match_expr(b, fnms(MT, MT, fsplat<f32[4]>(float_value)));
|
||||
if (auto [ok_fma] = match_expr(full_expr, fma(fnms(spu_rsqrte(x), c, fsplat<f32[4]>(float_value)), fm(fsplat<f32[4]>(0.5f), c), c)); ok_fma)
|
||||
{
|
||||
auto [ok_final_fm, to_del] = match_expr(c, fm(x, MT));
|
||||
ensure(ok_final_fm);
|
||||
erase_stores(a, b, c, to_del);
|
||||
set_vr(op.rt4, fsqrt(fabs(x)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto match_fm_half = [&]()
|
||||
if (check_sqrt_pattern_for_float(1.0f))
|
||||
return;
|
||||
|
||||
if (check_sqrt_pattern_for_float(ONEISH))
|
||||
return;
|
||||
|
||||
// Generate dynamic pattern for when floats are unknown because of scope
|
||||
if (auto [ok_fma, fnms_float, fm_float] = match_expr(full_expr, fma(fnms(spu_rsqrte(x), c, MT), fm(MT, c), c)); ok_fma)
|
||||
{
|
||||
auto res = match_expr(a, fm(MT, fsplat<f32[4]>(0.5)));
|
||||
if (std::get<0>(res))
|
||||
return res;
|
||||
erase_stores(a, b, c);
|
||||
const auto bitcast_float = bitcast<u32[4]>(fnms_float);
|
||||
set_vr(op.rt4, select(fcmp_uno(fm_float == fsplat<f32[4]>(0.5f)) & (bitcast_float == splat<u32[4]>(0x3F800000) | bitcast_float == splat<u32[4]>(0x3F800001)), fsqrt(fabs(x)), fma(fnms(spu_rsqrte(x), c, fnms_float), fm(fm_float, fm(x, spu_rsqrte(x))), fm(x, spu_rsqrte(x)))));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
res = match_expr(a, fm(fsplat<f32[4]>(0.5), MT));
|
||||
if (std::get<0>(res))
|
||||
return res;
|
||||
|
||||
res = match_expr(b, fm(MT, fsplat<f32[4]>(0.5)));
|
||||
if (std::get<0>(res))
|
||||
return res;
|
||||
|
||||
return match_expr(b, fm(fsplat<f32[4]>(0.5), MT));
|
||||
if (auto [ok_c, x] = match_expr(c, spu_rsqrte(MT)); ok_c)
|
||||
{
|
||||
auto check_accurate_resqrt_for_float = [&](f32 float_value) -> bool
|
||||
{
|
||||
if (auto [ok_fma] = match_expr(full_expr, fma(fnms(fm(c, c), x, fsplat<f32[4]>(float_value)), fm(fsplat<f32[4]>(0.5f), c), c)); ok_fma)
|
||||
{
|
||||
erase_stores(a, b, c);
|
||||
set_vr(op.rt4, fsplat<f32[4]>(1.0f)/fsqrt(fabs(x)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (check_accurate_resqrt_for_float(1.0f))
|
||||
return;
|
||||
|
||||
if (auto [ok_fnma, a1, b1] = match_fnms(float_value); ok_fnma)
|
||||
if (check_accurate_resqrt_for_float(ONEISH))
|
||||
return;
|
||||
|
||||
// Generate dynamic pattern for when floats are unknown because of scope
|
||||
if (auto [ok_fma, fnms_float, fm_float] = match_expr(full_expr, fma(fnms(fm(c, c), x, MT), fm(MT, c),c)); ok_fma)
|
||||
{
|
||||
if (auto [ok_fm2, fm_half_mul] = match_fm_half(); ok_fm2 && fm_half_mul.eq(b1))
|
||||
{
|
||||
if (fm_half_mul.eq(b1))
|
||||
{
|
||||
if (auto [ok_fm1, a3, b3] = match_expr(c, fm(MT, MT)); ok_fm1 && a3.eq(a1))
|
||||
{
|
||||
if (auto [ok_sqrte, src] = match_expr(a3, spu_rsqrte(MT)); ok_sqrte && src.eq(b3))
|
||||
{
|
||||
erase_stores(a, b, c, a3);
|
||||
set_vr(op.rt4, fsqrt(fabs(src)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (auto [ok_fm1, a3, b3] = match_expr(c, fm(MT, MT)); ok_fm1 && b3.eq(a1))
|
||||
{
|
||||
if (auto [ok_sqrte, src] = match_expr(b3, spu_rsqrte(MT)); ok_sqrte && src.eq(a3))
|
||||
{
|
||||
erase_stores(a, b, c, b3);
|
||||
set_vr(op.rt4, fsqrt(fabs(src)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fm_half_mul.eq(a1))
|
||||
{
|
||||
if (auto [ok_fm1, a3, b3] = match_expr(c, fm(MT, MT)); ok_fm1 && a3.eq(b1))
|
||||
{
|
||||
if (auto [ok_sqrte, src] = match_expr(a3, spu_rsqrte(MT)); ok_sqrte && src.eq(b3))
|
||||
{
|
||||
erase_stores(a, b, c, a3);
|
||||
set_vr(op.rt4, fsqrt(fabs(src)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (auto [ok_fm1, a3, b3] = match_expr(c, fm(MT, MT)); ok_fm1 && b3.eq(b1))
|
||||
{
|
||||
if (auto [ok_sqrte, src] = match_expr(b3, spu_rsqrte(MT)); ok_sqrte && src.eq(a3))
|
||||
{
|
||||
erase_stores(a, b, c, b3);
|
||||
set_vr(op.rt4, fsqrt(fabs(src)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
erase_stores(a, b, c);
|
||||
const auto bitcast_float = bitcast<u32[4]>(fnms_float);
|
||||
set_vr(op.rt4, select(fcmp_uno(fm_float == fsplat<f32[4]>(0.5f)) & (bitcast_float == splat<u32[4]>(0x3F800000) | bitcast_float == splat<u32[4]>(0x3F800001)), fsplat<f32[4]>(1.0f)/fsqrt(fabs(x)), fma(fnms(fm(spu_rsqrte(x), spu_rsqrte(x)), x, fnms_float), fm(fm_float, spu_rsqrte(x)), spu_rsqrte(x))));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (check_sqrt_pattern_for_float(1.0f))
|
||||
return;
|
||||
|
||||
if (check_sqrt_pattern_for_float(std::bit_cast<f32>(std::bit_cast<u32>(1.0f) + 1)))
|
||||
return;
|
||||
|
||||
auto check_accurate_reciprocal_pattern_for_float = [&](f32 float_value) -> bool
|
||||
// Full reciprocal patterns
|
||||
// FMA(FNMS(div <*> spu_re(div), float_value) <*> spu_re(div), spu_re(div))
|
||||
if (auto [ok_c, div] = match_expr(c, spu_re(MT)); ok_c)
|
||||
{
|
||||
// FMA(FNMS(div, spu_re(div), float_value), spu_re(div), spu_re(div))
|
||||
if (auto [ok_fnms, div] = match_expr(a, fnms(MT, b, fsplat<f32[4]>(float_value))); ok_fnms && op.rb == op.rc)
|
||||
auto check_accurate_reciprocal_pattern_for_float = [&](f32 float_value) -> bool
|
||||
{
|
||||
if (auto [ok_re] = match_expr(b, spu_re(div)); ok_re)
|
||||
if (auto [ok_fma] = match_expr(full_expr, fma(fnms(div, c, fsplat<f32[4]>(float_value)), c, c)); ok_fma)
|
||||
{
|
||||
erase_stores(a, b, c);
|
||||
set_vr(op.rt4, re_accurate(div, fsplat<f32[4]>(float_value)));
|
||||
set_vr(op.rt4, re_accurate(div));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// FMA(FNMS(spu_re(div), div, float_value), spu_re(div), spu_re(div))
|
||||
if (auto [ok_fnms, div] = match_expr(a, fnms(b, MT, fsplat<f32[4]>(float_value))); ok_fnms && op.rb == op.rc)
|
||||
if (check_accurate_reciprocal_pattern_for_float(1.0f))
|
||||
return;
|
||||
|
||||
if (check_accurate_reciprocal_pattern_for_float(ONEISH))
|
||||
return;
|
||||
|
||||
// Generate dynamic pattern for when floats are unknown because of scope
|
||||
if (auto [ok_fma, fnms_float] = match_expr(full_expr, fma(fnms(div, c, MT), c, c)); ok_fma)
|
||||
{
|
||||
if (auto [ok_re] = match_expr(b, spu_re(div)); ok_re)
|
||||
{
|
||||
erase_stores(a, b, c);
|
||||
set_vr(op.rt4, re_accurate(div, fsplat<f32[4]>(float_value)));
|
||||
return true;
|
||||
}
|
||||
erase_stores(a, b, c);
|
||||
const auto bitcast_float = bitcast<u32[4]>(fnms_float);
|
||||
set_vr(op.rt4, select(bitcast_float == splat<u32[4]>(0x3F800000) | bitcast_float == splat<u32[4]>(0x3F800001), re_accurate(div), fma(fnms(spu_re(div), div, fnms_float), spu_re(div), spu_re(div))));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// FMA(spu_re(div), FNMS(div, spu_re(div), float_value), spu_re(div))
|
||||
if (auto [ok_fnms, div] = match_expr(b, fnms(MT, a, fsplat<f32[4]>(float_value))); ok_fnms && op.ra == op.rc)
|
||||
{
|
||||
if (auto [ok_re] = match_expr(a, spu_re(div)); ok_re)
|
||||
{
|
||||
erase_stores(a, b, c);
|
||||
set_vr(op.rt4, re_accurate(div, fsplat<f32[4]>(float_value)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// FMA(spu_re(div), FNMS(spu_re(div), div, float_value), spu_re(div))
|
||||
if (auto [ok_fnms, div] = match_expr(b, fnms(a, MT, fsplat<f32[4]>(float_value))); ok_fnms && op.ra == op.rc)
|
||||
{
|
||||
if (auto [ok_re] = match_expr(a, spu_re(div)); ok_re)
|
||||
{
|
||||
erase_stores(a, b, c);
|
||||
set_vr(op.rt4, re_accurate(div, fsplat<f32[4]>(float_value)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (check_accurate_reciprocal_pattern_for_float(1.0f))
|
||||
return;
|
||||
|
||||
if (check_accurate_reciprocal_pattern_for_float(std::bit_cast<f32>(std::bit_cast<u32>(1.0f) + 1)))
|
||||
return;
|
||||
|
||||
// NFS Most Wanted doesn't like this
|
||||
if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::relaxed)
|
||||
{
|
||||
// Those patterns are not safe vs non optimization as inaccuracy from spu_re will spread with early fm before the accuracy is improved
|
||||
|
||||
// Match division (fast)
|
||||
// FMA(FNMS(fm(diva<*> spu_re(divb)), divb, diva), spu_re(divb), fm(diva<*> spu_re(divb)))
|
||||
// FMA(FNMS(fm(diva <*> spu_re(divb)), divb, diva), spu_re(divb), fm(diva <*> spu_re(divb)))
|
||||
// NFS: Most Wanted doesn't like this pattern
|
||||
|
||||
auto full_fast_div = [&](const auto& diva, const auto& divb)
|
||||
{
|
||||
const auto div_result = diva / divb;
|
||||
const auto result_and = bitcast<u32[4]>(div_result) & 0x7FFFFFFFu;
|
||||
const auto result_cmp_inf = sext<s32[4]>(result_and == splat<u32[4]>(0x7F800000u));
|
||||
const auto result_cmp_nan = sext<s32[4]>(result_and <= splat<u32[4]>(0x7F800000u));
|
||||
const auto and_mask_zero = bitcast<u32[4]>(sext<s32[4]>(result_and != splat<u32[4]>(0u)));
|
||||
const auto and_mask = bitcast<u32[4]>(result_cmp_nan) & splat<u32[4]>(0xFFFFFFFFu);
|
||||
const auto or_mask = bitcast<u32[4]>(result_cmp_inf) & splat<u32[4]>(0xFFFFFFFu);
|
||||
const auto final_result = bitcast<f32[4]>(((bitcast<u32[4]>(div_result) & and_mask) & and_mask_zero) | or_mask);
|
||||
set_vr(op.rt4, final_result);
|
||||
};
|
||||
|
||||
if (auto [ok_fnma, divb, diva] = match_expr(a, fnms(c, MT, MT)); ok_fnma)
|
||||
{
|
||||
if (auto [ok_fm, fm1, fm2] = match_expr(c, fm(MT, MT)); ok_fm && ((fm1.eq(diva) && fm2.eq(b)) || (fm1.eq(b) && fm2.eq(diva))))
|
||||
{
|
||||
if (auto [ok_re] = match_expr(b, spu_re(divb)); ok_re)
|
||||
{
|
||||
erase_stores(b, c);
|
||||
set_vr(op.rt4, diva / divb);
|
||||
erase_stores(a, b, c);
|
||||
full_fast_div(diva, divb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -6825,8 +6786,8 @@ public:
|
|||
{
|
||||
if (auto [ok_re] = match_expr(a, spu_re(divb)); ok_re)
|
||||
{
|
||||
erase_stores(a, c);
|
||||
set_vr(op.rt4, diva / divb);
|
||||
erase_stores(a, b, c);
|
||||
full_fast_div(diva, divb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -6840,18 +6801,28 @@ public:
|
|||
{
|
||||
spu_log.todo("[%s:0x%05x] Unmatched spu_re(a) found in FMA", m_hash, m_pos);
|
||||
}
|
||||
|
||||
if (auto [ok_re, mystery] = match_expr(b, spu_re(MT)); ok_re)
|
||||
else if (auto [ok_re, mystery] = match_expr(b, spu_re(MT)); ok_re)
|
||||
{
|
||||
spu_log.todo("[%s:0x%05x] Unmatched spu_re(b) found in FMA", m_hash, m_pos);
|
||||
}
|
||||
|
||||
if (auto [ok_resq, mystery] = match_expr(c, spu_rsqrte(MT)); ok_resq)
|
||||
else if (auto [ok_re, mystery] = match_expr(c, spu_re(MT)); ok_re)
|
||||
{
|
||||
spu_log.todo("[%s:0x%05x] Unmatched spu_re(c) found in FMA", m_hash, m_pos);
|
||||
}
|
||||
else if (auto [ok_resq, mystery] = match_expr(a, spu_rsqrte(MT)); ok_resq)
|
||||
{
|
||||
spu_log.todo("[%s:0x%05x] Unmatched spu_rsqrte(a) found in FMA", m_hash, m_pos);
|
||||
}
|
||||
else if (auto [ok_resq, mystery] = match_expr(b, spu_rsqrte(MT)); ok_resq)
|
||||
{
|
||||
spu_log.todo("[%s:0x%05x] Unmatched spu_rsqrte(b) found in FMA", m_hash, m_pos);
|
||||
}
|
||||
else if (auto [ok_resq, mystery] = match_expr(c, spu_rsqrte(MT)); ok_resq)
|
||||
{
|
||||
spu_log.todo("[%s:0x%05x] Unmatched spu_rsqrte(c) found in FMA", m_hash, m_pos);
|
||||
}
|
||||
|
||||
set_vr(op.rt4, fma(a, b, c));
|
||||
set_vr(op.rt4, full_expr);
|
||||
}
|
||||
|
||||
template <typename T, typename U, typename V>
|
||||
|
|
|
@ -9,9 +9,6 @@ using namespace std::chrono_literals;
|
|||
|
||||
LOG_CHANNEL(rb3_midi_drums_log);
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
namespace controller
|
||||
{
|
||||
|
||||
|
@ -158,67 +155,6 @@ u8 min_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)
|
||||
{
|
||||
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::Crash3, Note::Crash},
|
||||
};
|
||||
|
||||
// Apply configured overrides.
|
||||
auto split = fmt::split(g_cfg_rb3drums.midi_overrides.to_string(), {","});
|
||||
for (const auto& segment : split)
|
||||
const std::vector<std::string> segments = fmt::split(g_cfg_rb3drums.midi_overrides.to_string(), {","});
|
||||
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;
|
||||
auto note = midi_override->second;
|
||||
const auto id = midi_override->first;
|
||||
const auto note = midi_override->second;
|
||||
mapping[id] = note;
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
|
||||
|
@ -345,39 +275,18 @@ std::vector<u8> parse_combo(const std::string_view name, const std::string_view
|
|||
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()
|
||||
{
|
||||
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
|
||||
{
|
||||
|
||||
void set_flag(u8* buf, [[maybe_unused]] std::string_view name, const controller::FlagByIndex& fbi)
|
||||
{
|
||||
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_emulated(location)
|
||||
{
|
||||
m_id_to_note_mapping = midi::create_id_to_note_mapping();
|
||||
combo.reload_definitions();
|
||||
|
||||
UsbDeviceDescriptor descriptor{};
|
||||
descriptor.bcdDevice = 0x0200;
|
||||
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());
|
||||
|
||||
if (g_cfg_rb3drums.reload_requested)
|
||||
{
|
||||
m_id_to_note_mapping = midi::create_id_to_note_mapping();
|
||||
combo.reload_definitions();
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
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{};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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{};
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
auto is_past_threshold = [](u8 value)
|
||||
|
||||
const auto is_past_threshold = [](u8 value)
|
||||
{
|
||||
const u8 threshold = g_cfg_rb3drums.midi_cc_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();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 auto& defs = midi::combo::definitions();
|
||||
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])
|
||||
{
|
||||
|
@ -879,7 +818,7 @@ std::optional<rb3drums::KitState> usb_device_rb3_midi_drums::ComboTracker::take_
|
|||
{
|
||||
return {};
|
||||
}
|
||||
for (const auto& combo : midi::combo::definitions())
|
||||
for (const auto& combo : m_definitions)
|
||||
{
|
||||
if (midi_notes == combo.notes)
|
||||
{
|
||||
|
|
|
@ -38,10 +38,83 @@ struct KitState
|
|||
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
|
||||
{
|
||||
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:
|
||||
usz response_pos{};
|
||||
bool buttons_enabled{};
|
||||
|
@ -50,9 +123,19 @@ private:
|
|||
bool hold_kick{};
|
||||
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
|
||||
{
|
||||
public:
|
||||
void reload_definitions();
|
||||
void add(u8 note);
|
||||
void reset();
|
||||
std::optional<rb3drums::KitState> take_state();
|
||||
|
@ -60,18 +143,15 @@ private:
|
|||
private:
|
||||
std::chrono::steady_clock::time_point expiry;
|
||||
std::vector<u8> midi_notes;
|
||||
std::vector<Definition> m_definitions;
|
||||
};
|
||||
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_note(u8 id, u8 velocity);
|
||||
bool is_midi_cc(u8 id, u8 value);
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -27,7 +27,7 @@ bool mouse_config::load()
|
|||
return false;
|
||||
}
|
||||
|
||||
void mouse_config::save() const
|
||||
void mouse_config::save()
|
||||
{
|
||||
fs::pending_file file(cfg_name);
|
||||
|
||||
|
@ -36,6 +36,8 @@ void mouse_config::save() const
|
|||
file.file.write(to_string());
|
||||
file.commit();
|
||||
}
|
||||
|
||||
reload_requested = true;
|
||||
}
|
||||
|
||||
cfg::string& mouse_config::get_button(int code)
|
||||
|
|
|
@ -9,18 +9,20 @@ struct mouse_config final : cfg::node
|
|||
|
||||
const std::string cfg_name;
|
||||
|
||||
cfg::string mouse_button_1{this, "Button 1", "Mouse Left"};
|
||||
cfg::string mouse_button_2{this, "Button 2", "Mouse Right"};
|
||||
cfg::string mouse_button_3{this, "Button 3", "Mouse Middle"};
|
||||
cfg::string mouse_button_4{this, "Button 4", ""};
|
||||
cfg::string mouse_button_5{this, "Button 5", ""};
|
||||
cfg::string mouse_button_6{this, "Button 6", ""};
|
||||
cfg::string mouse_button_7{this, "Button 7", ""};
|
||||
cfg::string mouse_button_8{this, "Button 8", ""};
|
||||
cfg::string mouse_button_1{ this, "Button 1", "Mouse Left", true };
|
||||
cfg::string mouse_button_2{ this, "Button 2", "Mouse Right", true };
|
||||
cfg::string mouse_button_3{ this, "Button 3", "Mouse Middle", true };
|
||||
cfg::string mouse_button_4{ this, "Button 4", "", true };
|
||||
cfg::string mouse_button_5{ this, "Button 5", "", true };
|
||||
cfg::string mouse_button_6{ this, "Button 6", "", true };
|
||||
cfg::string mouse_button_7{ this, "Button 7", "", true };
|
||||
cfg::string mouse_button_8{ this, "Button 8", "", true };
|
||||
|
||||
atomic_t<bool> reload_requested = true;
|
||||
|
||||
bool exist() const;
|
||||
bool load();
|
||||
void save() const;
|
||||
void save();
|
||||
|
||||
cfg::string& get_button(int code);
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ bool cfg_rb3drums::load()
|
|||
return false;
|
||||
}
|
||||
|
||||
void cfg_rb3drums::save() const
|
||||
void cfg_rb3drums::save()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
reload_requested = true;
|
||||
}
|
||||
|
|
|
@ -6,22 +6,24 @@ struct cfg_rb3drums final : cfg::node
|
|||
{
|
||||
cfg_rb3drums();
|
||||
bool load();
|
||||
void save() const;
|
||||
void save();
|
||||
|
||||
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, 5000> combo_window_ms{this, "Combo window in milliseconds", 2000, true};
|
||||
cfg::_bool stagger_cymbals{this, "Stagger cymbal hits", true, true};
|
||||
cfg::string midi_overrides{this, "Midi id to note override", ""};
|
||||
cfg::string combo_start{this, "Combo Start", "HihatPedal,HihatPedal,HihatPedal,Snare"};
|
||||
cfg::string combo_select{this, "Combo Select", "HihatPedal,HihatPedal,HihatPedal,SnareRim"};
|
||||
cfg::string combo_toggle_hold_kick{this, "Combo Toggle Hold Kick", "HihatPedal,HihatPedal,HihatPedal,Kick"};
|
||||
cfg::string midi_overrides{this, "Midi id to note override", "", true};
|
||||
cfg::string combo_start{this, "Combo Start", "HihatPedal,HihatPedal,HihatPedal,Snare", true};
|
||||
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", 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_threshold{this, "Midi CC threshold", 64, true};
|
||||
cfg::_bool midi_cc_invert_threshold{this, "Midi CC invert threshold", false, true};
|
||||
|
||||
const std::string path;
|
||||
|
||||
atomic_t<bool> reload_requested = false;
|
||||
};
|
||||
|
||||
extern cfg_rb3drums g_cfg_rb3drums;
|
||||
|
|
|
@ -1549,4 +1549,26 @@ namespace rsx
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,4 +286,6 @@ namespace rsx
|
|||
|
||||
format_class classify_format(rsx::surface_depth_format2 format);
|
||||
format_class classify_format(u32 gcm_format);
|
||||
|
||||
bool is_texcoord_wrapping_mode(rsx::texture_wrap_mode mode);
|
||||
}
|
||||
|
|
|
@ -332,6 +332,9 @@ namespace glsl
|
|||
{ "SEXT_G_BIT" , rsx::texture_control_bits::SEXT_G },
|
||||
{ "SEXT_B_BIT" , rsx::texture_control_bits::SEXT_B },
|
||||
{ "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 },
|
||||
{ "RENORMALIZE ", rsx::texture_control_bits::RENORMALIZE },
|
||||
|
|
|
@ -34,6 +34,9 @@ namespace rsx
|
|||
FILTERED_MIN,
|
||||
UNNORMALIZED_COORDS,
|
||||
CLAMP_TEXCOORDS_BIT,
|
||||
WRAP_S,
|
||||
WRAP_T,
|
||||
WRAP_R,
|
||||
|
||||
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),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
)"
|
||||
|
|
|
@ -12,7 +12,7 @@ vec4 sampleTexture2DMS(in _MSAA_SAMPLER_TYPE_ tex, const in vec2 coords, const i
|
|||
{
|
||||
const uint flags = TEX_FLAGS(index);
|
||||
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 image_size = textureSize(tex) * sample_count;
|
||||
const ivec2 icoords = ivec2(normalized_coords * image_size);
|
||||
|
|
|
@ -11,6 +11,9 @@ R"(
|
|||
#define SEXT_G_MASK (1 << SEXT_G_BIT)
|
||||
#define SEXT_B_MASK (1 << SEXT_B_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 SIGN_EXPAND_MASK (EXPAND_R_MASK | EXPAND_G_MASK | EXPAND_B_MASK | EXPAND_A_MASK)
|
||||
|
|
|
@ -2525,14 +2525,28 @@ namespace rsx
|
|||
}
|
||||
}
|
||||
|
||||
if (backend_config.supports_hw_msaa &&
|
||||
sampler_descriptors[i]->samples > 1)
|
||||
if (backend_config.supports_hw_msaa && sampler_descriptors[i]->samples > 1)
|
||||
{
|
||||
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.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 |= (((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)
|
||||
|
|
|
@ -23,14 +23,7 @@ void basic_mouse_handler::Init(const u32 max_connect)
|
|||
g_cfg_mouse.from_default();
|
||||
g_cfg_mouse.load();
|
||||
|
||||
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);
|
||||
reload_config();
|
||||
|
||||
m_mice.clear();
|
||||
m_mice.emplace_back(Mouse());
|
||||
|
@ -52,6 +45,18 @@ void basic_mouse_handler::Init(const u32 max_connect)
|
|||
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. */
|
||||
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)
|
||||
if (!m_target || !m_target->isVisible() || target == m_target)
|
||||
{
|
||||
if (g_cfg_mouse.reload_requested.exchange(false))
|
||||
{
|
||||
reload_config();
|
||||
}
|
||||
|
||||
switch (ev->type())
|
||||
{
|
||||
case QEvent::MouseButtonPress:
|
||||
|
|
|
@ -27,9 +27,10 @@ public:
|
|||
|
||||
bool eventFilter(QObject* obj, QEvent* ev) override;
|
||||
private:
|
||||
QWindow* m_target = nullptr;
|
||||
void reload_config();
|
||||
bool get_mouse_lock_state() const;
|
||||
static int get_mouse_button(const cfg::string& button);
|
||||
|
||||
QWindow* m_target = nullptr;
|
||||
std::map<u8, int> m_buttons;
|
||||
};
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
LOG_CHANNEL(ds3_log, "DS3");
|
||||
|
||||
using namespace reports;
|
||||
|
||||
constexpr std::array<u8, 6> battery_capacity = {0, 1, 25, 50, 75, 100};
|
||||
|
||||
constexpr id_pair SONY_DS3_ID_0 = {0x054C, 0x0268};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
namespace reports
|
||||
{
|
||||
struct ds3_rumble
|
||||
{
|
||||
|
@ -67,7 +67,7 @@ public:
|
|||
#ifdef _WIN32
|
||||
u8 report_id = 0;
|
||||
#endif
|
||||
ds3_input_report report{};
|
||||
reports::ds3_input_report report{};
|
||||
};
|
||||
|
||||
class ds3_pad_handler final : public hid_pad_handler<ds3_device>
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
LOG_CHANNEL(ds4_log, "DS4");
|
||||
|
||||
using namespace reports;
|
||||
|
||||
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_2 = {0x054C, 0x09CC}; // CUH-ZCT2x
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
namespace reports
|
||||
{
|
||||
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
|
||||
|
@ -119,8 +119,8 @@ public:
|
|||
bool bt_controller{false};
|
||||
bool has_calib_data{false};
|
||||
std::array<CalibData, CalibIndex::COUNT> calib_data{};
|
||||
ds4_input_report_usb report_usb{};
|
||||
ds4_input_report_bt report_bt{};
|
||||
reports::ds4_input_report_usb report_usb{};
|
||||
reports::ds4_input_report_bt report_bt{};
|
||||
};
|
||||
|
||||
class ds4_pad_handler final : public hid_pad_handler<DS4Device>
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
LOG_CHANNEL(dualsense_log, "DualSense");
|
||||
|
||||
using namespace reports;
|
||||
|
||||
template <>
|
||||
void fmt_class_string<DualSenseDevice::DualSenseDataMode>::format(std::string& out, u64 arg)
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
namespace reports
|
||||
{
|
||||
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
|
||||
|
@ -172,7 +172,7 @@ public:
|
|||
u8 bt_sequence{0};
|
||||
bool has_calib_data{false};
|
||||
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};
|
||||
DualSenseFeatureSet feature_set{DualSenseFeatureSet::Normal};
|
||||
bool init_lightbar{true};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
reload_requested = true;
|
||||
}
|
||||
|
|
|
@ -18,14 +18,14 @@ public:
|
|||
|
||||
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_2{this, "Button 2", "Button 2"};
|
||||
cfg::string mouse_button_3{this, "Button 3", "Button 3"};
|
||||
cfg::string mouse_button_4{this, "Button 4", "Button 4"};
|
||||
cfg::string mouse_button_5{this, "Button 5", "Button 5"};
|
||||
cfg::string mouse_button_6{this, "Button 6", ""};
|
||||
cfg::string mouse_button_7{this, "Button 7", ""};
|
||||
cfg::string mouse_button_8{this, "Button 8", ""};
|
||||
cfg::string mouse_button_1{ this, "Button 1", "Button 1", true };
|
||||
cfg::string mouse_button_2{ this, "Button 2", "Button 2", true };
|
||||
cfg::string mouse_button_3{ this, "Button 3", "Button 3", true };
|
||||
cfg::string mouse_button_4{ this, "Button 4", "Button 4", true };
|
||||
cfg::string mouse_button_5{ this, "Button 5", "Button 5", true };
|
||||
cfg::string mouse_button_6{ this, "Button 6", "", true };
|
||||
cfg::string mouse_button_7{ this, "Button 7", "", true };
|
||||
cfg::string mouse_button_8{ this, "Button 8", "", true };
|
||||
|
||||
cfg::string& get_button_by_index(int index);
|
||||
cfg::string& get_button(int code);
|
||||
|
@ -38,6 +38,7 @@ struct raw_mice_config : cfg::node
|
|||
shared_mutex m_mutex;
|
||||
static constexpr std::string_view cfg_id = "raw_mouse";
|
||||
std::array<std::shared_ptr<raw_mouse_config>, 4> players;
|
||||
atomic_t<bool> reload_requested = false;
|
||||
|
||||
bool load();
|
||||
void save();
|
||||
|
|
|
@ -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)
|
||||
: 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))
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
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();
|
||||
|
||||
if (std::exchange(reload_requested, false))
|
||||
{
|
||||
reload_config();
|
||||
}
|
||||
|
||||
const auto get_button_pressed = [this](u8 button, int button_flags)
|
||||
{
|
||||
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_4, 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
|
||||
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);
|
||||
|
||||
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())
|
||||
{
|
||||
it->second.update_values(raw_input.data.mouse);
|
||||
|
|
|
@ -43,8 +43,10 @@ public:
|
|||
const std::string& device_name() const { return m_device_name; }
|
||||
u32 index() const { return m_index; }
|
||||
void set_index(u32 index) { m_index = index; }
|
||||
void request_reload() { reload_requested = true; }
|
||||
|
||||
private:
|
||||
void reload_config();
|
||||
static std::pair<int, int> get_mouse_button(const cfg::string& button);
|
||||
|
||||
u32 m_index = 0;
|
||||
|
@ -60,6 +62,7 @@ private:
|
|||
float m_mouse_acceleration = 1.0f;
|
||||
raw_mouse_handler* m_handler{};
|
||||
std::map<u8, std::pair<int, int>> m_buttons;
|
||||
bool reload_requested = false;
|
||||
};
|
||||
|
||||
class raw_mouse_handler final : public MouseHandlerBase
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
LOG_CHANNEL(skateboard_log, "Skateboard");
|
||||
|
||||
using namespace reports;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr id_pair SKATEBOARD_ID_0 = {0x12BA, 0x0400}; // Tony Hawk RIDE Skateboard
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
namespace reports
|
||||
{
|
||||
// Descriptor
|
||||
// 0x09, 0x05, // Usage (0x05)
|
||||
|
@ -143,7 +143,7 @@ class skateboard_device : public HidDevice
|
|||
{
|
||||
public:
|
||||
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>
|
||||
|
|
|
@ -748,9 +748,9 @@ skylander_dialog* skylander_dialog::get_dlg(QWidget* parent)
|
|||
|
||||
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);
|
||||
sky_slots[slot] = {};
|
||||
update_edits();
|
||||
|
@ -811,10 +811,10 @@ void skylander_dialog::update_edits()
|
|||
for (auto i = 0; i < UI_SKY_NUM; i++)
|
||||
{
|
||||
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();
|
||||
auto found_sky = list_skylanders.find(std::make_pair(sky_id, sky_var));
|
||||
const auto& [portal_slot, sky_id, sky_var] = sd.value();
|
||||
const auto found_sky = list_skylanders.find(std::make_pair(sky_id, sky_var));
|
||||
if (found_sky != list_skylanders.end())
|
||||
{
|
||||
display_string = QString::fromStdString(found_sky->second);
|
||||
|
|
Loading…
Reference in New Issue
Block a user