mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +00:00
Hacks up an [unsafe] return to something best-effort-updater-esque.
For profiling, mainly.
This commit is contained in:
parent
01a309909b
commit
3e2a6ef3f4
@ -20,6 +20,11 @@ inline Nanos nanos_now() {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
inline Seconds seconds(Nanos nanos) {
|
||||
return double(nanos) / 1e9;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* TimeTypes_h */
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace Concurrency {
|
||||
template <typename Performer> class AsyncUpdater {
|
||||
public:
|
||||
template <typename... Args> AsyncUpdater(Args&&... args) :
|
||||
performer_(std::forward<Args>(args)...),
|
||||
performer(std::forward<Args>(args)...),
|
||||
actions_(std::make_unique<ActionVector>()),
|
||||
performer_thread_{
|
||||
[this] {
|
||||
@ -30,7 +30,7 @@ template <typename Performer> class AsyncUpdater {
|
||||
while(!should_quit) {
|
||||
// Wait for new actions to be signalled, and grab them.
|
||||
std::unique_lock lock(condition_mutex_);
|
||||
while(!actions_) {
|
||||
while(actions_->empty()) {
|
||||
condition_.wait(lock);
|
||||
}
|
||||
std::swap(actions, actions_);
|
||||
@ -38,7 +38,7 @@ template <typename Performer> class AsyncUpdater {
|
||||
|
||||
// Update to now.
|
||||
auto time_now = Time::nanos_now();
|
||||
performer_.perform(time_now - last_fired);
|
||||
performer.perform(time_now - last_fired);
|
||||
last_fired = time_now;
|
||||
|
||||
// Perform the actions.
|
||||
@ -68,10 +68,10 @@ template <typename Performer> class AsyncUpdater {
|
||||
performer_thread_.join();
|
||||
}
|
||||
|
||||
private:
|
||||
// The object that will actually receive time advances.
|
||||
Performer performer_;
|
||||
Performer performer;
|
||||
|
||||
private:
|
||||
// The list of actions waiting be performed. These will be elided,
|
||||
// increasing their latency, if the emulation thread falls behind.
|
||||
using ActionVector = std::vector<std::function<void(void)>>;
|
||||
|
@ -15,7 +15,6 @@ class MachineDocument:
|
||||
NSWindowDelegate,
|
||||
CSMachineDelegate,
|
||||
CSScanTargetViewResponderDelegate,
|
||||
CSAudioQueueDelegate,
|
||||
CSROMReciverViewDelegate
|
||||
{
|
||||
// MARK: - Mutual Exclusion.
|
||||
@ -274,17 +273,12 @@ class MachineDocument:
|
||||
if self.audioQueue == nil || self.audioQueue.samplingRate != selectedSamplingRate || self.audioQueue != self.machine.audioQueue {
|
||||
self.machine.audioQueue = nil
|
||||
self.audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate), isStereo:isStereo)
|
||||
self.audioQueue.delegate = self
|
||||
self.machine.audioQueue = self.audioQueue
|
||||
self.machine.setAudioSamplingRate(Float(selectedSamplingRate), bufferSize:audioQueue.preferredBufferSize, stereo:isStereo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Responds to the CSAudioQueueDelegate dry-queue warning message by requesting a machine update.
|
||||
final func audioQueueIsRunningDry(_ audioQueue: CSAudioQueue) {
|
||||
}
|
||||
|
||||
// MARK: - Pasteboard Forwarding.
|
||||
|
||||
/// Forwards any text currently on the pasteboard into the active machine.
|
||||
|
@ -37,9 +37,16 @@
|
||||
|
||||
namespace {
|
||||
|
||||
struct Updater {};
|
||||
struct MachineUpdater {
|
||||
void perform(Time::Nanos duration) {
|
||||
// Top out at 0.1 seconds; this is a safeguard against a negative
|
||||
// feedback loop if emulation starts running slowly.
|
||||
const auto seconds = std::min(Time::seconds(duration), 0.1);
|
||||
machine->run_for(seconds);
|
||||
}
|
||||
|
||||
Concurrency::AsyncUpdater<Updater> updater();
|
||||
MachineTypes::TimedMachine *machine = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -49,6 +56,10 @@ Concurrency::AsyncUpdater<Updater> updater();
|
||||
- (void)addLED:(NSString *)led isPersistent:(BOOL)isPersistent;
|
||||
@end
|
||||
|
||||
@interface CSMachine() <CSAudioQueueDelegate>
|
||||
- (void)audioQueueIsRunningDry:(nonnull CSAudioQueue *)audioQueue;
|
||||
@end
|
||||
|
||||
struct LockProtectedDelegate {
|
||||
// Contractual promise is: machine, the pointer **and** the object **, may be accessed only
|
||||
// in sections protected by the machineAccessLock;
|
||||
@ -106,6 +117,7 @@ struct ActivityObserver: public Activity::Observer {
|
||||
CSStaticAnalyser *_analyser;
|
||||
std::unique_ptr<Machine::DynamicMachine> _machine;
|
||||
MachineTypes::JoystickMachine *_joystickMachine;
|
||||
Concurrency::AsyncUpdater<MachineUpdater> updater;
|
||||
|
||||
CSJoystickManager *_joystickManager;
|
||||
NSMutableArray<CSMachineLED *> *_leds;
|
||||
@ -142,6 +154,7 @@ struct ActivityObserver: public Activity::Observer {
|
||||
[missingROMs appendString:[NSString stringWithUTF8String:wstring_converter.to_bytes(description).c_str()]];
|
||||
return nil;
|
||||
}
|
||||
updater.performer.machine = _machine->timed_machine();
|
||||
|
||||
// Use the keyboard as a joystick if the machine has no keyboard, or if it has a 'non-exclusive' keyboard.
|
||||
_inputMode =
|
||||
@ -219,6 +232,11 @@ struct ActivityObserver: public Activity::Observer {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAudioQueue:(CSAudioQueue *)audioQueue {
|
||||
_audioQueue = audioQueue;
|
||||
audioQueue.delegate = self;
|
||||
}
|
||||
|
||||
- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize stereo:(BOOL)stereo {
|
||||
@synchronized(self) {
|
||||
[self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize stereo:stereo];
|
||||
@ -442,9 +460,12 @@ struct ActivityObserver: public Activity::Observer {
|
||||
}
|
||||
|
||||
- (void)applyInputEvent:(dispatch_block_t)event {
|
||||
@synchronized(_inputEvents) {
|
||||
[_inputEvents addObject:event];
|
||||
}
|
||||
updater.update([event] {
|
||||
event();
|
||||
});
|
||||
// @synchronized(_inputEvents) {
|
||||
// [_inputEvents addObject:event];
|
||||
// }
|
||||
}
|
||||
|
||||
- (void)clearAllKeys {
|
||||
@ -655,9 +676,21 @@ struct ActivityObserver: public Activity::Observer {
|
||||
|
||||
#pragma mark - Timer
|
||||
|
||||
- (void)audioQueueIsRunningDry:(nonnull CSAudioQueue *)audioQueue {
|
||||
// TODO: Make audio flushes overt, and do one here.
|
||||
updater.update([] {});
|
||||
}
|
||||
|
||||
- (void)scanTargetViewDisplayLinkDidFire:(CSScanTargetView *)view now:(const CVTimeStamp *)now outputTime:(const CVTimeStamp *)outputTime {
|
||||
updater.update([self] {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.view updateBacking];
|
||||
[self.view draw];
|
||||
});
|
||||
});
|
||||
|
||||
// First order of business: grab a timestamp.
|
||||
const auto timeNow = Time::nanos_now();
|
||||
/* const auto timeNow = Time::nanos_now();
|
||||
|
||||
BOOL isSyncLocking;
|
||||
@synchronized(self) {
|
||||
@ -682,13 +715,14 @@ struct ActivityObserver: public Activity::Observer {
|
||||
// Draw the current output. (TODO: do this within the timer if either raster racing or, at least, sync matching).
|
||||
if(!isSyncLocking) {
|
||||
[self.view draw];
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
#define TICKS 1000
|
||||
#define TICKS 120
|
||||
|
||||
- (void)start {
|
||||
__block auto lastTime = Time::nanos_now();
|
||||
// updater.performer.machine = _machine->timed_machine();
|
||||
/* __block auto lastTime = Time::nanos_now();
|
||||
|
||||
_timer = [[CSHighPrecisionTimer alloc] initWithTask:^{
|
||||
// Grab the time now and, therefore, the amount of time since the timer last fired
|
||||
@ -762,7 +796,7 @@ struct ActivityObserver: public Activity::Observer {
|
||||
}
|
||||
|
||||
lastTime = timeNow;
|
||||
} interval:uint64_t(1000000000) / uint64_t(TICKS)];
|
||||
} interval:uint64_t(1000000000) / uint64_t(TICKS)];*/
|
||||
}
|
||||
|
||||
#undef TICKS
|
||||
|
Loading…
Reference in New Issue
Block a user