add feature to make gradual taiko calc sync

This commit is contained in:
MaxOhn
2024-02-23 14:20:24 +01:00
parent b4f843fac1
commit 08e5d52006
17 changed files with 307 additions and 146 deletions
+11 -3
View File
@@ -42,8 +42,16 @@ jobs:
- name: Install nextest - name: Install nextest
uses: taiki-e/install-action@nextest uses: taiki-e/install-action@nextest
- name: Run tests - name: Run tests on default features
run: cargo nextest run --no-fail-fast --failure-output=immediate-final run: cargo nextest run --no-default-features --no-fail-fast --failure-output=immediate-final
- name: Run tests with sync feature
run: >
cargo nextest run
--features sync
--filter-expr 'test(util::sync::tests::share_gradual_taiko)'
--filter-expr 'test(taiko::difficulty::gradual::tests::next_and_nth)'
--no-fail-fast --failure-output=immediate-final
- name: Run doctests - name: Run doctests
run: cargo test --doc run: cargo test --no-default-features --doc
+1
View File
@@ -5,6 +5,7 @@ edition = "2021"
[features] [features]
default = [] default = []
sync = []
tracing = ["rosu-map/tracing"] tracing = ["rosu-map/tracing"]
[dependencies] [dependencies]
+3 -2
View File
@@ -48,7 +48,7 @@ let stars = diff_attrs.stars();
// Calculate performance attributes // Calculate performance attributes
let perf_attrs = map.performance() let perf_attrs = map.performance()
// To speed up the calculation, we can use the previous attributes. // To speed up the calculation, we can use the previous attributes.
// **Note** that this should only be done if the map, mode, mods, // **Note** that this should only be done if the map, mode, mods,
// clock rate, and amount of passed objects stay the same. // clock rate, and amount of passed objects stay the same.
// Otherwise, the final attributes will be incorrect. // Otherwise, the final attributes will be incorrect.
.attributes(diff_attrs) .attributes(diff_attrs)
@@ -79,7 +79,8 @@ TODO
| Flag | Description | Dependencies | Flag | Description | Dependencies
| --------- | ----------- | ------------ | --------- | ----------- | ------------
| `default` | No features | | `default` | No features |
| `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is not enabled, errors will be ignored. | [`tracing`] | `sync` | Some gradual calculation types can only be shared across threads if this feature is enabled. This adds a performance penalty so only enable this if really needed. |
| `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is **not** enabled, errors will be ignored. | [`tracing`]
### Bindings ### Bindings
+3 -2
View File
@@ -42,7 +42,7 @@
//! // Calculate performance attributes //! // Calculate performance attributes
//! let perf_attrs = map.performance() //! let perf_attrs = map.performance()
//! // To speed up the calculation, we can use the previous attributes. //! // To speed up the calculation, we can use the previous attributes.
//! // **Note** that this should only be done if the map, mode, mods, //! // **Note** that this should only be done if the map, mode, mods,
//! // clock rate, and amount of passed objects stay the same. //! // clock rate, and amount of passed objects stay the same.
//! // Otherwise, the final attributes will be incorrect. //! // Otherwise, the final attributes will be incorrect.
//! .attributes(diff_attrs) //! .attributes(diff_attrs)
@@ -73,7 +73,8 @@
//! | Flag | Description | Dependencies //! | Flag | Description | Dependencies
//! | --------- | ----------- | ------------ //! | --------- | ----------- | ------------
//! | `default` | No features | //! | `default` | No features |
//! | `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is not enabled, errors will be ignored. | [`tracing`] //! | `sync` | Some gradual calculation types can only be shared across threads if this feature is enabled. This adds a performance penalty so only enable this if really needed. |
//! | `tracing` | Any error encountered during beatmap decoding will be logged through `tracing::error`. If this feature is **not** enabled, errors will be ignored. | [`tracing`]
//! //!
//! ## Bindings //! ## Bindings
//! //!
@@ -1,41 +1,39 @@
use std::{ use crate::{
cell::RefCell, taiko::difficulty::object::TaikoDifficultyObject,
rc::{Rc, Weak}, util::sync::{RefCount, Weak},
}; };
use crate::taiko::difficulty::object::TaikoDifficultyObject;
use super::{mono_streak::MonoStreak, repeating_hit_patterns::RepeatingHitPatterns}; use super::{mono_streak::MonoStreak, repeating_hit_patterns::RepeatingHitPatterns};
#[derive(Debug)] #[derive(Debug)]
pub struct AlternatingMonoPattern { pub struct AlternatingMonoPattern {
pub mono_streaks: Vec<Rc<RefCell<MonoStreak>>>, pub mono_streaks: Vec<RefCount<MonoStreak>>,
pub parent: Option<Weak<RefCell<RepeatingHitPatterns>>>, pub parent: Option<Weak<RepeatingHitPatterns>>,
pub idx: usize, pub idx: usize,
} }
impl AlternatingMonoPattern { impl AlternatingMonoPattern {
pub fn new() -> Rc<RefCell<Self>> { pub fn new() -> RefCount<Self> {
Rc::new(RefCell::new(Self { RefCount::new(Self {
mono_streaks: Vec::new(), mono_streaks: Vec::new(),
parent: None, parent: None,
idx: 0, idx: 0,
})) })
} }
pub fn is_repetition_of(&self, other: &Self) -> bool { pub fn is_repetition_of(&self, other: &Self) -> bool {
self.has_identical_mono_len(other) self.has_identical_mono_len(other)
&& self.mono_streaks.len() == other.mono_streaks.len() && self.mono_streaks.len() == other.mono_streaks.len()
&& self.mono_streaks[0].borrow().hit_type() == other.mono_streaks[0].borrow().hit_type() && self.mono_streaks[0].get().hit_type() == other.mono_streaks[0].get().hit_type()
} }
pub fn has_identical_mono_len(&self, other: &Self) -> bool { pub fn has_identical_mono_len(&self, other: &Self) -> bool {
self.mono_streaks[0].borrow().run_len() == other.mono_streaks[0].borrow().run_len() self.mono_streaks[0].get().run_len() == other.mono_streaks[0].get().run_len()
} }
pub fn first_hit_object(&self) -> Option<Rc<RefCell<TaikoDifficultyObject>>> { pub fn first_hit_object(&self) -> Option<RefCount<TaikoDifficultyObject>> {
self.mono_streaks self.mono_streaks
.first() .first()
.and_then(|mono| mono.borrow().first_hit_object()) .and_then(|mono| mono.get().first_hit_object())
} }
} }
+4 -7
View File
@@ -1,7 +1,4 @@
use std::{ use crate::util::sync::{RefCount, Weak};
cell::RefCell,
rc::{Rc, Weak},
};
use self::{ use self::{
alternating_mono_pattern::AlternatingMonoPattern, mono_streak::MonoStreak, alternating_mono_pattern::AlternatingMonoPattern, mono_streak::MonoStreak,
@@ -15,7 +12,7 @@ pub mod repeating_hit_patterns;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TaikoDifficultyColor { pub struct TaikoDifficultyColor {
pub mono_streak: Option<Weak<RefCell<MonoStreak>>>, pub mono_streak: Option<Weak<MonoStreak>>,
pub alternating_mono_pattern: Option<Weak<RefCell<AlternatingMonoPattern>>>, pub alternating_mono_pattern: Option<Weak<AlternatingMonoPattern>>,
pub repeating_hit_patterns: Option<Rc<RefCell<RepeatingHitPatterns>>>, pub repeating_hit_patterns: Option<RefCount<RepeatingHitPatterns>>,
} }
+10 -12
View File
@@ -1,26 +1,24 @@
use std::{ use crate::{
cell::RefCell, taiko::{difficulty::object::TaikoDifficultyObject, object::HitType},
rc::{Rc, Weak}, util::sync::{RefCount, Weak},
}; };
use crate::taiko::{difficulty::object::TaikoDifficultyObject, object::HitType};
use super::alternating_mono_pattern::AlternatingMonoPattern; use super::alternating_mono_pattern::AlternatingMonoPattern;
#[derive(Debug)] #[derive(Debug)]
pub struct MonoStreak { pub struct MonoStreak {
pub hit_objects: Vec<Weak<RefCell<TaikoDifficultyObject>>>, pub hit_objects: Vec<Weak<TaikoDifficultyObject>>,
pub parent: Option<Weak<RefCell<AlternatingMonoPattern>>>, pub parent: Option<Weak<AlternatingMonoPattern>>,
pub idx: usize, pub idx: usize,
} }
impl MonoStreak { impl MonoStreak {
pub fn new() -> Rc<RefCell<Self>> { pub fn new() -> RefCount<Self> {
Rc::new(RefCell::new(Self { RefCount::new(Self {
hit_objects: Vec::new(), hit_objects: Vec::new(),
parent: None, parent: None,
idx: 0, idx: 0,
})) })
} }
pub fn run_len(&self) -> usize { pub fn run_len(&self) -> usize {
@@ -31,10 +29,10 @@ impl MonoStreak {
self.hit_objects self.hit_objects
.first() .first()
.and_then(Weak::upgrade) .and_then(Weak::upgrade)
.map(|h| h.borrow().base_hit_type) .map(|h| h.get().base_hit_type)
} }
pub fn first_hit_object(&self) -> Option<Rc<RefCell<TaikoDifficultyObject>>> { pub fn first_hit_object(&self) -> Option<RefCount<TaikoDifficultyObject>> {
self.hit_objects.first().and_then(Weak::upgrade) self.hit_objects.first().and_then(Weak::upgrade)
} }
} }
+47 -48
View File
@@ -1,10 +1,9 @@
use std::{ use std::collections::VecDeque;
cell::{Ref, RefCell},
collections::VecDeque,
rc::Rc,
};
use crate::taiko::difficulty::object::TaikoDifficultyObjects; use crate::{
taiko::difficulty::object::TaikoDifficultyObjects,
util::sync::{Ref, RefCount},
};
use super::{ use super::{
alternating_mono_pattern::AlternatingMonoPattern, mono_streak::MonoStreak, alternating_mono_pattern::AlternatingMonoPattern, mono_streak::MonoStreak,
@@ -18,54 +17,54 @@ impl ColorDifficultyPreprocessor {
let hit_patterns = Self::encode(hit_objects); let hit_patterns = Self::encode(hit_objects);
for repeating_hit_pattern in hit_patterns { for repeating_hit_pattern in hit_patterns {
if let Some(obj) = repeating_hit_pattern.borrow().first_hit_object() { if let Some(obj) = repeating_hit_pattern.get().first_hit_object() {
obj.borrow_mut().color.repeating_hit_patterns = obj.get_mut().color.repeating_hit_patterns =
Some(Rc::clone(&repeating_hit_pattern)); Some(RefCount::clone(&repeating_hit_pattern));
} }
let mono_patterns = Ref::map(repeating_hit_pattern.borrow(), |repeating| { let mono_patterns = Ref::map(repeating_hit_pattern.get(), |repeating| {
repeating.alternating_mono_patterns.as_slice() repeating.alternating_mono_patterns.as_slice()
}); });
for (i, mono_pattern) in mono_patterns.iter().enumerate() { for (i, mono_pattern) in mono_patterns.iter().enumerate() {
{ {
let mut mono_pattern = mono_pattern.borrow_mut(); let mut mono_pattern = mono_pattern.get_mut();
mono_pattern.parent = Some(Rc::downgrade(&repeating_hit_pattern)); mono_pattern.parent = Some(RefCount::downgrade(&repeating_hit_pattern));
mono_pattern.idx = i; mono_pattern.idx = i;
} }
if let Some(obj) = mono_pattern.borrow().first_hit_object() { if let Some(obj) = mono_pattern.get().first_hit_object() {
obj.borrow_mut().color.alternating_mono_pattern = obj.get_mut().color.alternating_mono_pattern =
Some(Rc::downgrade(mono_pattern)); Some(RefCount::downgrade(mono_pattern));
} }
let mono_streaks = Ref::map(mono_pattern.borrow(), |alternating| { let mono_streaks = Ref::map(mono_pattern.get(), |alternating| {
alternating.mono_streaks.as_slice() alternating.mono_streaks.as_slice()
}); });
for (j, mono_streak) in mono_streaks.iter().enumerate() { for (j, mono_streak) in mono_streaks.iter().enumerate() {
{ {
let mut borrowed = mono_streak.borrow_mut(); let mut borrowed = mono_streak.get_mut();
borrowed.parent = Some(Rc::downgrade(mono_pattern)); borrowed.parent = Some(RefCount::downgrade(mono_pattern));
borrowed.idx = j; borrowed.idx = j;
} }
if let Some(obj) = mono_streak.borrow().first_hit_object() { if let Some(obj) = mono_streak.get().first_hit_object() {
obj.borrow_mut().color.mono_streak = Some(Rc::downgrade(mono_streak)); obj.get_mut().color.mono_streak = Some(RefCount::downgrade(mono_streak));
}; };
} }
} }
} }
} }
fn encode(data: &TaikoDifficultyObjects) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> { fn encode(data: &TaikoDifficultyObjects) -> Vec<RefCount<RepeatingHitPatterns>> {
let mono_streaks = Self::encode_mono_streaks(data); let mono_streaks = Self::encode_mono_streaks(data);
let alternating_mono_patterns = Self::encode_alternating_mono_pattern(mono_streaks); let alternating_mono_patterns = Self::encode_alternating_mono_pattern(mono_streaks);
Self::encode_repeating_hit_patterns(alternating_mono_patterns) Self::encode_repeating_hit_patterns(alternating_mono_patterns)
} }
fn encode_mono_streaks(data: &TaikoDifficultyObjects) -> Vec<Rc<RefCell<MonoStreak>>> { fn encode_mono_streaks(data: &TaikoDifficultyObjects) -> Vec<RefCount<MonoStreak>> {
let mut data_iter = data.objects.iter(); let mut data_iter = data.objects.iter();
let Some(taiko_object) = data_iter.next() else { let Some(taiko_object) = data_iter.next() else {
@@ -76,15 +75,15 @@ impl ColorDifficultyPreprocessor {
let mut curr_mono_streak = mono_streaks.last(); let mut curr_mono_streak = mono_streaks.last();
if let Some(curr) = curr_mono_streak { if let Some(curr) = curr_mono_streak {
curr.borrow_mut() curr.get_mut()
.hit_objects .hit_objects
.push(Rc::downgrade(taiko_object)); .push(RefCount::downgrade(taiko_object));
} }
for taiko_object in data_iter { for taiko_object in data_iter {
let condition = data let condition = data
.previous_note(&taiko_object.borrow(), 0) .previous_note(&taiko_object.get(), 0)
.filter(|prev| taiko_object.borrow().base_hit_type == prev.borrow().base_hit_type); .filter(|prev| taiko_object.get().base_hit_type == prev.get().base_hit_type);
if condition.is_none() { if condition.is_none() {
mono_streaks.push(MonoStreak::new()); mono_streaks.push(MonoStreak::new());
@@ -92,9 +91,9 @@ impl ColorDifficultyPreprocessor {
} }
if let Some(curr) = curr_mono_streak { if let Some(curr) = curr_mono_streak {
curr.borrow_mut() curr.get_mut()
.hit_objects .hit_objects
.push(Rc::downgrade(taiko_object)); .push(RefCount::downgrade(taiko_object));
} }
} }
@@ -102,8 +101,8 @@ impl ColorDifficultyPreprocessor {
} }
fn encode_alternating_mono_pattern( fn encode_alternating_mono_pattern(
data: Vec<Rc<RefCell<MonoStreak>>>, data: Vec<RefCount<MonoStreak>>,
) -> VecDeque<Rc<RefCell<AlternatingMonoPattern>>> { ) -> VecDeque<RefCount<AlternatingMonoPattern>> {
let mut data = data.into_iter(); let mut data = data.into_iter();
let Some(mono) = data.next() else { let Some(mono) = data.next() else {
@@ -114,14 +113,14 @@ impl ColorDifficultyPreprocessor {
mono_patterns.push_back(AlternatingMonoPattern::new()); mono_patterns.push_back(AlternatingMonoPattern::new());
let mut curr_mono_pattern = mono_patterns.back(); let mut curr_mono_pattern = mono_patterns.back();
let mut prev_run_len = mono.borrow().run_len(); let mut prev_run_len = mono.get().run_len();
if let Some(curr) = curr_mono_pattern { if let Some(curr) = curr_mono_pattern {
curr.borrow_mut().mono_streaks.push(mono); curr.get_mut().mono_streaks.push(mono);
} }
for mono in data { for mono in data {
let run_len = mono.borrow().run_len(); let run_len = mono.get().run_len();
if run_len != prev_run_len { if run_len != prev_run_len {
mono_patterns.push_back(AlternatingMonoPattern::new()); mono_patterns.push_back(AlternatingMonoPattern::new());
@@ -131,7 +130,7 @@ impl ColorDifficultyPreprocessor {
prev_run_len = run_len; prev_run_len = run_len;
if let Some(curr_mono_pattern) = curr_mono_pattern { if let Some(curr_mono_pattern) = curr_mono_pattern {
curr_mono_pattern.borrow_mut().mono_streaks.push(mono); curr_mono_pattern.get_mut().mono_streaks.push(mono);
} }
} }
@@ -139,50 +138,50 @@ impl ColorDifficultyPreprocessor {
} }
fn encode_repeating_hit_patterns( fn encode_repeating_hit_patterns(
mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern>>>, mut data: VecDeque<RefCount<AlternatingMonoPattern>>,
) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> { ) -> Vec<RefCount<RepeatingHitPatterns>> {
let mut hit_patterns = Vec::new(); let mut hit_patterns = Vec::new();
let mut curr_hit_pattern = None; let mut curr_hit_pattern = None;
while !data.is_empty() { while !data.is_empty() {
let old = curr_hit_pattern.as_ref().map(Rc::downgrade); let old = curr_hit_pattern.as_ref().map(RefCount::downgrade);
let curr_hit_pattern = &*curr_hit_pattern.insert(RepeatingHitPatterns::new(old)); let curr_hit_pattern = &*curr_hit_pattern.insert(RepeatingHitPatterns::new(old));
let mut is_coupled = data.get(2).map_or(false, |other| { let mut is_coupled = data
data[0].borrow().is_repetition_of(&other.borrow()) .get(2)
}); .map_or(false, |other| data[0].get().is_repetition_of(&other.get()));
if is_coupled { if is_coupled {
while is_coupled { while is_coupled {
curr_hit_pattern curr_hit_pattern
.borrow_mut() .get_mut()
.alternating_mono_patterns .alternating_mono_patterns
.push(data.pop_front().unwrap()); .push(data.pop_front().unwrap());
is_coupled = data.get(2).map_or(false, |other| { is_coupled = data
data[0].borrow().is_repetition_of(&other.borrow()) .get(2)
}); .map_or(false, |other| data[0].get().is_repetition_of(&other.get()));
} }
for front in data.drain(..2) { for front in data.drain(..2) {
curr_hit_pattern curr_hit_pattern
.borrow_mut() .get_mut()
.alternating_mono_patterns .alternating_mono_patterns
.push(front); .push(front);
} }
} else { } else {
curr_hit_pattern curr_hit_pattern
.borrow_mut() .get_mut()
.alternating_mono_patterns .alternating_mono_patterns
.push(data.pop_front().unwrap()); .push(data.pop_front().unwrap());
} }
hit_patterns.push(Rc::clone(curr_hit_pattern)); hit_patterns.push(RefCount::clone(curr_hit_pattern));
} }
hit_patterns hit_patterns
.iter_mut() .iter_mut()
.for_each(|pattern| pattern.borrow_mut().find_repetition_interval()); .for_each(|pattern| pattern.get_mut().find_repetition_interval());
hit_patterns hit_patterns
} }
@@ -1,10 +1,9 @@
use std::{ use std::cmp;
cell::RefCell,
cmp,
rc::{Rc, Weak},
};
use crate::taiko::difficulty::object::TaikoDifficultyObject; use crate::{
taiko::difficulty::object::TaikoDifficultyObject,
util::sync::{RefCount, Weak},
};
use super::alternating_mono_pattern::AlternatingMonoPattern; use super::alternating_mono_pattern::AlternatingMonoPattern;
@@ -12,18 +11,18 @@ const MAX_REPETITION_INTERVAL: usize = 16;
#[derive(Debug)] #[derive(Debug)]
pub struct RepeatingHitPatterns { pub struct RepeatingHitPatterns {
pub alternating_mono_patterns: Vec<Rc<RefCell<AlternatingMonoPattern>>>, pub alternating_mono_patterns: Vec<RefCount<AlternatingMonoPattern>>,
pub prev: Option<Weak<RefCell<Self>>>, pub prev: Option<Weak<Self>>,
pub repetition_interval: usize, pub repetition_interval: usize,
} }
impl RepeatingHitPatterns { impl RepeatingHitPatterns {
pub fn new(prev: Option<Weak<RefCell<Self>>>) -> Rc<RefCell<Self>> { pub fn new(prev: Option<Weak<Self>>) -> RefCount<Self> {
Rc::new(RefCell::new(Self { RefCount::new(Self {
alternating_mono_patterns: Vec::new(), alternating_mono_patterns: Vec::new(),
prev, prev,
repetition_interval: 0, repetition_interval: 0,
})) })
} }
pub fn find_repetition_interval(&mut self) { pub fn find_repetition_interval(&mut self) {
@@ -34,13 +33,13 @@ impl RepeatingHitPatterns {
let mut interval = 1; let mut interval = 1;
while interval < MAX_REPETITION_INTERVAL { while interval < MAX_REPETITION_INTERVAL {
if self.is_repetition_of(&other.borrow()) { if self.is_repetition_of(&other.get()) {
self.repetition_interval = cmp::min(interval, MAX_REPETITION_INTERVAL); self.repetition_interval = cmp::min(interval, MAX_REPETITION_INTERVAL);
return; return;
} }
let Some(next) = other.borrow().prev.as_ref().and_then(Weak::upgrade) else { let Some(next) = other.get().prev.as_ref().and_then(Weak::upgrade) else {
break; break;
}; };
@@ -60,16 +59,12 @@ impl RepeatingHitPatterns {
.iter() .iter()
.zip(other.alternating_mono_patterns.iter()) .zip(other.alternating_mono_patterns.iter())
.take(2) .take(2)
.all(|(self_pat, other_pat)| { .all(|(self_pat, other_pat)| self_pat.get().has_identical_mono_len(&other_pat.get()))
self_pat
.borrow()
.has_identical_mono_len(&other_pat.borrow())
})
} }
pub fn first_hit_object(&self) -> Option<Rc<RefCell<TaikoDifficultyObject>>> { pub fn first_hit_object(&self) -> Option<RefCount<TaikoDifficultyObject>> {
self.alternating_mono_patterns self.alternating_mono_patterns
.first() .first()
.and_then(|mono| mono.borrow().first_hit_object()) .and_then(|mono| mono.get().first_hit_object())
} }
} }
+7 -6
View File
@@ -1,8 +1,9 @@
use std::{cell::RefCell, mem, rc::Rc, slice::Iter}; use std::{mem, slice::Iter};
use crate::{ use crate::{
model::{beatmap::HitWindows, hit_object::HitObject}, model::{beatmap::HitWindows, hit_object::HitObject},
taiko::TaikoBeatmap, taiko::TaikoBeatmap,
util::sync::RefCount,
ModeDifficulty, ModeDifficulty,
}; };
@@ -52,7 +53,7 @@ pub struct TaikoGradualDifficulty {
pub(crate) clock_rate: f64, pub(crate) clock_rate: f64,
attrs: TaikoDifficultyAttributes, attrs: TaikoDifficultyAttributes,
diff_objects: TaikoDifficultyObjects, diff_objects: TaikoDifficultyObjects,
diff_objects_iter: Iter<'static, Rc<RefCell<TaikoDifficultyObject>>>, diff_objects_iter: Iter<'static, RefCount<TaikoDifficultyObject>>,
peaks: Peaks, peaks: Peaks,
total_hits: usize, total_hits: usize,
first_combos: FirstTwoCombos, first_combos: FirstTwoCombos,
@@ -132,8 +133,8 @@ impl TaikoGradualDifficulty {
} }
fn extend_lifetime( fn extend_lifetime(
iter: Iter<'_, Rc<RefCell<TaikoDifficultyObject>>>, iter: Iter<'_, RefCount<TaikoDifficultyObject>>,
) -> Iter<'static, Rc<RefCell<TaikoDifficultyObject>>> { ) -> Iter<'static, RefCount<TaikoDifficultyObject>> {
// SAFETY: The underlying data will never be moved. // SAFETY: The underlying data will never be moved.
unsafe { mem::transmute(iter) } unsafe { mem::transmute(iter) }
} }
@@ -149,7 +150,7 @@ impl Iterator for TaikoGradualDifficulty {
if self.idx >= 2 { if self.idx >= 2 {
loop { loop {
let curr = self.diff_objects_iter.next()?; let curr = self.diff_objects_iter.next()?;
let borrowed = curr.borrow(); let borrowed = curr.get();
PeaksSkill::new(&mut self.peaks, &self.diff_objects).process(&borrowed); PeaksSkill::new(&mut self.peaks, &self.diff_objects).process(&borrowed);
if borrowed.base_hit_type.is_hit() { if borrowed.base_hit_type.is_hit() {
@@ -236,7 +237,7 @@ impl Iterator for TaikoGradualDifficulty {
for _ in 0..take { for _ in 0..take {
loop { loop {
let curr = self.diff_objects_iter.next()?; let curr = self.diff_objects_iter.next()?;
let borrowed = curr.borrow(); let borrowed = curr.get();
peaks.process(&borrowed); peaks.process(&borrowed);
if borrowed.base_hit_type.is_hit() { if borrowed.base_hit_type.is_hit() {
+1 -1
View File
@@ -98,7 +98,7 @@ impl DifficultyValues {
let mut peaks = PeaksSkill::new(&mut peaks, &diff_objects); let mut peaks = PeaksSkill::new(&mut peaks, &diff_objects);
for hit_object in diff_objects.iter().take(n_diff_objects) { for hit_object in diff_objects.iter().take(n_diff_objects) {
peaks.process(&hit_object.borrow()); peaks.process(&hit_object.get());
} }
} }
+17 -16
View File
@@ -1,8 +1,9 @@
use std::{cell::RefCell, rc::Rc, slice::Iter}; use std::slice::Iter;
use crate::{ use crate::{
any::difficulty::object::IDifficultyObject, any::difficulty::object::IDifficultyObject,
taiko::object::{HitType, TaikoObject}, taiko::object::{HitType, TaikoObject},
util::sync::RefCount,
}; };
use super::{color::TaikoDifficultyColor, rhythm::HitObjectRhythm}; use super::{color::TaikoDifficultyColor, rhythm::HitObjectRhythm};
@@ -27,7 +28,7 @@ impl TaikoDifficultyObject {
clock_rate: f64, clock_rate: f64,
idx: usize, idx: usize,
objects: &mut TaikoDifficultyObjects, objects: &mut TaikoDifficultyObjects,
) -> Rc<RefCell<Self>> { ) -> RefCount<Self> {
let delta_time = (hit_object.start_time - last_object.start_time) / clock_rate; let delta_time = (hit_object.start_time - last_object.start_time) / clock_rate;
let rhythm = closest_rhythm(delta_time, last_object, last_last_object, clock_rate); let rhythm = closest_rhythm(delta_time, last_object, last_last_object, clock_rate);
let color = TaikoDifficultyColor::default(); let color = TaikoDifficultyColor::default();
@@ -47,7 +48,7 @@ impl TaikoDifficultyObject {
HitType::NonHit => MonoIndex::None, HitType::NonHit => MonoIndex::None,
}; };
let this = Rc::new(RefCell::new(Self { let this = RefCount::new(Self {
idx, idx,
delta_time, delta_time,
start_time: hit_object.start_time / clock_rate, start_time: hit_object.start_time / clock_rate,
@@ -56,16 +57,16 @@ impl TaikoDifficultyObject {
note_idx, note_idx,
rhythm, rhythm,
color, color,
})); });
match hit_object.hit_type { match hit_object.hit_type {
HitType::Center => { HitType::Center => {
objects.note_objects.push(Rc::clone(&this)); objects.note_objects.push(RefCount::clone(&this));
objects.center_hit_objects.push(Rc::clone(&this)); objects.center_hit_objects.push(RefCount::clone(&this));
} }
HitType::Rim => { HitType::Rim => {
objects.note_objects.push(Rc::clone(&this)); objects.note_objects.push(RefCount::clone(&this));
objects.rim_hit_objects.push(Rc::clone(&this)); objects.rim_hit_objects.push(RefCount::clone(&this));
} }
HitType::NonHit => {} HitType::NonHit => {}
} }
@@ -82,10 +83,10 @@ pub enum MonoIndex {
} }
pub struct TaikoDifficultyObjects { pub struct TaikoDifficultyObjects {
pub objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>, pub objects: Vec<RefCount<TaikoDifficultyObject>>,
pub center_hit_objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>, pub center_hit_objects: Vec<RefCount<TaikoDifficultyObject>>,
pub rim_hit_objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>, pub rim_hit_objects: Vec<RefCount<TaikoDifficultyObject>>,
pub note_objects: Vec<Rc<RefCell<TaikoDifficultyObject>>>, pub note_objects: Vec<RefCount<TaikoDifficultyObject>>,
} }
impl TaikoDifficultyObjects { impl TaikoDifficultyObjects {
@@ -101,7 +102,7 @@ impl TaikoDifficultyObjects {
} }
} }
pub fn push(&mut self, hit_object: Rc<RefCell<TaikoDifficultyObject>>) { pub fn push(&mut self, hit_object: RefCount<TaikoDifficultyObject>) {
self.objects.push(hit_object); self.objects.push(hit_object);
} }
@@ -109,7 +110,7 @@ impl TaikoDifficultyObjects {
self.objects.is_empty() self.objects.is_empty()
} }
pub fn iter(&self) -> Iter<'_, Rc<RefCell<TaikoDifficultyObject>>> { pub fn iter(&self) -> Iter<'_, RefCount<TaikoDifficultyObject>> {
self.objects.iter() self.objects.iter()
} }
@@ -117,7 +118,7 @@ impl TaikoDifficultyObjects {
&self, &self,
curr: &TaikoDifficultyObject, curr: &TaikoDifficultyObject,
mut backwards_idx: usize, mut backwards_idx: usize,
) -> Option<&Rc<RefCell<TaikoDifficultyObject>>> { ) -> Option<&RefCount<TaikoDifficultyObject>> {
backwards_idx += 1; backwards_idx += 1;
match curr.mono_idx { match curr.mono_idx {
@@ -135,7 +136,7 @@ impl TaikoDifficultyObjects {
&self, &self,
curr: &TaikoDifficultyObject, curr: &TaikoDifficultyObject,
backwards_idx: usize, backwards_idx: usize,
) -> Option<&Rc<RefCell<TaikoDifficultyObject>>> { ) -> Option<&RefCount<TaikoDifficultyObject>> {
curr.note_idx curr.note_idx
.checked_sub(backwards_idx + 1) .checked_sub(backwards_idx + 1)
.and_then(|idx| self.note_objects.get(idx)) .and_then(|idx| self.note_objects.get(idx))
+9 -12
View File
@@ -1,8 +1,4 @@
use std::{ use std::f64::consts::E;
cell::RefCell,
f64::consts::E,
rc::{Rc, Weak},
};
use crate::{ use crate::{
any::difficulty::{ any::difficulty::{
@@ -16,6 +12,7 @@ use crate::{
}, },
object::{TaikoDifficultyObject, TaikoDifficultyObjects}, object::{TaikoDifficultyObject, TaikoDifficultyObjects},
}, },
util::sync::{RefCount, Weak},
}; };
const SKILL_MULTIPLIER: f64 = 0.12; const SKILL_MULTIPLIER: f64 = 0.12;
@@ -65,7 +62,7 @@ impl Skill<'_, Color> {
fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 { fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
let prev_start_time = curr let prev_start_time = curr
.previous(0, &self.diff_objects.objects) .previous(0, &self.diff_objects.objects)
.map_or(0.0, |prev| prev.borrow().start_time); .map_or(0.0, |prev| prev.get().start_time);
self.inner.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE) self.inner.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE)
} }
@@ -113,8 +110,8 @@ impl ColorEvaluator {
sigmoid * (height / 2.0) + middle sigmoid * (height / 2.0) + middle
} }
fn evaluate_diff_of_mono_streak(mono_streak: &Rc<RefCell<MonoStreak>>) -> f64 { fn evaluate_diff_of_mono_streak(mono_streak: &RefCount<MonoStreak>) -> f64 {
let mono_streak = mono_streak.borrow(); let mono_streak = mono_streak.get();
let parent_eval = mono_streak let parent_eval = mono_streak
.parent .parent
@@ -127,9 +124,9 @@ impl ColorEvaluator {
} }
fn evaluate_diff_of_alternating_mono_pattern( fn evaluate_diff_of_alternating_mono_pattern(
alternating_mono_pattern: &Rc<RefCell<AlternatingMonoPattern>>, alternating_mono_pattern: &RefCount<AlternatingMonoPattern>,
) -> f64 { ) -> f64 {
let alternating_mono_pattern = alternating_mono_pattern.borrow(); let alternating_mono_pattern = alternating_mono_pattern.get();
let parent_eval = alternating_mono_pattern let parent_eval = alternating_mono_pattern
.parent .parent
@@ -142,9 +139,9 @@ impl ColorEvaluator {
} }
fn evaluate_diff_of_repeating_hit_patterns( fn evaluate_diff_of_repeating_hit_patterns(
repeating_hit_patterns: &Rc<RefCell<RepeatingHitPatterns>>, repeating_hit_patterns: &RefCount<RepeatingHitPatterns>,
) -> f64 { ) -> f64 {
let repetition_interval = repeating_hit_patterns.borrow().repetition_interval as f64; let repetition_interval = repeating_hit_patterns.get().repetition_interval as f64;
2.0 * (1.0 - Self::sigmoid(repetition_interval, 2.0, 2.0, 0.5, 1.0)) 2.0 * (1.0 - Self::sigmoid(repetition_interval, 2.0, 2.0, 0.5, 1.0))
} }
+1 -1
View File
@@ -159,7 +159,7 @@ impl Skill<'_, Rhythm> {
fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 { fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
let prev_start_time = curr let prev_start_time = curr
.previous(0, &self.diff_objects.objects) .previous(0, &self.diff_objects.objects)
.map_or(0.0, |prev| prev.borrow().start_time); .map_or(0.0, |prev| prev.get().start_time);
self.inner.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE) self.inner.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE)
} }
+2 -2
View File
@@ -37,7 +37,7 @@ impl Skill<'_, Stamina> {
fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 { fn calculate_initial_strain(&mut self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
let prev_start_time = curr let prev_start_time = curr
.previous(0, &self.diff_objects.objects) .previous(0, &self.diff_objects.objects)
.map_or(0.0, |prev| prev.borrow().start_time); .map_or(0.0, |prev| prev.get().start_time);
self.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE) self.curr_strain() * strain_decay(time - prev_start_time, STRAIN_DECAY_BASE)
} }
@@ -118,7 +118,7 @@ impl StaminaEvaluator {
if let Some(key_prev) = key_prev { if let Some(key_prev) = key_prev {
// * Add a base strain to all objects // * Add a base strain to all objects
0.5 + Self::speed_bonus(taiko_curr.start_time - key_prev.borrow().start_time) 0.5 + Self::speed_bonus(taiko_curr.start_time - key_prev.get().start_time)
} else { } else {
// * There is no previous hit object hit by the current key // * There is no previous hit object hit by the current key
0.0 0.0
+1
View File
@@ -4,3 +4,4 @@ pub mod map_or_attrs;
pub mod mods; pub mod mods;
pub mod random; pub mod random;
pub mod sort; pub mod sort;
pub mod sync;
+163
View File
@@ -0,0 +1,163 @@
use std::fmt;
pub use inner::*;
#[cfg(not(feature = "sync"))]
mod inner {
use std::{cell::RefCell, rc::Rc};
pub struct RefCount<T>(pub(super) Rc<RefCell<T>>);
pub struct Weak<T>(pub(super) std::rc::Weak<RefCell<T>>);
pub type Ref<'a, T> = std::cell::Ref<'a, T>;
pub type RefMut<'a, T> = std::cell::RefMut<'a, T>;
impl<T> RefCount<T> {
pub fn new(inner: T) -> Self {
Self(Rc::new(RefCell::new(inner)))
}
pub fn clone(this: &Self) -> Self {
Self(Rc::clone(&this.0))
}
pub fn downgrade(&self) -> Weak<T> {
Weak(Rc::downgrade(&self.0))
}
pub fn get(&self) -> Ref<'_, T> {
self.0.borrow()
}
pub fn get_mut(&self) -> RefMut<'_, T> {
self.0.borrow_mut()
}
}
}
#[cfg(feature = "sync")]
mod inner {
use std::{
marker::PhantomData,
ops,
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
};
pub struct RefCount<T>(pub(super) Arc<RwLock<T>>);
pub struct Weak<T>(pub(super) std::sync::Weak<RwLock<T>>);
pub struct Ref<'a, T: ?Sized>(RwLockReadGuard<'a, T>);
pub type RefMut<'a, T> = RwLockWriteGuard<'a, T>;
impl<T> RefCount<T> {
pub fn new(inner: T) -> Self {
Self(Arc::new(RwLock::new(inner)))
}
pub fn clone(this: &Self) -> Self {
Self(Arc::clone(&this.0))
}
pub fn downgrade(&self) -> Weak<T> {
Weak(Arc::downgrade(&self.0))
}
pub fn get(&self) -> Ref<'_, T> {
Ref(self.0.read().unwrap())
}
pub fn get_mut(&self) -> RefMut<'_, T> {
self.0.write().unwrap()
}
}
impl<T> Ref<'_, T> {
pub fn map<U: ?Sized, F>(orig: Ref<'_, T>, f: F) -> RefWrap<'_, T, U, F>
where
F: Copy + FnOnce(&T) -> &U,
{
RefWrap {
orig,
access: f,
_phantom: PhantomData,
}
}
}
pub struct RefWrap<'a, T, U: ?Sized, F> {
orig: Ref<'a, T>,
access: F,
_phantom: PhantomData<U>,
}
impl<T, U: ?Sized, F> ops::Deref for RefWrap<'_, T, U, F>
where
F: Copy + FnOnce(&T) -> &U,
{
type Target = U;
fn deref(&self) -> &Self::Target {
(self.access)(&self.orig)
}
}
impl<T: ?Sized> ops::Deref for Ref<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
ops::Deref::deref(&self.0)
}
}
}
impl<T> Weak<T> {
pub fn upgrade(&self) -> Option<RefCount<T>> {
self.0.upgrade().map(RefCount)
}
}
impl<T: fmt::Debug> fmt::Debug for RefCount<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<T: fmt::Debug> fmt::Debug for Weak<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
/// ```compile_fail
/// use rosu_pp::{taiko::TaikoGradualDifficulty, Beatmap, ModeDifficulty};
///
/// let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
/// let difficulty = ModeDifficulty::new();
/// let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
///
/// // Rc<RefCell<_>> cannot be shared across threads so compilation should fail
/// std::thread::spawn(move || { let _ = gradual.next(); });
/// ```
#[cfg(not(feature = "sync"))]
fn _share_gradual_taiko() {}
#[cfg(all(test, feature = "sync"))]
mod tests {
#[test]
fn share_gradual_taiko() {
use crate::{taiko::TaikoGradualDifficulty, Beatmap, ModeDifficulty};
let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
let difficulty = ModeDifficulty::new();
let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
// Arc<RwLock<_>> *can* be shared across threads so this should compile
std::thread::spawn(move || {
let _ = gradual.next();
});
}
}