1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-23 20:29:42 +00:00

Back to asynchronous updates and drawing, to try to improve guarantees on audio latency; experimenting with whether other parts of the approach are fundamentally flawed; added a broad-phase for scheduled updates on the 2600.

This commit is contained in:
Thomas Harte 2016-06-02 21:22:55 -04:00
parent 11073daee1
commit e3b95b8d2b
3 changed files with 53 additions and 43 deletions

View File

@ -283,57 +283,63 @@ void Machine::output_pixels(unsigned int count)
{
while(count--)
{
// apply any queued changes and flush the record
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::HMoveSetup)
if(_upcomingEvents[_upcomingEventsPointer].updates)
{
_stateByTime = _stateByExtendTime[1];
// clear any ongoing moves
if(_hMoveFlags)
// apply any queued changes and flush the record
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::HMoveSetup)
{
for(int c = 0; c < number_of_upcoming_events; c++)
// schedule an extended left border
_stateByTime = _stateByExtendTime[1];
// clear any ongoing moves
if(_hMoveFlags)
{
_upcomingEvents[c].updates &= ~(Event::Action::HMoveCompare | Event::Action::HMoveDecrement);
for(int c = 0; c < number_of_upcoming_events; c++)
{
_upcomingEvents[c].updates &= ~(Event::Action::HMoveCompare | Event::Action::HMoveDecrement);
}
}
// schedule new moves
_hMoveFlags = 0x1f;
_hMoveCounter = 15;
// follow-through into a compare immediately
_upcomingEvents[_upcomingEventsPointer].updates |= Event::Action::HMoveCompare;
}
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::HMoveCompare)
{
for(int c = 0; c < 5; c++)
{
if(((_objectMotion[c] >> 4)^_hMoveCounter) == 7)
{
_hMoveFlags &= ~(1 << c);
}
}
if(_hMoveFlags)
{
if(_hMoveCounter) _hMoveCounter--;
_upcomingEvents[(_upcomingEventsPointer+4)%number_of_upcoming_events].updates |= Event::Action::HMoveCompare;
_upcomingEvents[(_upcomingEventsPointer+2)%number_of_upcoming_events].updates |= Event::Action::HMoveDecrement;
}
}
// schedule new moves
_hMoveFlags = 0x1f;
_hMoveCounter = 15;
// follow-through into a compare immediately
_upcomingEvents[_upcomingEventsPointer].updates |= Event::Action::HMoveCompare;
}
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::HMoveCompare)
{
for(int c = 0; c < 5; c++)
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::HMoveDecrement)
{
if(((_objectMotion[c] >> 4)^_hMoveCounter) == 7)
{
_hMoveFlags &= ~(1 << c);
}
update_timers(_hMoveFlags);
}
if(_hMoveFlags)
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::ResetCounter)
{
if(_hMoveCounter) _hMoveCounter--;
_upcomingEvents[(_upcomingEventsPointer+4)%number_of_upcoming_events].updates |= Event::Action::HMoveCompare;
_upcomingEvents[(_upcomingEventsPointer+2)%number_of_upcoming_events].updates |= Event::Action::HMoveDecrement;
_objectCounter[_objectCounterPointer][_upcomingEvents[_upcomingEventsPointer].counter].count = 0;
}
// zero out current update event
_upcomingEvents[_upcomingEventsPointer].updates = 0;
}
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::HMoveDecrement)
{
update_timers(_hMoveFlags);
}
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::ResetCounter)
{
_objectCounter[_objectCounterPointer][_upcomingEvents[_upcomingEventsPointer].counter].count = 0;
}
// zero out current update event, progress to next
_upcomingEvents[_upcomingEventsPointer].updates = 0;
// progress to next event
_upcomingEventsPointer = (_upcomingEventsPointer + 1)%number_of_upcoming_events;
// determine which output state is currently active
@ -392,6 +398,7 @@ void Machine::output_pixels(unsigned int count)
_horizontalTimer = (_horizontalTimer + 1) % horizontalTimerPeriod;
if(!_horizontalTimer)
{
// switch back to a normal length left border
_stateByTime = _stateByExtendTime[0];
set_ready_line(false);
}

View File

@ -16,7 +16,7 @@
namespace Atari2600 {
const unsigned int number_of_upcoming_events = 16;
const unsigned int number_of_upcoming_events = 6;
const unsigned int number_of_recorded_counters = 7;
class Speaker: public ::Outputs::Filter<Speaker> {

View File

@ -34,7 +34,9 @@
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
_serialDispatchQueue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
// create a serial dispatch queue
_serialDispatchQueue = dispatch_queue_create("OpenGLView", DISPATCH_QUEUE_SERIAL);
// dispatch_set_target_queue(_serialDispatchQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
// set the clear colour
[self.openGLContext makeCurrentContext];
@ -63,20 +65,21 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
BOOL didSkip = _hasSkipped;
dispatch_async(_serialDispatchQueue, ^{
[self.delegate openGLView:self didUpdateToTime:time didSkipPreviousUpdate:didSkip frequency:frequency];
[self drawViewOnlyIfDirty:YES];
OSAtomicTestAndClear(processingMask, &_updateIsOngoing);
});
_hasSkipped = NO;
NSLog(@"+");
}
else
{
_hasSkipped = YES;
NSLog(@"-");
}
// Draw the display only if a previous draw is not still ongoing. -drawViewOnlyIfDirty: is guaranteed
// to be safe to call concurrently with -openGLView:updateToTime: so there's no need to worry about
// the above interrupting the below or vice versa.
if(_hasSkipped && !OSAtomicTestAndSet(drawingMask, &_updateIsOngoing))
if(!OSAtomicTestAndSet(drawingMask, &_updateIsOngoing))
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self drawViewOnlyIfDirty:YES];