From 014da41471f36661485475d331cd052edf61c0e3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Nov 2018 21:06:25 -0500 Subject: [PATCH] Ensures scan positions are communicated with a specified range, and switches manner of pixel clock communication. --- Machines/Electron/Video.cpp | 2 +- Machines/ZX8081/Video.cpp | 2 +- .../Mac/Clock Signal/Machine/CSMachine.mm | 5 ++- Outputs/CRT/CRT.cpp | 33 ++++++++++--------- Outputs/CRT/CRT.hpp | 8 ++--- Outputs/ScanTarget.hpp | 23 +++++++++---- 6 files changed, 43 insertions(+), 30 deletions(-) diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index 65feffebb..f62ad62ab 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -45,7 +45,7 @@ VideoOutput::VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_tar crt_.reset(new Outputs::CRT::CRT( crt_cycles_per_line, - 1024, + 1, Outputs::Display::Type::PAL50, Outputs::Display::ScanTarget::Modals::DataType::Red1Green1Blue1, scan_target)); diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index d16dae1d5..ee98d1693 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -23,7 +23,7 @@ const std::size_t StandardAllocationSize = 320; } Video::Video(Outputs::Display::ScanTarget *scan_target) : - crt_(new Outputs::CRT::CRT(207 * 2, 414, Outputs::Display::Type::PAL50, Outputs::Display::ScanTarget::Modals::DataType::Luminance1, scan_target)) + crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::Display::Type::PAL50, Outputs::Display::ScanTarget::Modals::DataType::Luminance1, scan_target)) { // Set a composite sampling function that assumes two-level input; either a byte is 0, which is black, diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 824eedfc1..df53820fd 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -75,7 +75,9 @@ struct ActivityObserver: public Activity::Observer { class ScanTarget: public Outputs::Display::ScanTarget { public: - void set_modals(Modals) {} + void set_modals(Modals m) { + modals_ = m; + } Scan *get_scan() { scans_.emplace_back(); @@ -94,6 +96,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { private: std::vector scans_; std::vector write_area_; + Modals modals_; }; @implementation CSMachine { diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index b6f984fd5..a5b135d3c 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -28,8 +28,7 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di // 7 microseconds for horizontal retrace and 500 to 750 microseconds for vertical retrace // in NTSC and PAL TV." -// time_multiplier_ = IntermediateBufferWidth / cycles_per_line; - time_multiplier_ = 2048 / cycles_per_line; // TODO + time_multiplier_ = 65535 / cycles_per_line; phase_denominator_ = cycles_per_line * colour_cycle_denominator * time_multiplier_; phase_numerator_ = 0; colour_cycle_numerator_ = colour_cycle_numerator; @@ -56,11 +55,11 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di vertical_flywheel_.reset(new Flywheel(multiplied_cycles_per_line * height_of_display, scanlinesVerticalRetraceTime * multiplied_cycles_per_line, (multiplied_cycles_per_line * height_of_display) >> 3)); // figure out the divisor necessary to get the horizontal flywheel into a 16-bit range -// const int real_clock_scan_period = (multiplied_cycles_per_line * height_of_display) / (time_multiplier_); -// vertical_flywheel_output_divider_ = static_cast(ceilf(real_clock_scan_period / 65536.0f) * (time_multiplier_)); - -// openGL_output_builder_.set_timing(cycles_per_line, multiplied_cycles_per_line, height_of_display, horizontal_flywheel_->get_scan_period(), vertical_flywheel_->get_scan_period(), vertical_flywheel_output_divider_); + const int real_clock_scan_period = multiplied_cycles_per_line * height_of_display; + vertical_flywheel_output_divider_ = (real_clock_scan_period + 65534) / 65535; + scan_target_modals_.output_scale.x = uint16_t(time_multiplier_ * cycles_per_line); + scan_target_modals_.output_scale.y = uint16_t((multiplied_cycles_per_line * height_of_display) / vertical_flywheel_output_divider_); scan_target_modals_.expected_vertical_lines = height_of_display; scan_target_modals_.composite_colour_space = colour_space; scan_target_->set_modals(scan_target_modals_); @@ -69,13 +68,13 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di void CRT::set_new_display_type(int cycles_per_line, Outputs::Display::Type displayType) { switch(displayType) { case Outputs::Display::Type::PAL50: + scan_target_modals_.intended_gamma = 2.8f; set_new_timing(cycles_per_line, 312, Outputs::Display::ColourSpace::YUV, 709379, 2500, 5, true); // i.e. 283.7516; 2.5 lines = vertical sync - set_input_gamma(2.8f); break; case Outputs::Display::Type::NTSC60: + scan_target_modals_.intended_gamma = 2.2f; set_new_timing(cycles_per_line, 262, Outputs::Display::ColourSpace::YIQ, 455, 2, 6, false); // i.e. 227.5, 3 lines = vertical sync - set_input_gamma(2.2f); break; } } @@ -89,12 +88,12 @@ void CRT::set_composite_function_type(CompositeSourceType type, float offset_of_ } void CRT::set_input_gamma(float gamma) { -// input_gamma_ = gamma; -// update_gamma(); + scan_target_modals_.intended_gamma = gamma; + scan_target_->set_modals(scan_target_modals_); } CRT::CRT( int cycles_per_line, - int pixel_clock_least_common_multiple, + int clocks_per_pixel_greatest_common_divisor, int height_of_display, Outputs::Display::ColourSpace colour_space, int colour_cycle_numerator, int colour_cycle_denominator, @@ -104,18 +103,20 @@ CRT::CRT( int cycles_per_line, Outputs::Display::ScanTarget *scan_target) { scan_target_ = scan_target; scan_target_modals_.source_data_type = data_type; - scan_target_modals_.pixel_clock_least_common_multiple = pixel_clock_least_common_multiple; + scan_target_modals_.cycles_per_line = cycles_per_line; + scan_target_modals_.clocks_per_pixel_greatest_common_divisor = clocks_per_pixel_greatest_common_divisor; set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator, vertical_sync_half_lines, should_alternate); } CRT::CRT( int cycles_per_line, - int pixel_clock_least_common_multiple, + int clocks_per_pixel_greatest_common_divisor, Outputs::Display::Type display_type, Outputs::Display::ScanTarget::Modals::DataType data_type, Outputs::Display::ScanTarget *scan_target) { scan_target_ = scan_target; scan_target_modals_.source_data_type = data_type; - scan_target_modals_.pixel_clock_least_common_multiple = pixel_clock_least_common_multiple; + scan_target_modals_.cycles_per_line = cycles_per_line; + scan_target_modals_.clocks_per_pixel_greatest_common_divisor = clocks_per_pixel_greatest_common_divisor; set_new_display_type(cycles_per_line, display_type); } @@ -157,7 +158,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ // If outputting, store the start location and if(next_scan) { next_scan->end_points[0].x = static_cast(horizontal_flywheel_->get_current_output_position()); - next_scan->end_points[0].y = static_cast(vertical_flywheel_->get_current_output_position()); + next_scan->end_points[0].y = static_cast(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_); next_scan->end_points[0].composite_angle = colour_burst_angle_; // TODO. next_scan->end_points[0].data_offset = static_cast((total_cycles - number_of_cycles) * number_of_samples / total_cycles); next_scan->composite_amplitude = colour_burst_amplitude_; @@ -175,7 +176,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ // Store an endpoint if necessary. if(next_scan) { next_scan->end_points[1].x = static_cast(horizontal_flywheel_->get_current_output_position()); - next_scan->end_points[1].y = static_cast(vertical_flywheel_->get_current_output_position()); + next_scan->end_points[1].y = static_cast(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_); next_scan->end_points[1].composite_angle = colour_burst_angle_; // TODO. next_scan->end_points[1].data_offset = static_cast((total_cycles - number_of_cycles) * number_of_samples / total_cycles); } diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 1e4ad124a..4db06643f 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -33,7 +33,7 @@ class CRT { // the two flywheels regulating scanning std::unique_ptr horizontal_flywheel_, vertical_flywheel_; - uint16_t vertical_flywheel_output_divider_ = 1; + int vertical_flywheel_output_divider_ = 1; struct Scan { enum Type { @@ -89,7 +89,7 @@ class CRT { @param cycles_per_line The clock rate at which this CRT will be driven, specified as the number of cycles expected to take up one whole scanline of the display. - @param pixel_clock_least_common_multiple TODO. + @param minimum_cycles_per_pixel TODO. @param height_of_display The number of lines that nominally form one field of the display, rounded up to the next whole integer. @@ -109,7 +109,7 @@ class CRT { @see @c set_rgb_sampling_function , @c set_composite_sampling_function */ CRT(int cycles_per_line, - int pixel_clock_least_common_multiple, + int minimum_cycles_per_pixel, int height_of_display, Outputs::Display::ColourSpace colour_space, int colour_cycle_numerator, @@ -127,7 +127,7 @@ class CRT { looked up by display type. */ CRT(int cycles_per_line, - int pixel_clock_least_common_multiple, + int minimum_cycles_per_pixel, Outputs::Display::Type display_type, Outputs::Display::ScanTarget::Modals::DataType data_type, Outputs::Display::ScanTarget *scan_target); diff --git a/Outputs/ScanTarget.hpp b/Outputs/ScanTarget.hpp index a52cc79d6..7cdbeb5eb 100644 --- a/Outputs/ScanTarget.hpp +++ b/Outputs/ScanTarget.hpp @@ -98,10 +98,15 @@ struct ScanTarget { /// If being fed composite data, this defines the colour space in use. ColourSpace composite_colour_space; - /// Nominates a least common multiple of the potential input pixel clocks; - /// if this isn't a crazy number then it'll be used potentially to optimise - /// the composite encoding and decoding process. - int pixel_clock_least_common_multiple; + /// Provides an integral clock rate for the duration of "a single line", specifically + /// for an idealised line. So e.g. in NTSC this will be for the duration of 227.5 + /// colour clocks, regardless of whether the source actually stretches lines to + /// 228 colour cycles, abbreviates them to 227 colour cycles, etc. + int cycles_per_line; + + /// Sets a GCD for the durations of pixels coming out of this device. This with + /// the @c cycles_per_line are offered for sizing of intermediary buffers. + int clocks_per_pixel_greatest_common_divisor; /// Provides a pre-estimate of the likely number of left-to-right scans per frame. /// This isn't a guarantee, but it should provide a decent-enough estimate. @@ -111,8 +116,13 @@ struct ScanTarget { /// to contain interesting content. Rect visible_area; - /// Describes the + /// Describes the usual gamma of the output device these scans would appear on. float intended_gamma; + + /// Specifies the range of values that will be output for x and y coordinates. + struct { + uint16_t x, y; + } output_scale; }; /// Sets the total format of input data. @@ -130,8 +140,7 @@ struct ScanTarget { struct Scan { struct EndPoint { /// Provide the coordinate of this endpoint. These are fixed point, purely fractional - /// numbers: 0 is the extreme left or top of the scanned rectangle, and 65535 is the - /// extreme right or bottom. + /// numbers, relative to the scale provided in the Modals. uint16_t x, y; /// Provides the offset, in samples, into the most recently allocated write area, of data