mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-24 12:30:17 +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();
|
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 */
|
#endif /* TimeTypes_h */
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ namespace Concurrency {
|
|||||||
template <typename Performer> class AsyncUpdater {
|
template <typename Performer> class AsyncUpdater {
|
||||||
public:
|
public:
|
||||||
template <typename... Args> AsyncUpdater(Args&&... args) :
|
template <typename... Args> AsyncUpdater(Args&&... args) :
|
||||||
performer_(std::forward<Args>(args)...),
|
performer(std::forward<Args>(args)...),
|
||||||
actions_(std::make_unique<ActionVector>()),
|
actions_(std::make_unique<ActionVector>()),
|
||||||
performer_thread_{
|
performer_thread_{
|
||||||
[this] {
|
[this] {
|
||||||
@ -30,7 +30,7 @@ template <typename Performer> class AsyncUpdater {
|
|||||||
while(!should_quit) {
|
while(!should_quit) {
|
||||||
// Wait for new actions to be signalled, and grab them.
|
// Wait for new actions to be signalled, and grab them.
|
||||||
std::unique_lock lock(condition_mutex_);
|
std::unique_lock lock(condition_mutex_);
|
||||||
while(!actions_) {
|
while(actions_->empty()) {
|
||||||
condition_.wait(lock);
|
condition_.wait(lock);
|
||||||
}
|
}
|
||||||
std::swap(actions, actions_);
|
std::swap(actions, actions_);
|
||||||
@ -38,7 +38,7 @@ template <typename Performer> class AsyncUpdater {
|
|||||||
|
|
||||||
// Update to now.
|
// Update to now.
|
||||||
auto time_now = Time::nanos_now();
|
auto time_now = Time::nanos_now();
|
||||||
performer_.perform(time_now - last_fired);
|
performer.perform(time_now - last_fired);
|
||||||
last_fired = time_now;
|
last_fired = time_now;
|
||||||
|
|
||||||
// Perform the actions.
|
// Perform the actions.
|
||||||
@ -68,10 +68,10 @@ template <typename Performer> class AsyncUpdater {
|
|||||||
performer_thread_.join();
|
performer_thread_.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
// The object that will actually receive time advances.
|
// The object that will actually receive time advances.
|
||||||
Performer performer_;
|
Performer performer;
|
||||||
|
|
||||||
|
private:
|
||||||
// The list of actions waiting be performed. These will be elided,
|
// The list of actions waiting be performed. These will be elided,
|
||||||
// increasing their latency, if the emulation thread falls behind.
|
// increasing their latency, if the emulation thread falls behind.
|
||||||
using ActionVector = std::vector<std::function<void(void)>>;
|
using ActionVector = std::vector<std::function<void(void)>>;
|
||||||
|
@ -15,7 +15,6 @@ class MachineDocument:
|
|||||||
NSWindowDelegate,
|
NSWindowDelegate,
|
||||||
CSMachineDelegate,
|
CSMachineDelegate,
|
||||||
CSScanTargetViewResponderDelegate,
|
CSScanTargetViewResponderDelegate,
|
||||||
CSAudioQueueDelegate,
|
|
||||||
CSROMReciverViewDelegate
|
CSROMReciverViewDelegate
|
||||||
{
|
{
|
||||||
// MARK: - Mutual Exclusion.
|
// MARK: - Mutual Exclusion.
|
||||||
@ -274,17 +273,12 @@ class MachineDocument:
|
|||||||
if self.audioQueue == nil || self.audioQueue.samplingRate != selectedSamplingRate || self.audioQueue != self.machine.audioQueue {
|
if self.audioQueue == nil || self.audioQueue.samplingRate != selectedSamplingRate || self.audioQueue != self.machine.audioQueue {
|
||||||
self.machine.audioQueue = nil
|
self.machine.audioQueue = nil
|
||||||
self.audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate), isStereo:isStereo)
|
self.audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate), isStereo:isStereo)
|
||||||
self.audioQueue.delegate = self
|
|
||||||
self.machine.audioQueue = self.audioQueue
|
self.machine.audioQueue = self.audioQueue
|
||||||
self.machine.setAudioSamplingRate(Float(selectedSamplingRate), bufferSize:audioQueue.preferredBufferSize, stereo:isStereo)
|
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.
|
// MARK: - Pasteboard Forwarding.
|
||||||
|
|
||||||
/// Forwards any text currently on the pasteboard into the active machine.
|
/// Forwards any text currently on the pasteboard into the active machine.
|
||||||
|
@ -37,9 +37,16 @@
|
|||||||
|
|
||||||
namespace {
|
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;
|
- (void)addLED:(NSString *)led isPersistent:(BOOL)isPersistent;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface CSMachine() <CSAudioQueueDelegate>
|
||||||
|
- (void)audioQueueIsRunningDry:(nonnull CSAudioQueue *)audioQueue;
|
||||||
|
@end
|
||||||
|
|
||||||
struct LockProtectedDelegate {
|
struct LockProtectedDelegate {
|
||||||
// Contractual promise is: machine, the pointer **and** the object **, may be accessed only
|
// Contractual promise is: machine, the pointer **and** the object **, may be accessed only
|
||||||
// in sections protected by the machineAccessLock;
|
// in sections protected by the machineAccessLock;
|
||||||
@ -106,6 +117,7 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
CSStaticAnalyser *_analyser;
|
CSStaticAnalyser *_analyser;
|
||||||
std::unique_ptr<Machine::DynamicMachine> _machine;
|
std::unique_ptr<Machine::DynamicMachine> _machine;
|
||||||
MachineTypes::JoystickMachine *_joystickMachine;
|
MachineTypes::JoystickMachine *_joystickMachine;
|
||||||
|
Concurrency::AsyncUpdater<MachineUpdater> updater;
|
||||||
|
|
||||||
CSJoystickManager *_joystickManager;
|
CSJoystickManager *_joystickManager;
|
||||||
NSMutableArray<CSMachineLED *> *_leds;
|
NSMutableArray<CSMachineLED *> *_leds;
|
||||||
@ -142,6 +154,7 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
[missingROMs appendString:[NSString stringWithUTF8String:wstring_converter.to_bytes(description).c_str()]];
|
[missingROMs appendString:[NSString stringWithUTF8String:wstring_converter.to_bytes(description).c_str()]];
|
||||||
return nil;
|
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.
|
// Use the keyboard as a joystick if the machine has no keyboard, or if it has a 'non-exclusive' keyboard.
|
||||||
_inputMode =
|
_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 {
|
- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize stereo:(BOOL)stereo {
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
[self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize stereo:stereo];
|
[self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize stereo:stereo];
|
||||||
@ -442,9 +460,12 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)applyInputEvent:(dispatch_block_t)event {
|
- (void)applyInputEvent:(dispatch_block_t)event {
|
||||||
@synchronized(_inputEvents) {
|
updater.update([event] {
|
||||||
[_inputEvents addObject:event];
|
event();
|
||||||
}
|
});
|
||||||
|
// @synchronized(_inputEvents) {
|
||||||
|
// [_inputEvents addObject:event];
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)clearAllKeys {
|
- (void)clearAllKeys {
|
||||||
@ -655,9 +676,21 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
|
|
||||||
#pragma mark - Timer
|
#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 {
|
- (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.
|
// First order of business: grab a timestamp.
|
||||||
const auto timeNow = Time::nanos_now();
|
/* const auto timeNow = Time::nanos_now();
|
||||||
|
|
||||||
BOOL isSyncLocking;
|
BOOL isSyncLocking;
|
||||||
@synchronized(self) {
|
@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).
|
// Draw the current output. (TODO: do this within the timer if either raster racing or, at least, sync matching).
|
||||||
if(!isSyncLocking) {
|
if(!isSyncLocking) {
|
||||||
[self.view draw];
|
[self.view draw];
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TICKS 1000
|
#define TICKS 120
|
||||||
|
|
||||||
- (void)start {
|
- (void)start {
|
||||||
__block auto lastTime = Time::nanos_now();
|
// updater.performer.machine = _machine->timed_machine();
|
||||||
|
/* __block auto lastTime = Time::nanos_now();
|
||||||
|
|
||||||
_timer = [[CSHighPrecisionTimer alloc] initWithTask:^{
|
_timer = [[CSHighPrecisionTimer alloc] initWithTask:^{
|
||||||
// Grab the time now and, therefore, the amount of time since the timer last fired
|
// 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;
|
lastTime = timeNow;
|
||||||
} interval:uint64_t(1000000000) / uint64_t(TICKS)];
|
} interval:uint64_t(1000000000) / uint64_t(TICKS)];*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef TICKS
|
#undef TICKS
|
||||||
|
Loading…
Reference in New Issue
Block a user