From 6d277fecd58f7f74a8115cdb81a5d32d5ed733bd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Nov 2018 19:52:57 -0500 Subject: [PATCH] Makes `ScanTarget` a little more communicative and orthogonal. --- Components/6560/6560.hpp | 4 +-- Components/9918/9918.cpp | 6 ++--- Machines/AmstradCPC/AmstradCPC.cpp | 4 +-- Machines/AppleII/Video.hpp | 2 +- Machines/Atari2600/TIA.cpp | 2 +- Machines/Electron/Video.cpp | 2 +- Machines/Oric/Video.cpp | 2 +- Machines/ZX8081/Video.cpp | 6 ++--- Outputs/CRT/CRT.cpp | 41 +++++++++++++++++++++--------- Outputs/CRT/CRT.hpp | 11 ++++---- Outputs/OpenGL/ScanTarget.cpp | 8 +++--- Outputs/OpenGL/ScanTarget.hpp | 7 ++--- Outputs/ScanTarget.hpp | 32 +++++++++++++---------- 13 files changed, 76 insertions(+), 51 deletions(-) diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 70f5d9886..3783247e2 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -294,7 +294,7 @@ template class MOS6560 { pixel_pointer = nullptr; if(output_state_ == State::Pixels) { - pixel_pointer = reinterpret_cast(crt_->allocate_write_area(260)); + pixel_pointer = reinterpret_cast(crt_->begin_data(260)); } } cycles_in_state_++; @@ -512,7 +512,7 @@ template class MOS6560 { uint16_t *pixel_pointer; void output_border(int number_of_cycles) { - uint16_t *colour_pointer = reinterpret_cast(crt_->allocate_write_area(1)); + uint16_t *colour_pointer = reinterpret_cast(crt_->begin_data(1)); if(colour_pointer) *colour_pointer = registers_.borderColour; crt_->output_level(number_of_cycles); } diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index 40710e0a6..7e4f3c5e1 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -411,7 +411,7 @@ void TMS9918::run_for(const HalfCycles cycles) { if(!asked_for_write_area_) { asked_for_write_area_ = true; pixel_origin_ = pixel_target_ = reinterpret_cast( - crt_->allocate_write_area(size_t(line_buffer.next_border_column - line_buffer.first_pixel_output_column)) + crt_->begin_data(size_t(line_buffer.next_border_column - line_buffer.first_pixel_output_column)) ); } @@ -471,14 +471,14 @@ void Base::output_border(int cycles, uint32_t cram_dot) { palette[background_colour_]; if(cram_dot) { - uint32_t *const pixel_target = reinterpret_cast(crt_->allocate_write_area(1)); + uint32_t *const pixel_target = reinterpret_cast(crt_->begin_data(1)); *pixel_target = border_colour | cram_dot; crt_->output_level(4); cycles -= 4; } if(cycles) { - uint32_t *const pixel_target = reinterpret_cast(crt_->allocate_write_area(1)); + uint32_t *const pixel_target = reinterpret_cast(crt_->begin_data(1)); *pixel_target = border_colour; crt_->output_level(cycles); } diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 1b4097622..b93359bb0 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -239,7 +239,7 @@ class CRTCBusHandler { // collect some more pixels if output is ongoing if(previous_output_mode_ == OutputMode::Pixels) { if(!pixel_data_) { - pixel_pointer_ = pixel_data_ = crt_->allocate_write_area(320, 8); + pixel_pointer_ = pixel_data_ = crt_->begin_data(320, 8); } if(pixel_pointer_) { // the CPC shuffles output lines as: @@ -378,7 +378,7 @@ class CRTCBusHandler { private: void output_border(int length) { - uint8_t *colour_pointer = static_cast(crt_->allocate_write_area(1)); + uint8_t *colour_pointer = static_cast(crt_->begin_data(1)); if(colour_pointer) *colour_pointer = border_; crt_->output_level(length * 16); } diff --git a/Machines/AppleII/Video.hpp b/Machines/AppleII/Video.hpp index 467dead9b..a907347dc 100644 --- a/Machines/AppleII/Video.hpp +++ b/Machines/AppleII/Video.hpp @@ -402,7 +402,7 @@ template class Video: public VideoBase { // remain where they would naturally be but auxiliary // graphics appear to the left of that. if(!column_) { - pixel_pointer_ = crt_->allocate_write_area(568); + pixel_pointer_ = crt_->begin_data(568); graphics_carry_ = 0; was_double_ = true; } diff --git a/Machines/Atari2600/TIA.cpp b/Machines/Atari2600/TIA.cpp index 0e0ee5ff7..047f2b9b9 100644 --- a/Machines/Atari2600/TIA.cpp +++ b/Machines/Atari2600/TIA.cpp @@ -449,7 +449,7 @@ void TIA::output_for_cycles(int number_of_cycles) { } else { if(!pixels_start_location_ && crt_) { pixels_start_location_ = output_cursor; - pixel_target_ = crt_->allocate_write_area(160); + pixel_target_ = crt_->begin_data(160); } // convert that into pixels diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index 1b9dbd565..54fed963e 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -119,7 +119,7 @@ void VideoOutput::output_pixels(int number_of_cycles) { crt_->output_data(data_length * current_output_divider_, size_t(data_length)); } current_output_divider_ = divider; - initial_output_target_ = current_output_target_ = crt_->allocate_write_area(size_t(640 / current_output_divider_), size_t(8 / divider)); + initial_output_target_ = current_output_target_ = crt_->begin_data(size_t(640 / current_output_divider_), size_t(8 / divider)); } #define get_pixel() \ diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index a7c20a92b..256a18d2f 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -96,7 +96,7 @@ void VideoOutput::run_for(const Cycles cycles) { paper_ = 0x0; use_alternative_character_set_ = use_double_height_characters_ = blink_text_ = false; set_character_set_base_address(); - pixel_target_ = reinterpret_cast(crt_->allocate_write_area(240)); + pixel_target_ = reinterpret_cast(crt_->begin_data(240)); if(!counter_) { frame_counter_++; diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index 379ac2aa7..1afde2ebb 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -68,7 +68,7 @@ void Video::flush(bool next_sync) { } // Any pending pixels being dealt with, pad with the white level. - uint8_t *colour_pointer = static_cast(crt_->allocate_write_area(1)); + uint8_t *colour_pointer = static_cast(crt_->begin_data(1)); if(colour_pointer) *colour_pointer = 0xff; crt_->output_level(time_since_update_.as_int()); } @@ -92,7 +92,7 @@ void Video::output_byte(uint8_t byte) { // Grab a buffer if one isn't already available. if(!line_data_) { - line_data_pointer_ = line_data_ = crt_->allocate_write_area(StandardAllocationSize); + line_data_pointer_ = line_data_ = crt_->begin_data(StandardAllocationSize); } // If a buffer was obtained, serialise the new pixels. @@ -101,7 +101,7 @@ void Video::output_byte(uint8_t byte) { if(line_data_pointer_ - line_data_ == StandardAllocationSize) { crt_->output_data(StandardAllocationSize, StandardAllocationSize); time_since_update_ -= StandardAllocationSize; - line_data_pointer_ = line_data_ = crt_->allocate_write_area(StandardAllocationSize); + line_data_pointer_ = line_data_ = crt_->begin_data(StandardAllocationSize); if(!line_data_) return; } diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index a2bfec632..9e1867e56 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -162,7 +162,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ // Determine whether to output any data for this portion of the output; if so then grab somewhere to put it. const bool is_output_segment = ((is_output_run && next_run_length) && !horizontal_flywheel_->is_in_retrace() && !vertical_flywheel_->is_in_retrace()); - Outputs::Display::ScanTarget::Scan *const next_scan = is_output_segment ? scan_target_->get_scan() : nullptr; + Outputs::Display::ScanTarget::Scan *const next_scan = is_output_segment ? scan_target_->begin_scan() : nullptr; did_output |= is_output_segment; // If outputting, store the start location and scan constants. @@ -182,24 +182,41 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ horizontal_flywheel_->apply_event(next_run_length, (next_run_length == time_until_horizontal_sync_event) ? next_horizontal_sync_event : Flywheel::SyncEvent::None); vertical_flywheel_->apply_event(next_run_length, (next_run_length == time_until_vertical_sync_event) ? next_vertical_sync_event : Flywheel::SyncEvent::None); - // Store an endpoint if necessary. + // End the scan if necessary. if(next_scan) { next_scan->end_points[1].x = uint16_t(horizontal_flywheel_->get_current_output_position()); next_scan->end_points[1].y = uint16_t(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_); next_scan->end_points[1].composite_angle = int16_t((phase_numerator_ << 6) / phase_denominator_) * (is_alernate_line_ ? -1 : 1); next_scan->end_points[1].data_offset = uint16_t((total_cycles - number_of_cycles) * number_of_samples / total_cycles); + scan_target_->end_scan(); } - // If this is horizontal retrace then announce as such, and prepare for the next line. - if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) { - scan_target_->announce(Outputs::Display::ScanTarget::Event::HorizontalRetrace); - is_alernate_line_ ^= phase_alternates_; - colour_burst_amplitude_ = 0; + // Announce horizontal retrace events. + if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) { + const auto event = + (next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) + ? Outputs::Display::ScanTarget::Event::BeginHorizontalRetrace : Outputs::Display::ScanTarget::Event::EndHorizontalRetrace; + scan_target_->announce( + event, + uint16_t(horizontal_flywheel_->get_current_output_position()), + uint16_t(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_)); + + // Prepare for the next line. + if(next_horizontal_sync_event == Flywheel::SyncEvent::EndRetrace) { + is_alernate_line_ ^= phase_alternates_; + colour_burst_amplitude_ = 0; + } } - // Also announce if this is vertical retrace. - if(next_run_length == time_until_vertical_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) { - scan_target_->announce(Outputs::Display::ScanTarget::Event::VerticalRetrace); + // Also announce vertical retrace events. + if(next_run_length == time_until_vertical_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) { + const auto event = + (next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) + ? Outputs::Display::ScanTarget::Event::BeginVerticalRetrace : Outputs::Display::ScanTarget::Event::EndVerticalRetrace; + scan_target_->announce( + event, + uint16_t(horizontal_flywheel_->get_current_output_position()), + uint16_t(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_)); } // if this is vertical retrace then adcance a field @@ -308,7 +325,7 @@ void CRT::output_blank(int number_of_cycles) { } void CRT::output_level(int number_of_cycles) { - scan_target_->reduce_previous_allocation_to(1); + scan_target_->end_data(1); Scan scan; scan.type = Scan::Type::Level; scan.number_of_cycles = number_of_cycles; @@ -336,7 +353,7 @@ void CRT::set_immediate_default_phase(float phase) { } void CRT::output_data(int number_of_cycles, size_t number_of_samples) { - scan_target_->reduce_previous_allocation_to(number_of_samples); + scan_target_->end_data(number_of_samples); Scan scan; scan.type = Scan::Type::Data; scan.number_of_cycles = number_of_cycles; diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 04e9f91a0..71c82e989 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -169,15 +169,14 @@ class CRT { */ void output_level(int number_of_cycles); - /*! Declares that the caller has created a run of data via @c allocate_write_area and @c get_write_target_for_buffer - that is at least @c number_of_samples long, and that the first @c number_of_samples should be spread - over @c number_of_cycles. + /*! Declares that the caller has created a run of data via @c begin_data that is at least @c number_of_samples + long, and that the first @c number_of_samples should be spread over @c number_of_cycles. @param number_of_cycles The amount of data to output. @param number_of_samples The number of samples of input data to output. - @see @c allocate_write_area , @c get_write_target_for_buffer + @see @c begin_data */ void output_data(int number_of_cycles, size_t number_of_samples); @@ -222,8 +221,8 @@ class CRT { @param required_length The number of samples to allocate. @returns A pointer to the allocated area if room is available; @c nullptr otherwise. */ - inline uint8_t *allocate_write_area(std::size_t required_length, std::size_t required_alignment = 1) { - return scan_target_->allocate_write_area(required_length, required_alignment); + inline uint8_t *begin_data(std::size_t required_length, std::size_t required_alignment = 1) { + return scan_target_->begin_data(required_length, required_alignment); } /*! Sets the gamma exponent for the simulated screen. */ diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 9b382e79e..f9b119fe2 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -56,12 +56,14 @@ ScanTarget::ScanTarget() { // write straight into it. glGenTextures(1, &write_area_texture_name_); + glGenVertexArrays(1, &vertex_array_); } ScanTarget::~ScanTarget() { // Release scan space. glDeleteBuffers(1, &scan_buffer_name_); glDeleteTextures(1, &write_area_texture_name_); + glDeleteVertexArrays(1, &vertex_array_); } void ScanTarget::set_modals(Modals modals) { @@ -81,7 +83,7 @@ void ScanTarget::set_modals(Modals modals) { } } -Outputs::Display::ScanTarget::Scan *ScanTarget::get_scan() { +Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() { if(allocation_has_failed_) return nullptr; const auto result = &scan_buffer_[write_pointers_.scan_buffer]; @@ -104,7 +106,7 @@ Outputs::Display::ScanTarget::Scan *ScanTarget::get_scan() { return static_cast(result); } -uint8_t *ScanTarget::allocate_write_area(size_t required_length, size_t required_alignment) { +uint8_t *ScanTarget::begin_data(size_t required_length, size_t required_alignment) { if(allocation_has_failed_) return nullptr; // Determine where the proposed write area would start and end. @@ -143,7 +145,7 @@ uint8_t *ScanTarget::allocate_write_area(size_t required_length, size_t required // write_pointers_.write_area points to the first pixel the client is expected to draw to. } -void ScanTarget::reduce_previous_allocation_to(size_t actual_length) { +void ScanTarget::end_data(size_t actual_length) { if(allocation_has_failed_) return; // The write area was allocated in the knowledge that there's sufficient diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 8fd362881..149762c59 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -29,9 +29,9 @@ class ScanTarget: public Outputs::Display::ScanTarget { // Outputs::Display::ScanTarget overrides. void set_modals(Modals) override; - Scan *get_scan() override; - uint8_t *allocate_write_area(size_t required_length, size_t required_alignment) override; - void reduce_previous_allocation_to(size_t actual_length) override; + Scan *begin_scan() override; + uint8_t *begin_data(size_t required_length, size_t required_alignment) override; + void end_data(size_t actual_length) override; void submit() override; private: @@ -65,6 +65,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { // Maintains a buffer of the most recent 3072 scans. std::array scan_buffer_; GLuint scan_buffer_name_ = 0; + GLuint vertex_array_; // Uses a texture to vend write areas. std::vector write_area_texture_; diff --git a/Outputs/ScanTarget.hpp b/Outputs/ScanTarget.hpp index fe30471f4..29fcf28a2 100644 --- a/Outputs/ScanTarget.hpp +++ b/Outputs/ScanTarget.hpp @@ -196,29 +196,32 @@ struct ScanTarget { /// Requests a new scan to populate. /// /// @return A valid pointer, or @c nullptr if insufficient further storage is available. - virtual Scan *get_scan() = 0; + virtual Scan *begin_scan() = 0; - /// Finds the first available space of at least @c required_length pixels in size which is suitably aligned - /// for writing of @c required_alignment number of pixels at a time. + /// Requests a new scan to populate. + virtual void end_scan() {} + + /// Finds the first available storage of at least @c required_length pixels in size which is + /// suitably aligned for writing of @c required_alignment number of samples at a time. /// - /// Calls will be paired off with calls to @c reduce_previous_allocation_to. + /// Calls will be paired off with calls to @c end_data. /// /// @returns a pointer to the allocated space if any was available; @c nullptr otherwise. - virtual uint8_t *allocate_write_area(size_t required_length, size_t required_alignment = 1) = 0; + virtual uint8_t *begin_data(size_t required_length, size_t required_alignment = 1) = 0; - /// Announces that the owner is finished with the region created by the most recent @c allocate_write_area + /// Announces that the owner is finished with the region created by the most recent @c begin_data /// and indicates that its actual final size was @c actual_length. /// - /// It is required that every call to allocate_write_area be paired with a call to reduce_previous_allocation_to. - virtual void reduce_previous_allocation_to(size_t actual_length) {} + /// It is required that every call to begin_data be paired with a call to end_data. + virtual void end_data(size_t actual_length) {} /// Marks the end of an atomic set of data. Drawing is best effort, so the scan target should either: /// /// (i) output everything received since the previous submit; or /// (ii) output nothing. /// - /// If there were any allocation failures — i.e. any null responses to allocate_write_area or - /// get_scan — then (ii) is a required response. But a scan target may also need to opt for (ii) + /// If there were any allocation failures — i.e. any nullptr responses to begin_data or + /// begin_scan — then (ii) is a required response. But a scan target may also need to opt for (ii) /// for any other reason. /// /// The ScanTarget isn't bound to take any drawing action immediately; it may sit on submitted data for @@ -232,12 +235,15 @@ struct ScanTarget { */ enum class Event { - HorizontalRetrace, - VerticalRetrace + BeginHorizontalRetrace, + EndHorizontalRetrace, + + BeginVerticalRetrace, + EndVerticalRetrace, }; /// Provides a hint that the named event has occurred. - virtual void announce(Event event) {} + virtual void announce(Event event, uint16_t x, uint16_t y) {} }; }