From 8c9062857c83c1e602e9cbeb2c3f1fb6b21cd9db Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 25 Feb 2017 17:10:24 -0500 Subject: [PATCH] Added a single-slot queue for player objects to defer drawing, thereby deferring pixel lookup. Which I think is correct. Though more slots might be needed. --- Machines/Atari2600/TIA.cpp | 23 +++++++++-- Machines/Atari2600/TIA.hpp | 83 +++++++++++++++++++++++++++++--------- 2 files changed, 85 insertions(+), 21 deletions(-) diff --git a/Machines/Atari2600/TIA.cpp b/Machines/Atari2600/TIA.cpp index 356e48116..0fa51316d 100644 --- a/Machines/Atari2600/TIA.cpp +++ b/Machines/Atari2600/TIA.cpp @@ -659,6 +659,8 @@ template void TIA::draw_object(T &object, const uint8_t collision_ident { int first_pixel = first_pixel_cycle - 4 + (horizontal_blank_extend_ ? 8 : 0); + object.dequeue_pixels(collision_buffer_, collision_identity, end - first_pixel_cycle); + // movement works across the entire screen, so do work that falls outside of the pixel area if(start < first_pixel) { @@ -673,7 +675,7 @@ template void TIA::draw_object(T &object, const uint8_t collision_ident // perform the visible part of the line, if any if(start < 224) { - draw_object_visible(object, collision_identity, start - first_pixel_cycle + 4, std::min(end - first_pixel_cycle + 4, 160)); + draw_object_visible(object, collision_identity, start - first_pixel_cycle + 4, std::min(end - first_pixel_cycle + 4, 160), end - first_pixel_cycle); } // move further if required @@ -683,7 +685,7 @@ template void TIA::draw_object(T &object, const uint8_t collision_ident } } -template void TIA::draw_object_visible(T &object, const uint8_t collision_identity, int start, int end) +template void TIA::draw_object_visible(T &object, const uint8_t collision_identity, int start, int end, int time_now) { // perform a miniature event loop on (i) triggering draws; (ii) drawing; and (iii) motion int next_motion_time = object.motion_time - first_pixel_cycle + 4; @@ -723,7 +725,22 @@ template void TIA::draw_object_visible(T &object, const uint8_t collisi // an appropriate solution would probably be to capture the drawing request into a queue and honour them outside // this loop, clipped to the real output parameters. Assuming all state consumed by draw_pixels is captured, // and mutated now then also queueing resets and skips shouldn't be necessary. - object.enqueue_pixels(&collision_buffer_[start], length, collision_identity); + if(next_event_time > time_now) + { + if(start < time_now) + { + object.output_pixels(&collision_buffer_[start], time_now - start, collision_identity); + object.enqueue_pixels(time_now, next_event_time); + } + else + { + object.enqueue_pixels(start, next_event_time); + } + } + else + { + object.output_pixels(&collision_buffer_[start], length, collision_identity); + } // the next interesting event is after next_event_time cycles, so progress object.position = (object.position + length) % 160; diff --git a/Machines/Atari2600/TIA.hpp b/Machines/Atari2600/TIA.hpp index da6a77d86..4114a44be 100644 --- a/Machines/Atari2600/TIA.hpp +++ b/Machines/Atari2600/TIA.hpp @@ -133,7 +133,7 @@ class TIA { // output twice. // objects - struct Object { + template struct Object { // the two programmer-set values int position; int motion; @@ -146,10 +146,31 @@ class TIA { bool is_moving; Object() : is_moving(false) {}; + + void dequeue_pixels(uint8_t *const target, const uint8_t collision_identity, const int time_now) + { +// if(enqueued_start_ != enqueued_end_) +// { +// static_cast(this)->output_pixels(&target[enqueued_start_], enqueued_end_ - enqueued_start_, collision_identity); +// enqueued_end_ = enqueued_start_ = 0; +// } + } + + void enqueue_pixels(const int start, const int end) + { +// enqueued_start_ = start; +// enqueued_end_ = end; + static_cast(this)->skip_pixels(end - start); + } + + private: +// int enqueued_start_, enqueued_end_; }; // player state - struct Player: public Object { + struct Player: public Object { + Player() : copy_flags(0), graphic{0, 0}, reverse_mask(false), pixel_position(32), graphic_index(0), adder(4) {} + int adder; int copy_flags; // a bit field, corresponding to the first few values of NUSIZ uint8_t graphic[2]; // the player graphic; 1 = new, 0 = current @@ -168,11 +189,43 @@ class TIA { pixel_position = 0; } - inline void enqueue_pixels(uint8_t *const target, const int count, const uint8_t collision_identity) + inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity) { - if(pixel_position == 32) return; - if(graphic[graphic_index]) + output_pixels(target, count, collision_identity, pixel_position, adder, reverse_mask); + skip_pixels(count); + } + + void dequeue_pixels(uint8_t *const target, const uint8_t collision_identity, const int time_now) + { + if(queue_.start != queue_.end) { + output_pixels(&target[queue_.start], queue_.end - queue_.start, collision_identity, queue_.pixel_position, queue_.adder, queue_.reverse_mask); + queue_.start = queue_.end; + } + } + + void enqueue_pixels(const int start, const int end) + { + queue_.start = start; + queue_.end = end; + queue_.pixel_position = pixel_position; + queue_.adder = adder; + queue_.reverse_mask = reverse_mask; + skip_pixels(end - start); + } + + private: + struct QueuedPixels + { + int start, end; + int pixel_position; + int adder; + int reverse_mask; + } queue_; + + inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity, int pixel_position, int adder, int reverse_mask) + { + if(pixel_position == 32 || !graphic[graphic_index]) return; int output_cursor = 0; while(pixel_position < 32 && output_cursor < count) { @@ -182,17 +235,11 @@ class TIA { pixel_position += adder; } } - else - { - skip_pixels(count); - } - } - Player() : copy_flags(0), graphic{0, 0}, reverse_mask(false), pixel_position(32), graphic_index(0), adder(4) {} } player_[2]; // common actor for things that appear as a horizontal run of pixels - struct HorizontalRun: public Object { + struct HorizontalRun: public Object { int pixel_position; int size; @@ -206,7 +253,7 @@ class TIA { pixel_position = size; } - inline void enqueue_pixels(uint8_t *const target, const int count, const uint8_t collision_identity) + inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity) { int output_cursor = 0; while(pixel_position && output_cursor < count) @@ -227,12 +274,12 @@ class TIA { bool locked_to_player; int copy_flags; - inline void enqueue_pixels(uint8_t *const target, const int count, const uint8_t collision_identity) + inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity) { if(!pixel_position) return; if(enabled && !locked_to_player) { - HorizontalRun::enqueue_pixels(target, count, collision_identity); + HorizontalRun::output_pixels(target, count, collision_identity); } else { @@ -249,12 +296,12 @@ class TIA { int enabled_index; const int copy_flags = 0; - inline void enqueue_pixels(uint8_t *const target, const int count, const uint8_t collision_identity) + inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity) { if(!pixel_position) return; if(enabled[enabled_index]) { - HorizontalRun::enqueue_pixels(target, count, collision_identity); + HorizontalRun::output_pixels(target, count, collision_identity); } else { @@ -272,7 +319,7 @@ class TIA { // drawing methods and state template void draw_object(T &, const uint8_t collision_identity, int start, int end); - template void draw_object_visible(T &, const uint8_t collision_identity, int start, int end); + template void draw_object_visible(T &, const uint8_t collision_identity, int start, int end, int time_now); inline void draw_playfield(int start, int end); inline void output_for_cycles(int number_of_cycles);