diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 23bb6e4d1..cf0f3f718 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -68,19 +68,19 @@ template class MOS6560 { audio_generator_(audio_queue_), speaker_(audio_generator_) { - crt_->set_svideo_sampling_function( - "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" - "{" - "vec2 yc = texture(texID, coordinate).rg / vec2(255.0);" - - "float phaseOffset = 6.283185308 * 2.0 * yc.y;" - "float chroma = step(yc.y, 0.75) * cos(phase + phaseOffset);" - - "return vec2(yc.x, chroma);" - "}"); +// crt_->set_svideo_sampling_function( +// "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" +// "{" +// "vec2 yc = texture(texID, coordinate).rg / vec2(255.0);" +// +// "float phaseOffset = 6.283185308 * 2.0 * yc.y;" +// "float chroma = step(yc.y, 0.75) * cos(phase + phaseOffset);" +// +// "return vec2(yc.x, chroma);" +// "}"); // default to s-video output - crt_->set_video_signal(Outputs::CRT::VideoSignal::SVideo); +// crt_->set_video_signal(Outputs::CRT::VideoSignal::SVideo); // default to NTSC set_output_mode(OutputMode::NTSC); @@ -155,7 +155,7 @@ template class MOS6560 { break; } - crt_->set_new_display_type(static_cast(timing_.cycles_per_line*4), display_type); + crt_->set_new_display_type(timing_.cycles_per_line*4, display_type); switch(output_mode) { case OutputMode::PAL: @@ -465,7 +465,7 @@ template class MOS6560 { enum State { Sync, ColourBurst, Border, Pixels } this_state_, output_state_; - unsigned int cycles_in_state_; + int cycles_in_state_; // counters that cover an entire field int horizontal_counter_ = 0, vertical_counter_ = 0; @@ -511,7 +511,7 @@ template class MOS6560 { uint16_t colours_[16]; uint16_t *pixel_pointer; - void output_border(unsigned int number_of_cycles) { + void output_border(int number_of_cycles) { uint16_t *colour_pointer = reinterpret_cast(crt_->allocate_write_area(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 26f5604cd..47501d994 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -86,12 +86,12 @@ TMS9918::TMS9918(Personality p): Base(p) { // Unimaginatively, this class just passes RGB through to the shader. Investigation is needed // into whether there's a more natural form. - crt_->set_rgb_sampling_function( - "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" - "{" - "return texture(sampler, coordinate).rgb / vec3(255.0);" - "}"); - crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB); +// crt_->set_rgb_sampling_function( +// "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" +// "{" +// "return texture(sampler, coordinate).rgb / vec3(255.0);" +// "}"); +// crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB); crt_->set_visible_area(Outputs::CRT::Rect(0.055f, 0.025f, 0.9f, 0.9f)); // The TMS remains in-phase with the NTSC colour clock; this is an empirical measurement @@ -410,7 +410,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(static_cast(line_buffer.next_border_column - line_buffer.first_pixel_output_column)) + crt_->allocate_write_area(size_t(line_buffer.next_border_column - line_buffer.first_pixel_output_column)) ); } @@ -427,8 +427,8 @@ void TMS9918::run_for(const HalfCycles cycles) { } if(end == line_buffer.next_border_column) { - const unsigned int length = static_cast(line_buffer.next_border_column - line_buffer.first_pixel_output_column); - crt_->output_data(length * 4, length); + const int length = line_buffer.next_border_column - line_buffer.first_pixel_output_column; + crt_->output_data(length * 4, size_t(length)); pixel_origin_ = pixel_target_ = nullptr; asked_for_write_area_ = false; } @@ -479,7 +479,7 @@ void Base::output_border(int cycles, uint32_t cram_dot) { if(cycles) { uint32_t *const pixel_target = reinterpret_cast(crt_->allocate_write_area(1)); *pixel_target = border_colour; - crt_->output_level(static_cast(cycles)); + crt_->output_level(cycles); } } diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 2a3935039..71d3940ee 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -222,7 +222,7 @@ class CRTCBusHandler { case OutputMode::Border: output_border(cycles_); break; case OutputMode::ColourBurst: crt_->output_default_colour_burst(cycles_ * 16); break; case OutputMode::Pixels: - crt_->output_data(cycles_ * 16, cycles_ * 16 / pixel_divider_); + crt_->output_data(cycles_ * 16, size_t(cycles_ * 16 / pixel_divider_)); pixel_pointer_ = pixel_data_ = nullptr; break; } @@ -283,7 +283,7 @@ class CRTCBusHandler { // widths so it's not necessarily possible to predict the correct number in advance // and using the upper bound could lead to inefficient behaviour if(pixel_pointer_ == pixel_data_ + 320) { - crt_->output_data(cycles_ * 16, cycles_ * 16 / pixel_divider_); + crt_->output_data(cycles_ * 16, size_t(cycles_ * 16 / pixel_divider_)); pixel_pointer_ = pixel_data_ = nullptr; cycles_ = 0; } @@ -326,14 +326,14 @@ class CRTCBusHandler { /// Constructs an appropriate CRT for video output. void setup_output(float aspect_ratio) { crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::CRT::DisplayType::PAL50, 1)); - crt_->set_rgb_sampling_function( - "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" - "{" - "uint sample = texture(texID, coordinate).r;" - "return vec3(float((sample >> 4) & 3u), float((sample >> 2) & 3u), float(sample & 3u)) / 2.0;" - "}"); +// crt_->set_rgb_sampling_function( +// "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" +// "{" +// "uint sample = texture(texID, coordinate).r;" +// "return vec3(float((sample >> 4) & 3u), float((sample >> 2) & 3u), float(sample & 3u)) / 2.0;" +// "}"); crt_->set_visible_area(Outputs::CRT::Rect(0.1072f, 0.1f, 0.842105263157895f, 0.842105263157895f)); - crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB); +// crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB); } /// Destructs the CRT. @@ -376,7 +376,7 @@ class CRTCBusHandler { } private: - void output_border(unsigned int length) { + void output_border(int length) { uint8_t *colour_pointer = static_cast(crt_->allocate_write_area(1)); if(colour_pointer) *colour_pointer = border_; crt_->output_level(length * 16); @@ -528,7 +528,7 @@ class CRTCBusHandler { Border, Pixels } previous_output_mode_ = OutputMode::Sync; - unsigned int cycles_ = 0; + int cycles_ = 0; bool was_hsync_ = false, was_vsync_ = false; int cycles_into_hsync_ = 0; @@ -540,7 +540,7 @@ class CRTCBusHandler { int next_mode_ = 2, mode_ = 2; - unsigned int pixel_divider_ = 1; + int pixel_divider_ = 1; uint16_t mode0_output_[256]; uint32_t mode1_output_[256]; uint64_t mode2_output_[256]; diff --git a/Machines/AppleII/Video.cpp b/Machines/AppleII/Video.cpp index fb064673b..921427fb2 100644 --- a/Machines/AppleII/Video.cpp +++ b/Machines/AppleII/Video.cpp @@ -17,14 +17,14 @@ VideoBase::VideoBase(bool is_iie, std::function &&target) : // Set a composite sampling function that assumes one byte per pixel input, and // accepts any non-zero value as being fully on, zero being fully off. - crt_->set_composite_sampling_function( - "float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)" - "{" - "return clamp(texture(sampler, coordinate).r, 0.0, 0.66);" - "}"); +// crt_->set_composite_sampling_function( +// "float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)" +// "{" +// "return clamp(texture(sampler, coordinate).r, 0.0, 0.66);" +// "}"); // Show only the centre 75% of the TV frame. - crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); +// crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); crt_->set_visible_area(Outputs::CRT::Rect(0.118f, 0.122f, 0.77f, 0.77f)); crt_->set_immediate_default_phase(0.0f); diff --git a/Machines/AppleII/Video.hpp b/Machines/AppleII/Video.hpp index 3df0e48f7..467dead9b 100644 --- a/Machines/AppleII/Video.hpp +++ b/Machines/AppleII/Video.hpp @@ -351,14 +351,14 @@ template class Video: public VideoBase { const int blank_end = std::min(first_sync_column, ending_column); if(blank_end > blank_start) { if(blank_start > column_) { - crt_->output_sync(static_cast(blank_start - column_) * 14); + crt_->output_sync((blank_start - column_) * 14); } - crt_->output_blank(static_cast(blank_end - blank_start) * 14); + crt_->output_blank((blank_end - blank_start) * 14); if(blank_end < ending_column) { - crt_->output_sync(static_cast(ending_column - blank_end) * 14); + crt_->output_sync((ending_column - blank_end) * 14); } } else { - crt_->output_sync(static_cast(cycles_this_line) * 14); + crt_->output_sync((cycles_this_line) * 14); } } else { const GraphicsMode line_mode = graphics_mode(row_); @@ -527,7 +527,7 @@ template class Video: public VideoBase { const int colour_burst_start = std::max(first_sync_column + sync_length + 1, column_); const int colour_burst_end = std::min(first_sync_column + sync_length + 4, ending_column); if(colour_burst_end > colour_burst_start) { - crt_->output_colour_burst(static_cast(colour_burst_end - colour_burst_start) * 14, 192); + crt_->output_colour_burst((colour_burst_end - colour_burst_start) * 14, 192); } second_blank_start = std::max(first_sync_column + sync_length + 3, column_); @@ -536,7 +536,7 @@ template class Video: public VideoBase { } if(ending_column > second_blank_start) { - crt_->output_blank(static_cast(ending_column - second_blank_start) * 14); + crt_->output_blank((ending_column - second_blank_start) * 14); } } diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 1a60148aa..09dda70d2 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -181,15 +181,15 @@ class ConcreteMachine: } // to satisfy Outputs::CRT::Delegate - void crt_did_end_batch_of_frames(Outputs::CRT::CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs) override { + void crt_did_end_batch_of_frames(Outputs::CRT::CRT *crt, int number_of_frames, int number_of_unexpected_vertical_syncs) override { const std::size_t number_of_frame_records = sizeof(frame_records_) / sizeof(frame_records_[0]); frame_records_[frame_record_pointer_ % number_of_frame_records].number_of_frames = number_of_frames; frame_records_[frame_record_pointer_ % number_of_frame_records].number_of_unexpected_vertical_syncs = number_of_unexpected_vertical_syncs; frame_record_pointer_ ++; if(frame_record_pointer_ >= 6) { - unsigned int total_number_of_frames = 0; - unsigned int total_number_of_unexpected_vertical_syncs = 0; + int total_number_of_frames = 0; + int total_number_of_unexpected_vertical_syncs = 0; for(std::size_t c = 0; c < number_of_frame_records; c++) { total_number_of_frames += frame_records_[c].number_of_frames; total_number_of_unexpected_vertical_syncs += frame_records_[c].number_of_unexpected_vertical_syncs; @@ -228,10 +228,8 @@ class ConcreteMachine: // output frame rate tracker struct FrameRecord { - unsigned int number_of_frames; - unsigned int number_of_unexpected_vertical_syncs; - - FrameRecord() : number_of_frames(0), number_of_unexpected_vertical_syncs(0) {} + int number_of_frames = 0; + int number_of_unexpected_vertical_syncs = 0; } frame_records_[4]; unsigned int frame_record_pointer_ = 0; bool is_ntsc_ = true; diff --git a/Machines/Atari2600/TIA.cpp b/Machines/Atari2600/TIA.cpp index 2d540e97a..89861c84f 100644 --- a/Machines/Atari2600/TIA.cpp +++ b/Machines/Atari2600/TIA.cpp @@ -25,7 +25,7 @@ namespace { TIA::TIA(bool create_crt) { if(create_crt) { crt_.reset(new Outputs::CRT::CRT(cycles_per_line * 2 - 1, 1, Outputs::CRT::DisplayType::NTSC60, 1)); - crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); +// crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); set_output_mode(OutputMode::NTSC); } @@ -123,33 +123,33 @@ void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) { Outputs::CRT::DisplayType display_type; if(output_mode == OutputMode::NTSC) { - crt_->set_svideo_sampling_function( - "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" - "{" - "uint c = texture(texID, coordinate).r;" - "uint y = c & 14u;" - "uint iPhase = (c >> 4);" - - "float phaseOffset = 6.283185308 * float(iPhase) / 13.0 + 5.074880441076923;" - "return vec2(float(y) / 14.0, step(1, iPhase) * cos(phase - phaseOffset));" - "}"); +// crt_->set_svideo_sampling_function( +// "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" +// "{" +// "uint c = texture(texID, coordinate).r;" +// "uint y = c & 14u;" +// "uint iPhase = (c >> 4);" +// +// "float phaseOffset = 6.283185308 * float(iPhase) / 13.0 + 5.074880441076923;" +// "return vec2(float(y) / 14.0, step(1, iPhase) * cos(phase - phaseOffset));" +// "}"); display_type = Outputs::CRT::DisplayType::NTSC60; } else { - crt_->set_svideo_sampling_function( - "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" - "{" - "uint c = texture(texID, coordinate).r;" - "uint y = c & 14u;" - "uint iPhase = (c >> 4);" - - "uint direction = iPhase & 1u;" - "float phaseOffset = float(7u - direction) + (float(direction) - 0.5) * 2.0 * float(iPhase >> 1);" - "phaseOffset *= 6.283185308 / 12.0;" - "return vec2(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset));" - "}"); +// crt_->set_svideo_sampling_function( +// "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" +// "{" +// "uint c = texture(texID, coordinate).r;" +// "uint y = c & 14u;" +// "uint iPhase = (c >> 4);" +// +// "uint direction = iPhase & 1u;" +// "float phaseOffset = float(7u - direction) + (float(direction) - 0.5) * 2.0 * float(iPhase >> 1);" +// "phaseOffset *= 6.283185308 / 12.0;" +// "return vec2(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset));" +// "}"); display_type = Outputs::CRT::DisplayType::PAL50; } - crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); +// crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); // line number of cycles in a line of video is one less than twice the number of clock cycles per line; the Atari // outputs 228 colour cycles of material per line when an NTSC line 227.5. Since all clock numbers will be doubled @@ -407,11 +407,11 @@ void TIA::output_for_cycles(int number_of_cycles) { #define Period(function, target) \ if(output_cursor < target) { \ if(horizontal_counter_ <= target) { \ - if(crt_) crt_->function(static_cast((horizontal_counter_ - output_cursor) * 2)); \ + if(crt_) crt_->function((horizontal_counter_ - output_cursor) * 2); \ horizontal_counter_ %= cycles_per_line; \ return; \ } else { \ - if(crt_) crt_->function(static_cast((target - output_cursor) * 2)); \ + if(crt_) crt_->function((target - output_cursor) * 2); \ output_cursor = target; \ } \ } @@ -438,14 +438,14 @@ void TIA::output_for_cycles(int number_of_cycles) { if(pixel_target_) { output_pixels(pixels_start_location_, output_cursor); if(crt_) { - const unsigned int data_length = static_cast(output_cursor - pixels_start_location_); - crt_->output_data(data_length * 2, data_length); + const int data_length = int(output_cursor - pixels_start_location_); + crt_->output_data(data_length * 2, size_t(data_length)); } pixel_target_ = nullptr; pixels_start_location_ = 0; } int duration = std::min(228, horizontal_counter_) - output_cursor; - if(crt_) crt_->output_blank(static_cast(duration * 2)); + if(crt_) crt_->output_blank(duration * 2); } else { if(!pixels_start_location_ && crt_) { pixels_start_location_ = output_cursor; @@ -462,8 +462,8 @@ void TIA::output_for_cycles(int number_of_cycles) { } if(horizontal_counter_ == cycles_per_line && crt_) { - const unsigned int data_length = static_cast(output_cursor - pixels_start_location_); - crt_->output_data(data_length * 2, data_length); + const int data_length = int(output_cursor - pixels_start_location_); + crt_->output_data(data_length * 2, size_t(data_length)); pixel_target_ = nullptr; pixels_start_location_ = 0; } diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp index 9c7346782..956b26f33 100644 --- a/Machines/CRTMachine.hpp +++ b/Machines/CRTMachine.hpp @@ -92,7 +92,7 @@ class Machine { Forwards the video signal to the CRT returned by get_crt(). */ virtual void set_video_signal(Outputs::CRT::VideoSignal video_signal) { - get_crt()->set_video_signal(video_signal); +// get_crt()->set_video_signal(video_signal); } private: diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 6fe10319c..0c2794d38 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -171,7 +171,7 @@ class ConcreteMachine: void setup_output(float aspect_ratio) override { vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A)); - get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite); +// get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite); } void close_output() override { diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index 82cabd225..f57bad88b 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -44,12 +44,12 @@ VideoOutput::VideoOutput(uint8_t *memory) : ram_(memory) { setup_base_address(); crt_.reset(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::CRT::DisplayType::PAL50, 1)); - crt_->set_rgb_sampling_function( - "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" - "{" - "uint texValue = texture(sampler, coordinate).r;" - "return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));" - "}"); +// crt_->set_rgb_sampling_function( +// "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" +// "{" +// "uint texValue = texture(sampler, coordinate).r;" +// "return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));" +// "}"); // TODO: as implied below, I've introduced a clock's latency into the graphics pipeline somehow. Investigate. crt_->set_visible_area(crt_->get_rect_for_area(first_graphics_line - 1, 256, (first_graphics_cycle+1) * crt_cycles_multiplier, 80 * crt_cycles_multiplier, 4.0f / 3.0f)); } @@ -88,19 +88,19 @@ void VideoOutput::start_pixel_line() { void VideoOutput::end_pixel_line() { if(current_output_target_) { - const unsigned int data_length = static_cast(current_output_target_ - initial_output_target_); - crt_->output_data(data_length * current_output_divider_, data_length); + const int data_length = int(current_output_target_ - initial_output_target_); + crt_->output_data(data_length * current_output_divider_, size_t(data_length)); } current_character_row_++; } -void VideoOutput::output_pixels(unsigned int number_of_cycles) { +void VideoOutput::output_pixels(int number_of_cycles) { if(!number_of_cycles) return; if(is_blank_line_) { crt_->output_blank(number_of_cycles * crt_cycles_multiplier); } else { - unsigned int divider = 1; + int divider = 1; switch(screen_mode_) { case 0: case 3: divider = 1; break; case 1: case 4: case 6: divider = 2; break; @@ -109,11 +109,11 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) { if(!initial_output_target_ || divider != current_output_divider_) { if(current_output_target_) { - const unsigned int data_length = static_cast(current_output_target_ - initial_output_target_); - crt_->output_data(data_length * current_output_divider_, data_length); + const int data_length = int(current_output_target_ - initial_output_target_); + 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(640 / current_output_divider_, 8 / divider); + initial_output_target_ = current_output_target_ = crt_->allocate_write_area(size_t(640 / current_output_divider_), size_t(8 / divider)); } #define get_pixel() \ @@ -230,16 +230,16 @@ void VideoOutput::run_for(const Cycles cycles) { while(number_of_cycles) { int draw_action_length = screen_map_[screen_map_pointer_].length; int time_left_in_action = std::min(number_of_cycles, draw_action_length - cycles_into_draw_action_); - if(screen_map_[screen_map_pointer_].type == DrawAction::Pixels) output_pixels(static_cast(time_left_in_action)); + if(screen_map_[screen_map_pointer_].type == DrawAction::Pixels) output_pixels(time_left_in_action); number_of_cycles -= time_left_in_action; cycles_into_draw_action_ += time_left_in_action; if(cycles_into_draw_action_ == draw_action_length) { switch(screen_map_[screen_map_pointer_].type) { - case DrawAction::Sync: crt_->output_sync(static_cast(draw_action_length * crt_cycles_multiplier)); break; - case DrawAction::ColourBurst: crt_->output_default_colour_burst(static_cast(draw_action_length * crt_cycles_multiplier)); break; - case DrawAction::Blank: crt_->output_blank(static_cast(draw_action_length * crt_cycles_multiplier)); break; - case DrawAction::Pixels: end_pixel_line(); break; + case DrawAction::Sync: crt_->output_sync(draw_action_length * crt_cycles_multiplier); break; + case DrawAction::ColourBurst: crt_->output_default_colour_burst(draw_action_length * crt_cycles_multiplier); break; + case DrawAction::Blank: crt_->output_blank(draw_action_length * crt_cycles_multiplier); break; + case DrawAction::Pixels: end_pixel_line(); break; } screen_map_pointer_ = (screen_map_pointer_ + 1) % screen_map_.size(); cycles_into_draw_action_ = 0; diff --git a/Machines/Electron/Video.hpp b/Machines/Electron/Video.hpp index 04aa939f2..232440281 100644 --- a/Machines/Electron/Video.hpp +++ b/Machines/Electron/Video.hpp @@ -77,7 +77,7 @@ class VideoOutput { private: inline void start_pixel_line(); inline void end_pixel_line(); - inline void output_pixels(unsigned int number_of_cycles); + inline void output_pixels(int number_of_cycles); inline void setup_base_address(); int output_position_ = 0; @@ -109,7 +109,7 @@ class VideoOutput { // CRT output uint8_t *current_output_target_ = nullptr; uint8_t *initial_output_target_ = nullptr; - unsigned int current_output_divider_ = 1; + int current_output_divider_ = 1; std::unique_ptr crt_; diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index e49689cb0..46bd480c7 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -172,7 +172,7 @@ class ConcreteMachine: vdp_->set_tv_standard( (region_ == Target::Region::Europe) ? TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC); - get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite); +// get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite); time_until_debounce_ = vdp_->get_time_until_line(-1); } diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index e28d7cfe0..113c05271 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -24,21 +24,21 @@ VideoOutput::VideoOutput(uint8_t *memory) : crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::CRT::DisplayType::PAL50, 2)), v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition), counter_period_(PAL50Period) { - crt_->set_rgb_sampling_function( - "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" - "{" - "uint texValue = texture(sampler, coordinate).r;" - "return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));" - "}"); - crt_->set_composite_sampling_function( - "float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)" - "{" - "uint texValue = uint(dot(texture(sampler, coordinate).rg, uvec2(1, 256)));" - "uint iPhase = uint((phase + 3.141592654 + 0.39269908175) * 2.0 / 3.141592654) & 3u;" - "texValue = (texValue >> (4u*(3u - iPhase))) & 15u;" - "return (float(texValue) - 4.0) / 20.0;" - "}" - ); +// crt_->set_rgb_sampling_function( +// "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" +// "{" +// "uint texValue = texture(sampler, coordinate).r;" +// "return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));" +// "}"); +// crt_->set_composite_sampling_function( +// "float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)" +// "{" +// "uint texValue = uint(dot(texture(sampler, coordinate).rg, uvec2(1, 256)));" +// "uint iPhase = uint((phase + 3.141592654 + 0.39269908175) * 2.0 / 3.141592654) & 3u;" +// "texValue = (texValue >> (4u*(3u - iPhase))) & 15u;" +// "return (float(texValue) - 4.0) / 20.0;" +// "}" +// ); crt_->set_composite_function_type(Outputs::CRT::CRT::CompositeSourceType::DiscreteFourSamplesPerCycle, 0.0f); set_video_signal(Outputs::CRT::VideoSignal::Composite); @@ -47,7 +47,7 @@ VideoOutput::VideoOutput(uint8_t *memory) : void VideoOutput::set_video_signal(Outputs::CRT::VideoSignal video_signal) { video_signal_ = video_signal; - crt_->set_video_signal(video_signal); +// crt_->set_video_signal(video_signal); } void VideoOutput::set_colour_rom(const std::vector &rom) { @@ -86,7 +86,7 @@ void VideoOutput::run_for(const Cycles cycles) { if(counter_ >= v_sync_start_position_ && counter_ < v_sync_end_position_) { // this is a sync line cycles_run_for = v_sync_end_position_ - counter_; - clamp(crt_->output_sync(static_cast(v_sync_end_position_ - v_sync_start_position_) * 6)); + clamp(crt_->output_sync((v_sync_end_position_ - v_sync_start_position_) * 6)); } else if(counter_ < 224*64 && h_counter < 40) { // this is a pixel line if(!h_counter) { @@ -199,7 +199,7 @@ void VideoOutput::run_for(const Cycles cycles) { cycles_run_for = 48 - h_counter; clamp( int period = (counter_ < 224*64) ? 8 : 48; - crt_->output_blank(static_cast(period) * 6); + crt_->output_blank(period * 6); ); } else if(h_counter < 54) { cycles_run_for = 54 - h_counter; diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index ce2fcbc55..26445c026 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -25,14 +25,14 @@ Video::Video() : // Set a composite sampling function that assumes two-level input; either a byte is 0, which is black, // or it is non-zero, which is white. - crt_->set_composite_sampling_function( - "float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)" - "{" - "return texture(sampler, coordinate).r;" - "}"); +// crt_->set_composite_sampling_function( +// "float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)" +// "{" +// "return texture(sampler, coordinate).r;" +// "}"); // Show only the centre 80% of the TV frame. - crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); +// crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); crt_->set_visible_area(Outputs::CRT::Rect(0.1f, 0.1f, 0.8f, 0.8f)); } @@ -48,7 +48,7 @@ void Video::flush() { void Video::flush(bool next_sync) { if(sync_) { // If in sync, that takes priority. Output the proper amount of sync. - crt_->output_sync(static_cast(time_since_update_.as_int())); + crt_->output_sync(time_since_update_.as_int()); } else { // If not presently in sync, then... @@ -58,7 +58,7 @@ void Video::flush(bool next_sync) { int data_length = static_cast(line_data_pointer_ - line_data_); if(data_length < time_since_update_.as_int() || next_sync) { auto output_length = std::min(data_length, time_since_update_.as_int()); - crt_->output_data(static_cast(output_length), static_cast(output_length)); + crt_->output_data(output_length); line_data_pointer_ = line_data_ = nullptr; time_since_update_ -= HalfCycles(output_length); } else return; @@ -67,7 +67,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)); if(colour_pointer) *colour_pointer = 0xff; - crt_->output_level(static_cast(time_since_update_.as_int())); + crt_->output_level(time_since_update_.as_int()); } time_since_update_ = 0; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 95080054f..d0942d194 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -718,6 +718,7 @@ 4B0B6E121C9DBD5D00FFB60D /* CRTConstants.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CRTConstants.hpp; sourceTree = ""; }; 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRT.cpp; sourceTree = ""; }; 4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = ""; }; + 4B0CD7252189117C00665042 /* ScanTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ScanTarget.hpp; sourceTree = ""; }; 4B0E04E81FC9E5DA00F43484 /* CAS.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAS.cpp; sourceTree = ""; }; 4B0E04E91FC9E5DA00F43484 /* CAS.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CAS.hpp; sourceTree = ""; }; 4B0E04F81FC9FA3000F43484 /* 9918.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 9918.hpp; path = 9918/9918.hpp; sourceTree = ""; }; @@ -1527,6 +1528,7 @@ 4B0CCC411C62D0B3001CAC5F /* CRT */ = { isa = PBXGroup; children = ( + 4B0CD7252189117C00665042 /* ScanTarget.hpp */, 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */, 4B0CCC431C62D0B3001CAC5F /* CRT.hpp */, 4BBF99191C8FC2750075DAFB /* CRTTypes.hpp */, diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 7305fee4e..de86c6019 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -231,12 +231,12 @@ struct ActivityObserver: public Activity::Observer { _machine->crt_machine()->setup_output(aspectRatio); // Since OS X v10.6, Macs have had a gamma of 2.2. - _machine->crt_machine()->get_crt()->set_output_gamma(2.2f); - _machine->crt_machine()->get_crt()->set_target_framebuffer(0); +// _machine->crt_machine()->get_crt()->set_output_gamma(2.2f); +// _machine->crt_machine()->get_crt()->set_target_framebuffer(0); } - (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty { - _machine->crt_machine()->get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); +// _machine->crt_machine()->get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); } - (void)paste:(NSString *)paste { diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 3535f33ab..9e0386a4a 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -15,18 +15,18 @@ using namespace Outputs::CRT; -void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int vertical_sync_half_lines, bool should_alternate) { - openGL_output_builder_.set_colour_format(colour_space, colour_cycle_numerator, colour_cycle_denominator); +void CRT::set_new_timing(int cycles_per_line, int height_of_display, ColourSpace colour_space, int colour_cycle_numerator, int colour_cycle_denominator, int vertical_sync_half_lines, bool should_alternate) { +// openGL_output_builder_.set_colour_format(colour_space, colour_cycle_numerator, colour_cycle_denominator); - const unsigned int millisecondsHorizontalRetraceTime = 7; // source: Dictionary of Video and Television Technology, p. 234 - const unsigned int scanlinesVerticalRetraceTime = 8; // source: ibid + const int millisecondsHorizontalRetraceTime = 7; // source: Dictionary of Video and Television Technology, p. 234 + const int scanlinesVerticalRetraceTime = 8; // source: ibid - // To quote: - // - // "retrace interval; The interval of time for the return of the blanked scanning beam of - // a TV picture tube or camera tube to the starting point of a line or field. It is about - // 7 microseconds for horizontal retrace and 500 to 750 microseconds for vertical retrace - // in NTSC and PAL TV." + // To quote: + // + // "retrace interval; The interval of time for the return of the blanked scanning beam of + // a TV picture tube or camera tube to the starting point of a line or field. It is about + // 7 microseconds for horizontal retrace and 500 to 750 microseconds for vertical retrace + // in NTSC and PAL TV." time_multiplier_ = IntermediateBufferWidth / cycles_per_line; phase_denominator_ = cycles_per_line * colour_cycle_denominator * time_multiplier_; @@ -35,7 +35,7 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di phase_alternates_ = should_alternate; is_alernate_line_ &= phase_alternates_; cycles_per_line_ = cycles_per_line; - unsigned int multiplied_cycles_per_line = cycles_per_line * time_multiplier_; + const int multiplied_cycles_per_line = cycles_per_line * time_multiplier_; // allow sync to be detected (and acted upon) a line earlier than the specified requirement, // as a simple way of avoiding not-quite-exact comparison issues while still being true enough to @@ -55,13 +55,15 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_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 - unsigned int real_clock_scan_period = (multiplied_cycles_per_line * height_of_display) / (time_multiplier_ * common_output_divisor_); + const int real_clock_scan_period = (multiplied_cycles_per_line * height_of_display) / (time_multiplier_ * common_output_divisor_); vertical_flywheel_output_divider_ = static_cast(ceilf(real_clock_scan_period / 65536.0f) * (time_multiplier_ * common_output_divisor_)); - 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_); +// 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_); + + // TODO: set scan_target modals. } -void CRT::set_new_display_type(unsigned int cycles_per_line, DisplayType displayType) { +void CRT::set_new_display_type(int cycles_per_line, DisplayType displayType) { switch(displayType) { case DisplayType::PAL50: set_new_timing(cycles_per_line, 312, ColourSpace::YUV, 709379, 2500, 5, true); // i.e. 283.7516; 2.5 lines = vertical sync @@ -84,175 +86,99 @@ void CRT::set_composite_function_type(CompositeSourceType type, float offset_of_ } void CRT::set_input_gamma(float gamma) { - input_gamma_ = gamma; - update_gamma(); +// input_gamma_ = gamma; +// update_gamma(); } -void CRT::set_output_gamma(float gamma) { - output_gamma_ = gamma; - update_gamma(); -} +CRT::CRT(int common_output_divisor, int buffer_depth) : + common_output_divisor_(common_output_divisor) {} -void CRT::update_gamma() { - float gamma_ratio = input_gamma_ / output_gamma_; - openGL_output_builder_.set_gamma(gamma_ratio); -} - -CRT::CRT(unsigned int common_output_divisor, unsigned int buffer_depth) : - common_output_divisor_(common_output_divisor), - openGL_output_builder_(buffer_depth) {} - -CRT::CRT( unsigned int cycles_per_line, - unsigned int common_output_divisor, - unsigned int height_of_display, +CRT::CRT( int cycles_per_line, + int common_output_divisor, + int height_of_display, ColourSpace colour_space, - unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, - unsigned int vertical_sync_half_lines, + int colour_cycle_numerator, int colour_cycle_denominator, + int vertical_sync_half_lines, bool should_alternate, - unsigned int buffer_depth) : + int buffer_depth) : CRT(common_output_divisor, buffer_depth) { 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(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int buffer_depth) : +CRT::CRT(int cycles_per_line, int common_output_divisor, DisplayType displayType, int buffer_depth) : CRT(common_output_divisor, buffer_depth) { set_new_display_type(cycles_per_line, displayType); } // MARK: - Sync loop -Flywheel::SyncEvent CRT::get_next_vertical_sync_event(bool vsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced) { +Flywheel::SyncEvent CRT::get_next_vertical_sync_event(bool vsync_is_requested, int cycles_to_run_for, int *cycles_advanced) { return vertical_flywheel_->get_next_event_in_period(vsync_is_requested, cycles_to_run_for, cycles_advanced); } -Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced) { +Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested, int cycles_to_run_for, int *cycles_advanced) { return horizontal_flywheel_->get_next_event_in_period(hsync_is_requested, cycles_to_run_for, cycles_advanced); } -#define output_x1() (*reinterpret_cast(&next_output_run[OutputVertexOffsetOfHorizontal + 0])) -#define output_x2() (*reinterpret_cast(&next_output_run[OutputVertexOffsetOfHorizontal + 2])) -#define output_position_y() (*reinterpret_cast(&next_output_run[OutputVertexOffsetOfVertical + 0])) -#define output_tex_y() (*reinterpret_cast(&next_output_run[OutputVertexOffsetOfVertical + 2])) - -#define source_input_position_y() (*reinterpret_cast(&next_run[SourceVertexOffsetOfInputStart + 2])) -#define source_output_position_x1() (*reinterpret_cast(&next_run[SourceVertexOffsetOfOutputStart + 0])) -#define source_output_position_x2() (*reinterpret_cast(&next_run[SourceVertexOffsetOfEnds + 2])) -#define source_phase() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 0] -#define source_amplitude() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 1] - -void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bool vsync_requested, const Scan::Type type) { - std::unique_lock output_lock = openGL_output_builder_.get_output_lock(); +void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_requested, const Scan::Type type, int number_of_samples) { number_of_cycles *= time_multiplier_; - bool is_output_run = ((type == Scan::Type::Level) || (type == Scan::Type::Data)); + const bool is_output_run = ((type == Scan::Type::Level) || (type == Scan::Type::Data)); + const auto total_cycles = number_of_cycles * time_multiplier_; while(number_of_cycles) { - unsigned int time_until_vertical_sync_event, time_until_horizontal_sync_event; + // Get time until next horizontal and vertical sync generator events. + int time_until_vertical_sync_event, time_until_horizontal_sync_event; Flywheel::SyncEvent next_vertical_sync_event = get_next_vertical_sync_event(vsync_requested, number_of_cycles, &time_until_vertical_sync_event); Flywheel::SyncEvent next_horizontal_sync_event = get_next_horizontal_sync_event(hsync_requested, time_until_vertical_sync_event, &time_until_horizontal_sync_event); - // get the next sync event and its timing; hsync request is instantaneous (being edge triggered) so - // set it to false for the next run through this loop (if any) - unsigned int next_run_length = std::min(time_until_vertical_sync_event, time_until_horizontal_sync_event); - phase_numerator_ += next_run_length * colour_cycle_numerator_; - phase_numerator_ %= phase_denominator_; + // Whichever event is scheduled to happen first is the one to advance to. + int next_run_length = std::min(time_until_vertical_sync_event, time_until_horizontal_sync_event); hsync_requested = false; vsync_requested = false; + // Determine whether to output any data for this portion of the output; if so then grab somewhere to put it. bool is_output_segment = ((is_output_run && next_run_length) && !horizontal_flywheel_->is_in_retrace() && !vertical_flywheel_->is_in_retrace()); - uint8_t *next_run = nullptr; - if(is_output_segment && !openGL_output_builder_.composite_output_buffer_is_full()) { - bool did_retain_source_data = openGL_output_builder_.texture_builder.retain_latest(); - if(did_retain_source_data) { - next_run = openGL_output_builder_.array_builder.get_input_storage(SourceVertexSize); - if(!next_run) { - openGL_output_builder_.texture_builder.discard_latest(); - } - } + ScanTarget::Scan *const next_scan = is_output_segment ? scan_target_->get_scan() : nullptr; + + // 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].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_; } - if(next_run) { - // output_y and texture locations will be written later; we won't necessarily know what they are - // outside of the locked region - source_output_position_x1() = static_cast(horizontal_flywheel_->get_current_output_position()); - source_phase() = colour_burst_phase_; - - // TODO: determine what the PAL phase-shift machines actually do re: the swinging burst. - source_amplitude() = phase_alternates_ ? 128 - colour_burst_amplitude_ : 128 + colour_burst_amplitude_; - } - - // decrement the number of cycles left to run for and increment the - // horizontal counter appropriately + // Advance time: that'll affect both the colour subcarrier position and the number of cycles left to run. + phase_numerator_ += next_run_length * colour_cycle_numerator_; + phase_numerator_ %= phase_denominator_; number_of_cycles -= next_run_length; - // react to the incoming event... + // React to the incoming event. 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); - if(next_run) { - source_output_position_x2() = static_cast(horizontal_flywheel_->get_current_output_position()); - } - - // if this is horizontal retrace then advance the output line counter and bookend an output run - Flywheel::SyncEvent honoured_event = Flywheel::SyncEvent::None; - if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event != Flywheel::SyncEvent::None) honoured_event = next_vertical_sync_event; - if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) honoured_event = next_horizontal_sync_event; - bool needs_endpoint = - (honoured_event == Flywheel::SyncEvent::StartRetrace && is_writing_composite_run_) || - (honoured_event == Flywheel::SyncEvent::EndRetrace && !horizontal_flywheel_->is_in_retrace() && !vertical_flywheel_->is_in_retrace()); - - if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) is_alernate_line_ ^= phase_alternates_; - - if(needs_endpoint) { - if( - !openGL_output_builder_.array_builder.is_full() && - !openGL_output_builder_.composite_output_buffer_is_full()) { - - if(!is_writing_composite_run_) { - output_run_.x1 = static_cast(horizontal_flywheel_->get_current_output_position()); - output_run_.y = static_cast(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_); - } else { - // Get and write all those previously unwritten output ys - const uint16_t output_y = openGL_output_builder_.get_composite_output_y(); - - // Construct the output run - uint8_t *next_output_run = openGL_output_builder_.array_builder.get_output_storage(OutputVertexSize); - if(next_output_run) { - output_x1() = output_run_.x1; - output_position_y() = output_run_.y; - output_tex_y() = output_y; - output_x2() = static_cast(horizontal_flywheel_->get_current_output_position()); - } - - // TODO: below I've assumed a one-to-one correspondance with output runs and input data; that's - // obviously not completely sustainable. It's a latent bug. - openGL_output_builder_.array_builder.flush( - [=] (uint8_t *input_buffer, std::size_t input_size, uint8_t *output_buffer, std::size_t output_size) { - openGL_output_builder_.texture_builder.flush( - [=] (const std::vector &write_areas, std::size_t number_of_write_areas) { -// assert(number_of_write_areas * SourceVertexSize == input_size); - if(number_of_write_areas * SourceVertexSize == input_size) { - for(std::size_t run = 0; run < number_of_write_areas; run++) { - *reinterpret_cast(&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 0]) = write_areas[run].x; - *reinterpret_cast(&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 2]) = write_areas[run].y; - *reinterpret_cast(&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfEnds + 0]) = write_areas[run].x + write_areas[run].length; - } - } - }); - for(std::size_t position = 0; position < input_size; position += SourceVertexSize) { - (*reinterpret_cast(&input_buffer[position + SourceVertexOffsetOfOutputStart + 2])) = output_y; - } - }); - colour_burst_amplitude_ = 0; - } - is_writing_composite_run_ ^= true; - } + // 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].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); } + // 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) { - openGL_output_builder_.increment_composite_output_y(); + scan_target_->announce(Outputs::CRT::ScanTarget::Event::HorizontalRetrace); + 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::CRT::ScanTarget::Event::VerticalRetrace); } // if this is vertical retrace then adcance a field @@ -260,9 +186,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo if(delegate_) { frames_since_last_delegate_call_++; if(frames_since_last_delegate_call_ == 20) { - output_lock.unlock(); delegate_->crt_did_end_batch_of_frames(this, frames_since_last_delegate_call_, vertical_flywheel_->get_and_reset_number_of_surprises()); - output_lock.lock(); frames_since_last_delegate_call_ = 0; } } @@ -270,29 +194,18 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo } } -#undef output_x1 -#undef output_x2 -#undef output_position_y -#undef output_tex_y - -#undef source_input_position_y -#undef source_output_position_x1 -#undef source_output_position_x2 -#undef source_phase -#undef source_amplitude - // MARK: - stream feeding methods void CRT::output_scan(const Scan *const scan) { // simplified colour burst logic: if it's within the back porch we'll take it if(scan->type == Scan::Type::ColourBurst) { if(!colour_burst_amplitude_ && horizontal_flywheel_->get_current_time() < (horizontal_flywheel_->get_standard_period() * 12) >> 6) { - unsigned int position_phase = (horizontal_flywheel_->get_current_time() * colour_cycle_numerator_ * 256) / phase_denominator_; - colour_burst_phase_ = (position_phase + scan->phase) & 255; +// int position_phase = (horizontal_flywheel_->get_current_time() * colour_cycle_numerator_ * 256) / phase_denominator_; +// colour_burst_phase_ = (position_phase + scan->phase) & 255; colour_burst_amplitude_ = scan->amplitude; - if(colour_burst_phase_adjustment_ != 0xff) - colour_burst_phase_ = (colour_burst_phase_ & ~63) + colour_burst_phase_adjustment_; +// if(colour_burst_phase_adjustment_ != 0xff) +// colour_burst_phase_ = (colour_burst_phase_ & ~63) + colour_burst_phase_adjustment_; } } // TODO: inspect raw data for potential colour burst if required; the DPLL and some zero crossing logic @@ -323,7 +236,7 @@ void CRT::output_scan(const Scan *const scan) { } } - unsigned int number_of_cycles = scan->number_of_cycles; + int number_of_cycles = scan->number_of_cycles; bool vsync_requested = false; // if sync is being accumulated then accumulate it; if it crosses the vertical sync threshold then @@ -332,10 +245,10 @@ void CRT::output_scan(const Scan *const scan) { cycles_of_sync_ += scan->number_of_cycles; if(this_is_sync && cycles_of_sync_ >= sync_capacitor_charge_threshold_) { - unsigned int overshoot = std::min(cycles_of_sync_ - sync_capacitor_charge_threshold_, number_of_cycles); + int overshoot = std::min(cycles_of_sync_ - sync_capacitor_charge_threshold_, number_of_cycles); if(overshoot) { number_of_cycles -= overshoot; - advance_cycles(number_of_cycles, hsync_requested, false, scan->type); + advance_cycles(number_of_cycles, hsync_requested, false, scan->type, 0); hsync_requested = false; number_of_cycles = overshoot; } @@ -345,35 +258,36 @@ void CRT::output_scan(const Scan *const scan) { } } - advance_cycles(number_of_cycles, hsync_requested, vsync_requested, scan->type); + advance_cycles(number_of_cycles, hsync_requested, vsync_requested, scan->type, scan->number_of_samples); } /* These all merely channel into advance_cycles, supplying appropriate arguments */ -void CRT::output_sync(unsigned int number_of_cycles) { +void CRT::output_sync(int number_of_cycles) { Scan scan; scan.type = Scan::Type::Sync; scan.number_of_cycles = number_of_cycles; output_scan(&scan); } -void CRT::output_blank(unsigned int number_of_cycles) { +void CRT::output_blank(int number_of_cycles) { Scan scan; scan.type = Scan::Type::Blank; scan.number_of_cycles = number_of_cycles; output_scan(&scan); } -void CRT::output_level(unsigned int number_of_cycles) { - openGL_output_builder_.texture_builder.reduce_previous_allocation_to(1); +void CRT::output_level(int number_of_cycles) { + scan_target_->reduce_previous_allocation_to(1); Scan scan; scan.type = Scan::Type::Level; scan.number_of_cycles = number_of_cycles; + scan.number_of_samples = 1; output_scan(&scan); } -void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude) { +void CRT::output_colour_burst(int number_of_cycles, uint8_t phase, uint8_t amplitude) { Scan scan; scan.type = Scan::Type::ColourBurst; scan.number_of_cycles = number_of_cycles; @@ -382,20 +296,21 @@ void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint output_scan(&scan); } -void CRT::output_default_colour_burst(unsigned int number_of_cycles) { +void CRT::output_default_colour_burst(int number_of_cycles) { output_colour_burst(number_of_cycles, static_cast((phase_numerator_ * 256) / phase_denominator_)); } void CRT::set_immediate_default_phase(float phase) { phase = fmodf(phase, 1.0f); - phase_numerator_ = static_cast(phase * static_cast(phase_denominator_)); + phase_numerator_ = static_cast(phase * static_cast(phase_denominator_)); } -void CRT::output_data(unsigned int number_of_cycles, unsigned int number_of_samples) { - openGL_output_builder_.texture_builder.reduce_previous_allocation_to(number_of_samples); +void CRT::output_data(int number_of_cycles, size_t number_of_samples) { + scan_target_->reduce_previous_allocation_to(number_of_samples); Scan scan; scan.type = Scan::Type::Data; scan.number_of_cycles = number_of_cycles; + scan.number_of_samples = int(number_of_samples); output_scan(&scan); } @@ -407,30 +322,30 @@ Outputs::CRT::Rect CRT::get_rect_for_area(int first_line_after_sync, int number_ number_of_lines += 4; // determine prima facie x extent - unsigned int horizontal_period = horizontal_flywheel_->get_standard_period(); - unsigned int horizontal_scan_period = horizontal_flywheel_->get_scan_period(); - unsigned int horizontal_retrace_period = horizontal_period - horizontal_scan_period; + int horizontal_period = horizontal_flywheel_->get_standard_period(); + int horizontal_scan_period = horizontal_flywheel_->get_scan_period(); + int horizontal_retrace_period = horizontal_period - horizontal_scan_period; // make sure that the requested range is visible - if(static_cast(first_cycle_after_sync) < horizontal_retrace_period) first_cycle_after_sync = static_cast(horizontal_retrace_period); - if(static_cast(first_cycle_after_sync + number_of_cycles) > horizontal_scan_period) number_of_cycles = static_cast(horizontal_scan_period - static_cast(first_cycle_after_sync)); + if(static_cast(first_cycle_after_sync) < horizontal_retrace_period) first_cycle_after_sync = static_cast(horizontal_retrace_period); + if(static_cast(first_cycle_after_sync + number_of_cycles) > horizontal_scan_period) number_of_cycles = static_cast(horizontal_scan_period - static_cast(first_cycle_after_sync)); - float start_x = static_cast(static_cast(first_cycle_after_sync) - horizontal_retrace_period) / static_cast(horizontal_scan_period); + float start_x = static_cast(static_cast(first_cycle_after_sync) - horizontal_retrace_period) / static_cast(horizontal_scan_period); float width = static_cast(number_of_cycles) / static_cast(horizontal_scan_period); // determine prima facie y extent - unsigned int vertical_period = vertical_flywheel_->get_standard_period(); - unsigned int vertical_scan_period = vertical_flywheel_->get_scan_period(); - unsigned int vertical_retrace_period = vertical_period - vertical_scan_period; + int vertical_period = vertical_flywheel_->get_standard_period(); + int vertical_scan_period = vertical_flywheel_->get_scan_period(); + int vertical_retrace_period = vertical_period - vertical_scan_period; // make sure that the requested range is visible -// if(static_cast(first_line_after_sync) * horizontal_period < vertical_retrace_period) +// if(static_cast(first_line_after_sync) * horizontal_period < vertical_retrace_period) // first_line_after_sync = (vertical_retrace_period + horizontal_period - 1) / horizontal_period; // if((first_line_after_sync + number_of_lines) * horizontal_period > vertical_scan_period) -// number_of_lines = static_cast(horizontal_scan_period - static_cast(first_cycle_after_sync)); +// number_of_lines = static_cast(horizontal_scan_period - static_cast(first_cycle_after_sync)); - float start_y = static_cast((static_cast(first_line_after_sync) * horizontal_period) - vertical_retrace_period) / static_cast(vertical_scan_period); - float height = static_cast(static_cast(number_of_lines) * horizontal_period) / vertical_scan_period; + float start_y = static_cast((static_cast(first_line_after_sync) * horizontal_period) - vertical_retrace_period) / static_cast(vertical_scan_period); + float height = static_cast(static_cast(number_of_lines) * horizontal_period) / vertical_scan_period; // adjust to ensure aspect ratio is correct float adjusted_aspect_ratio = (3.0f*aspect_ratio / 4.0f); diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 814b41503..973e4106b 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -26,17 +26,17 @@ class CRT; class Delegate { public: - virtual void crt_did_end_batch_of_frames(CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs) = 0; + virtual void crt_did_end_batch_of_frames(CRT *crt, int number_of_frames, int number_of_unexpected_vertical_syncs) = 0; }; class CRT { private: - CRT(unsigned int common_output_divisor, unsigned int buffer_depth); + CRT(int common_output_divisor, int buffer_depth); // the incoming clock lengths will be multiplied by something to give at least 1000 // sample points per line - unsigned int time_multiplier_ = 1; - const unsigned int common_output_divisor_ = 1; + int time_multiplier_ = 1; + const int common_output_divisor_ = 1; // the two flywheels regulating scanning std::unique_ptr horizontal_flywheel_, vertical_flywheel_; @@ -46,7 +46,7 @@ class CRT { enum Type { Sync, Level, Data, Blank, ColourBurst } type; - unsigned int number_of_cycles; + int number_of_cycles, number_of_samples; union { struct { uint8_t phase, amplitude; @@ -55,52 +55,37 @@ class CRT { }; void output_scan(const Scan *scan); - uint8_t colour_burst_phase_ = 0, colour_burst_amplitude_ = 30, colour_burst_phase_adjustment_ = 0; + int16_t colour_burst_angle_ = 0; + uint8_t colour_burst_amplitude_ = 30; + int colour_burst_phase_adjustment_ = 0; bool is_writing_composite_run_ = false; - unsigned int phase_denominator_ = 1, phase_numerator_ = 1, colour_cycle_numerator_ = 1; + int phase_denominator_ = 1, phase_numerator_ = 1, colour_cycle_numerator_ = 1; bool is_alernate_line_ = false, phase_alternates_ = false; // the outer entry point for dispatching output_sync, output_blank, output_level and output_data - void advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bool vsync_requested, const Scan::Type type); + void advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_requested, const Scan::Type type, int number_of_samples); // the inner entry point that determines whether and when the next sync event will occur within // the current output window - Flywheel::SyncEvent get_next_vertical_sync_event(bool vsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced); - Flywheel::SyncEvent get_next_horizontal_sync_event(bool hsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced); - - // OpenGL state - OpenGLOutputBuilder openGL_output_builder_; - - // temporary storage used during the construction of output runs - struct { - uint16_t x1, y; - } output_run_; + Flywheel::SyncEvent get_next_vertical_sync_event(bool vsync_is_requested, int cycles_to_run_for, int *cycles_advanced); + Flywheel::SyncEvent get_next_horizontal_sync_event(bool hsync_is_requested, int cycles_to_run_for, int *cycles_advanced); // the delegate Delegate *delegate_ = nullptr; - unsigned int frames_since_last_delegate_call_ = 0; - - // queued tasks for the OpenGL queue; performed before the next draw - std::mutex function_mutex_; - std::vector> enqueued_openGL_functions_; - inline void enqueue_openGL_function(const std::function &function) { - std::lock_guard function_guard(function_mutex_); - enqueued_openGL_functions_.push_back(function); - } + int frames_since_last_delegate_call_ = 0; // sync counter, for determining vertical sync bool is_receiving_sync_ = false; // true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync) bool is_accumulating_sync_ = false; // true if a sync level has triggered the suspicion that a vertical sync might be in progress bool is_refusing_sync_ = false; // true once a vertical sync has been detected, until a prolonged period of non-sync has ended suspicion of an ongoing vertical sync - unsigned int sync_capacitor_charge_threshold_ = 0; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync - unsigned int cycles_of_sync_ = 0; // the number of cycles since the potential vertical sync began - unsigned int cycles_since_sync_ = 0; // the number of cycles since last in sync, for defeating the possibility of this being a vertical sync + int sync_capacitor_charge_threshold_ = 0; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync + int cycles_of_sync_ = 0; // the number of cycles since the potential vertical sync began + int cycles_since_sync_ = 0; // the number of cycles since last in sync, for defeating the possibility of this being a vertical sync - unsigned int cycles_per_line_ = 1; + int cycles_per_line_ = 1; - float input_gamma_ = 1.0f, output_gamma_ = 1.0f; - void update_gamma(); + ScanTarget *scan_target_ = nullptr; public: /*! Constructs the CRT with a specified clock rate, height and colour subcarrier frequency. @@ -134,14 +119,14 @@ class CRT { @see @c set_rgb_sampling_function , @c set_composite_sampling_function */ - CRT(unsigned int cycles_per_line, - unsigned int common_output_divisor, - unsigned int height_of_display, + CRT(int cycles_per_line, + int common_output_divisor, + int height_of_display, ColourSpace colour_space, - unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, - unsigned int vertical_sync_half_lines, + int colour_cycle_numerator, int colour_cycle_denominator, + int vertical_sync_half_lines, bool should_alternate, - unsigned int buffer_depth); + int buffer_depth); /*! Constructs the CRT with the specified clock rate, with the display height and colour subcarrier frequency dictated by a standard display type and with the requested number of @@ -150,36 +135,36 @@ class CRT { Exactly identical to calling the designated constructor with colour subcarrier information looked up by display type. */ - CRT(unsigned int cycles_per_line, - unsigned int common_output_divisor, + CRT(int cycles_per_line, + int common_output_divisor, DisplayType displayType, - unsigned int buffer_depth); + int buffer_depth); /*! Resets the CRT with new timing information. The CRT then continues as though the new timing had been provided at construction. */ - void set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int vertical_sync_half_lines, bool should_alternate); + void set_new_timing(int cycles_per_line, int height_of_display, ColourSpace colour_space, int colour_cycle_numerator, int colour_cycle_denominator, int vertical_sync_half_lines, bool should_alternate); /*! Resets the CRT with new timing information derived from a new display type. The CRT then continues as though the new timing had been provided at construction. */ - void set_new_display_type(unsigned int cycles_per_line, DisplayType displayType); + void set_new_display_type(int cycles_per_line, DisplayType displayType); /*! Output at the sync level. @param number_of_cycles The amount of time to putput sync for. */ - void output_sync(unsigned int number_of_cycles); + void output_sync(int number_of_cycles); /*! Output at the blanking level. @param number_of_cycles The amount of time to putput the blanking level for. */ - void output_blank(unsigned int number_of_cycles); + void output_blank(int number_of_cycles); /*! Outputs the first written to the most-recently created run of data repeatedly for a prolonged period. @param number_of_cycles The number of cycles to repeat the output for. */ - void output_level(unsigned int number_of_cycles); + 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 @@ -191,11 +176,11 @@ class CRT { @see @c allocate_write_area , @c get_write_target_for_buffer */ - void output_data(unsigned int number_of_cycles, unsigned int number_of_samples); + void output_data(int number_of_cycles, size_t number_of_samples); /*! A shorthand form for output_data that assumes the number of cycles to output for is the same as the number of samples. */ - void output_data(unsigned int number_of_cycles) { - output_data(number_of_cycles, number_of_cycles); + void output_data(int number_of_cycles) { + output_data(number_of_cycles, size_t(number_of_cycles)); } /*! Outputs a colour burst. @@ -208,13 +193,13 @@ class CRT { @param amplitude The amplitude of the colour burst in 1/256ths of the amplitude of the positive portion of the wave. */ - void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude = 102); + void output_colour_burst(int number_of_cycles, uint8_t phase, uint8_t amplitude = 102); /*! Outputs a colour burst exactly in phase with CRT expectations using the idiomatic amplitude. @param number_of_cycles The length of the colour burst; */ - void output_default_colour_burst(unsigned int number_of_cycles); + void output_default_colour_burst(int number_of_cycles); /*! Sets the current phase of the colour subcarrier used by output_default_colour_burst. @@ -235,63 +220,12 @@ class CRT { @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) { - std::unique_lock output_lock = openGL_output_builder_.get_output_lock(); - return openGL_output_builder_.texture_builder.allocate_write_area(required_length, required_alignment); - } - - /*! Causes appropriate OpenGL or OpenGL ES calls to be issued in order to draw the current CRT state. - The caller is responsible for ensuring that a valid OpenGL context exists for the duration of this call. - */ - inline void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty) { - { - std::lock_guard function_guard(function_mutex_); - for(std::function function : enqueued_openGL_functions_) { - function(); - } - enqueued_openGL_functions_.clear(); - } - openGL_output_builder_.draw_frame(output_width, output_height, only_if_dirty); - } - - /*! Sets the OpenGL framebuffer to which output is drawn. */ - inline void set_target_framebuffer(GLint framebuffer) { - enqueue_openGL_function( [framebuffer, this] { - openGL_output_builder_.set_target_framebuffer(framebuffer); - }); + return scan_target_->allocate_write_area(required_length, required_alignment); } /*! Sets the gamma exponent for the simulated screen. */ void set_input_gamma(float gamma); - /*! Sets the gamma exponent for the real, tangible screen on which content will be drawn. */ - void set_output_gamma(float gamma); - - /*! Tells the CRT that the next call to draw_frame will occur on a different OpenGL context than - the previous. - - @param should_delete_resources If @c true then all resources, textures, vertex arrays, etc, - currently held by the CRT will be deleted now via calls to glDeleteTexture and equivalent. If - @c false then the references are simply marked as invalid. - */ - inline void set_openGL_context_will_change(bool should_delete_resources) { - enqueue_openGL_function([should_delete_resources, this] { - openGL_output_builder_.set_openGL_context_will_change(should_delete_resources); - }); - } - - /*! Sets a function that will map from whatever data the machine provided to a composite signal. - - @param shader A GLSL fragment including a function with the signature - `float composite_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)` - that evaluates to the composite signal level as a function of a source buffer, sampling location, colour - carrier phase and amplitude. - */ - inline void set_composite_sampling_function(const std::string &shader) { - enqueue_openGL_function([shader, this] { - openGL_output_builder_.set_composite_sampling_function(shader); - }); - } - enum CompositeSourceType { /// The composite function provides continuous output. Continuous, @@ -312,51 +246,7 @@ class CRT { */ void set_composite_function_type(CompositeSourceType type, float offset_of_first_sample = 0.0f); - /*! Sets a function that will map from whatever data the machine provided to an s-video signal. - - If the output mode is composite then a default mapping from RGB to the display's - output mode will be applied. - - @param shader A GLSL fragment including a function with the signature - `vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)` - that evaluates to the s-video signal level, luminance as the first component and chrominance - as the second, as a function of a source buffer, sampling location and colour - carrier phase; amplitude is supplied for its sign. - */ - inline void set_svideo_sampling_function(const std::string &shader) { - enqueue_openGL_function([shader, this] { - openGL_output_builder_.set_svideo_sampling_function(shader); - }); - } - - /*! Sets a function that will map from whatever data the machine provided to an RGB signal. - - If the output mode is composite or svideo then a default mapping from RGB to the display's - output mode will be applied. - - @param shader A GLSL fragent including a function with the signature - `vec3 rgb_sample(usampler2D sampler, vec2 coordinate)` that evaluates to an RGB colour - as a function of: - - * `usampler2D sampler` representing the source buffer; and - * `vec2 coordinate` representing the source buffer location to sample from in the range [0, 1). - */ - inline void set_rgb_sampling_function(const std::string &shader) { - enqueue_openGL_function([shader, this] { - openGL_output_builder_.set_rgb_sampling_function(shader); - }); - } - - inline void set_video_signal(VideoSignal video_signal) { - enqueue_openGL_function([video_signal, this] { - openGL_output_builder_.set_video_signal(video_signal); - }); - } - inline void set_visible_area(Rect visible_area) { - enqueue_openGL_function([visible_area, this] { - openGL_output_builder_.set_visible_area(visible_area); - }); } Rect get_rect_for_area(int first_line_after_sync, int number_of_lines, int first_cycle_after_sync, int number_of_cycles, float aspect_ratio); diff --git a/Outputs/CRT/CRTTypes.hpp b/Outputs/CRT/CRTTypes.hpp index 33fd33f3f..a674ed943 100644 --- a/Outputs/CRT/CRTTypes.hpp +++ b/Outputs/CRT/CRTTypes.hpp @@ -12,20 +12,6 @@ namespace Outputs { namespace CRT { -struct Rect { - struct { - float x, y; - } origin; - - struct { - float width, height; - } size; - - Rect() {} - Rect(float x, float y, float width, float height) : - origin({x, y}), size({width, height}) {} -}; - enum class DisplayType { PAL50, NTSC60 diff --git a/Outputs/CRT/Internals/Flywheel.hpp b/Outputs/CRT/Internals/Flywheel.hpp index 2525d5415..5edc82947 100644 --- a/Outputs/CRT/Internals/Flywheel.hpp +++ b/Outputs/CRT/Internals/Flywheel.hpp @@ -29,7 +29,7 @@ struct Flywheel { @param retrace_time The amount of time it takes to complete a retrace. @param sync_error_window The permitted deviation of sync timings from the norm. */ - Flywheel(unsigned int standard_period, unsigned int retrace_time, unsigned int sync_error_window) : + Flywheel(int standard_period, int retrace_time, int sync_error_window) : standard_period_(standard_period), retrace_time_(retrace_time), sync_error_window_(sync_error_window), @@ -60,11 +60,11 @@ struct Flywheel { @returns The next synchronisation event. */ - inline SyncEvent get_next_event_in_period(bool sync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced) { + inline SyncEvent get_next_event_in_period(bool sync_is_requested, int cycles_to_run_for, int *cycles_advanced) { // do we recognise this hsync, thereby adjusting future time expectations? if(sync_is_requested) { if(counter_ < sync_error_window_ || counter_ > expected_next_sync_ - sync_error_window_) { - unsigned int time_now = (counter_ < sync_error_window_) ? expected_next_sync_ + counter_ : counter_; + int time_now = (counter_ < sync_error_window_) ? expected_next_sync_ + counter_ : counter_; expected_next_sync_ = (3*expected_next_sync_ + time_now) >> 2; } else { number_of_surprises_++; @@ -78,7 +78,7 @@ struct Flywheel { } SyncEvent proposed_event = SyncEvent::None; - unsigned int proposed_sync_time = cycles_to_run_for; + int proposed_sync_time = cycles_to_run_for; // will we end an ongoing retrace? if(counter_ < retrace_time_ && counter_ + proposed_sync_time >= retrace_time_) { @@ -104,7 +104,7 @@ struct Flywheel { @param event The synchronisation event to apply after that period. */ - inline void apply_event(unsigned int cycles_advanced, SyncEvent event) { + inline void apply_event(int cycles_advanced, SyncEvent event) { counter_ += cycles_advanced; switch(event) { @@ -122,9 +122,9 @@ struct Flywheel { @returns The current output position. */ - inline unsigned int get_current_output_position() { + inline int get_current_output_position() { if(counter_ < retrace_time_) { - unsigned int retrace_distance = (counter_ * standard_period_) / retrace_time_; + int retrace_distance = (counter_ * standard_period_) / retrace_time_; if(retrace_distance > counter_before_retrace_) return 0; return counter_before_retrace_ - retrace_distance; } @@ -135,7 +135,7 @@ struct Flywheel { /*! @returns the amount of time since retrace last began. Time then counts monotonically up from zero. */ - inline unsigned int get_current_time() { + inline int get_current_time() { return counter_; } @@ -149,14 +149,14 @@ struct Flywheel { /*! @returns the expected length of the scan period (excluding retrace). */ - inline unsigned int get_scan_period() { + inline int get_scan_period() { return standard_period_ - retrace_time_; } /*! @returns the expected length of a complete scan and retrace cycle. */ - inline unsigned int get_standard_period() { + inline int get_standard_period() { return standard_period_; } @@ -164,8 +164,8 @@ struct Flywheel { @returns the number of synchronisation events that have seemed surprising since the last time this method was called; a low number indicates good synchronisation. */ - inline unsigned int get_and_reset_number_of_surprises() { - unsigned int result = number_of_surprises_; + inline int get_and_reset_number_of_surprises() { + const int result = number_of_surprises_; number_of_surprises_ = 0; return result; } @@ -174,19 +174,19 @@ struct Flywheel { @returns `true` if a sync is expected soon or the time at which it was expected was recent. */ inline bool is_near_expected_sync() { - return abs(static_cast(counter_) - static_cast(expected_next_sync_)) < static_cast(standard_period_) / 50; + return abs(counter_ - expected_next_sync_) < standard_period_ / 50; } private: - unsigned int standard_period_; // the normal length of time between syncs - const unsigned int retrace_time_; // a constant indicating the amount of time it takes to perform a retrace - const unsigned int sync_error_window_; // a constant indicating the window either side of the next expected sync in which we'll accept other syncs + const int standard_period_; // the normal length of time between syncs + const int retrace_time_; // a constant indicating the amount of time it takes to perform a retrace + const int sync_error_window_; // a constant indicating the window either side of the next expected sync in which we'll accept other syncs - unsigned int counter_; // time since the _start_ of the last sync - unsigned int counter_before_retrace_; // the value of _counter immediately before retrace began - unsigned int expected_next_sync_; // our current expection of when the next sync will be encountered (which implies velocity) + int counter_; // time since the _start_ of the last sync + int counter_before_retrace_; // the value of _counter immediately before retrace began + int expected_next_sync_; // our current expection of when the next sync will be encountered (which implies velocity) - unsigned int number_of_surprises_; // a count of the surprising syncs + int number_of_surprises_; // a count of the surprising syncs /* Implementation notes: diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.hpp b/Outputs/CRT/Internals/Shaders/OutputShader.hpp index 5a43832d2..a6ffc50a4 100644 --- a/Outputs/CRT/Internals/Shaders/OutputShader.hpp +++ b/Outputs/CRT/Internals/Shaders/OutputShader.hpp @@ -11,6 +11,7 @@ #include "Shader.hpp" #include "../../CRTTypes.hpp" +#include "../../ScanTarget.hpp" #include namespace OpenGL { diff --git a/Outputs/CRT/ScanTarget.hpp b/Outputs/CRT/ScanTarget.hpp index e1191a163..2f6773da9 100644 --- a/Outputs/CRT/ScanTarget.hpp +++ b/Outputs/CRT/ScanTarget.hpp @@ -12,6 +12,20 @@ namespace Outputs { namespace CRT { +struct Rect { + struct Point { + float x, y; + } origin; + + struct { + float width, height; + } size; + + Rect() {} + Rect(float x, float y, float width, float height) : + origin({x, y}), size({width, height}) {} +}; + enum class ColourSpace { /// YIQ is the NTSC colour space. YIQ, @@ -54,8 +68,10 @@ struct ScanTarget { // of a colour subcarrier. So they can be used to generate a luminance signal, // or an s-video pipeline. - Phase4Luminance4, // 1 byte/pixel; top nibble is a phase offset, bottom nibble is luminance. - Phase8Luminance8, // 1 bytes/pixel; first is phase, second is luminance. + Phase8Luminance8, // 2 bytes/pixel; first is phase, second is luminance. + // Phase is encoded on a 192-unit circle; anything + // greater than 192 implies that the colour part of + // the signal should be omitted. // The RGB types can directly feed an RGB pipeline, naturally, or can be mapped // to phase+luminance, or just to luminance. @@ -66,12 +82,28 @@ struct ScanTarget { Red8Green8Blue8, // 4 bytes/pixel; first is red, second is green, third is blue, fourth is vacant. } source_data_type; - // If being fed composite data, this defines the colour space in use. + /// 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 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. + int expected_vertical_lines; + + /// Provides an additional restriction on the section of the display that is expected + /// to contain interesting content. + Rect visible_area; + + /// Describes the + float intended_gamma; }; /// Sets the total format of input data. - virtual void set_modals(Modals); + virtual void set_modals(Modals) = 0; /* @@ -114,14 +146,14 @@ struct ScanTarget { } end_points[2]; /// For composite video, dictates the amplitude of the colour subcarrier as a proportion of - /// the whole, as determined from the colour burst. + /// the whole, as determined from the colour burst. Will be 0 if there was no colour burst. uint8_t composite_amplitude; }; /// Requests a new scan to populate. /// /// @return A valid pointer, or @c nullptr if insufficient further storage is available. - Scan *get_scan(); + virtual Scan *get_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. @@ -140,7 +172,7 @@ struct ScanTarget { /// /// The ScanTarget isn't bound to take any drawing action immediately; it may sit on submitted data for /// as long as it feels is appropriate subject to an @c flush. - virtual void submit() = 0; + virtual void submit(bool only_if_no_allocation_failures = true) = 0; /// Discards all data and endpoints supplied since the last @c submit. This is generally used when /// failures in either get_endpoing_pair of allocate_write_area mean that proceeding would produce @@ -164,7 +196,7 @@ struct ScanTarget { }; /// Provides a hint that the named event has occurred. - virtual void announce(Event event) = 0; + virtual void announce(Event event) {} }; }