From 4701aa149aeba7f40a2648f64eb9c49dede17beb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Oct 2018 22:08:17 -0400 Subject: [PATCH 001/208] Adds first draft of an interface to separate CRT logic from the GPU-side stuff. --- Outputs/CRT/CRT.hpp | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index edd7a7561..43e186588 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -27,6 +27,52 @@ class Delegate { virtual void crt_did_end_batch_of_frames(CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs) = 0; }; +struct ScanTarget { + struct Modals { + enum class DataType { + Luminance1, // 1 byte/pixel; any bit set => white; no bits set => black. + Luminance8, // 1 byte/pixel; linear scale. + + Phase4Luminance4, // 1 byte/pixel; top nibble is a phase offset, bottom nibble is luminance. + Phase8Luminance8, // 1 bytes/pixel; first is phase, second is luminance. + + Red1Green1Blue1, // 1 byte/pixel; bit 0 is blue on or off, bit 1 is green, bit 2 is red. + Red2Green2Blue2, // 1 byte/pixel; bits 0 and 1 are blue, bits 2 and 3 are green, bits 4 and 5 are blue. + Red4Green4Blue4, // 2 bytes/pixel; first nibble is red, second is green, third is blue. + Red8Green8Blue8, // 4 bytes/pixel; first is red, second is green, third is blue, fourth is vacant. + } source_data_type; + ColourSpace colour_space; + }; + virtual void set_modals(Modals); + + struct EndPoint { + uint32_t data_offset; + uint16_t x, y; + uint16_t phase; + uint8_t amplitude; + }; + EndPoint *get_endpoint_pair(); + + /// 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. + /// Calls must be paired off with calls to @c reduce_previous_allocation_to. + /// @returns a pointer to the allocated space if any was available; @c nullptr otherwise. + virtual uint8_t *allocate_write_area(std::size_t required_length, std::size_t required_alignment = 1) = 0; + + /// Announces that the owner is finished with the region created by the most recent @c allocate_write_area + /// and indicates that its actual final size was @c actual_length. + virtual void reduce_previous_allocation_to(std::size_t actual_length) = 0; + + virtual void submit() = 0; + virtual void reset() = 0; + + enum class Event { + HorizontalSync, + VerticalSync + }; + virtual void announce(Event event) = 0; +}; + class CRT { private: CRT(unsigned int common_output_divisor, unsigned int buffer_depth); From 373820f0801acd421eb88f103cdfdbb0836b4b6c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 30 Oct 2018 21:50:35 -0400 Subject: [PATCH 002/208] Attempts to establish interface to decouple scan output from generation. Restores some functionality that had dropped out in the interim: diagonal scans, decoupling of scan scaling from timing of the composite subcarrier. --- Outputs/CRT/CRT.hpp | 48 +------- Outputs/CRT/CRTTypes.hpp | 5 - Outputs/CRT/Internals/CRTOpenGL.hpp | 2 + Outputs/CRT/ScanTarget.hpp | 173 ++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 51 deletions(-) create mode 100644 Outputs/CRT/ScanTarget.hpp diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 43e186588..814b41503 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -17,6 +17,8 @@ #include "Internals/ArrayBuilder.hpp" #include "Internals/TextureBuilder.hpp" +#include "ScanTarget.hpp" + namespace Outputs { namespace CRT { @@ -27,52 +29,6 @@ class Delegate { virtual void crt_did_end_batch_of_frames(CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs) = 0; }; -struct ScanTarget { - struct Modals { - enum class DataType { - Luminance1, // 1 byte/pixel; any bit set => white; no bits set => black. - Luminance8, // 1 byte/pixel; linear scale. - - Phase4Luminance4, // 1 byte/pixel; top nibble is a phase offset, bottom nibble is luminance. - Phase8Luminance8, // 1 bytes/pixel; first is phase, second is luminance. - - Red1Green1Blue1, // 1 byte/pixel; bit 0 is blue on or off, bit 1 is green, bit 2 is red. - Red2Green2Blue2, // 1 byte/pixel; bits 0 and 1 are blue, bits 2 and 3 are green, bits 4 and 5 are blue. - Red4Green4Blue4, // 2 bytes/pixel; first nibble is red, second is green, third is blue. - Red8Green8Blue8, // 4 bytes/pixel; first is red, second is green, third is blue, fourth is vacant. - } source_data_type; - ColourSpace colour_space; - }; - virtual void set_modals(Modals); - - struct EndPoint { - uint32_t data_offset; - uint16_t x, y; - uint16_t phase; - uint8_t amplitude; - }; - EndPoint *get_endpoint_pair(); - - /// 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. - /// Calls must be paired off with calls to @c reduce_previous_allocation_to. - /// @returns a pointer to the allocated space if any was available; @c nullptr otherwise. - virtual uint8_t *allocate_write_area(std::size_t required_length, std::size_t required_alignment = 1) = 0; - - /// Announces that the owner is finished with the region created by the most recent @c allocate_write_area - /// and indicates that its actual final size was @c actual_length. - virtual void reduce_previous_allocation_to(std::size_t actual_length) = 0; - - virtual void submit() = 0; - virtual void reset() = 0; - - enum class Event { - HorizontalSync, - VerticalSync - }; - virtual void announce(Event event) = 0; -}; - class CRT { private: CRT(unsigned int common_output_divisor, unsigned int buffer_depth); diff --git a/Outputs/CRT/CRTTypes.hpp b/Outputs/CRT/CRTTypes.hpp index fbbf31aed..33fd33f3f 100644 --- a/Outputs/CRT/CRTTypes.hpp +++ b/Outputs/CRT/CRTTypes.hpp @@ -31,11 +31,6 @@ enum class DisplayType { NTSC60 }; -enum class ColourSpace { - YIQ, - YUV -}; - enum class VideoSignal { RGB, SVideo, diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 5d9359b64..bc246710a 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -22,6 +22,8 @@ #include "Shaders/IntermediateShader.hpp" #include "Rectangle.hpp" +#include "../ScanTarget.hpp" + #include #include diff --git a/Outputs/CRT/ScanTarget.hpp b/Outputs/CRT/ScanTarget.hpp new file mode 100644 index 000000000..e1191a163 --- /dev/null +++ b/Outputs/CRT/ScanTarget.hpp @@ -0,0 +1,173 @@ +// +// ScanTarget.hpp +// Clock Signal +// +// Created by Thomas Harte on 30/10/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef Outputs_CRT_ScanTarget_h +#define Outputs_CRT_ScanTarget_h + +namespace Outputs { +namespace CRT { + +enum class ColourSpace { + /// YIQ is the NTSC colour space. + YIQ, + + /// YUV is the PAL colour space. + YUV +}; + +/*! + Provides an abstract target for 'scans' i.e. continuous sweeps of output data, + which are identified by 2d start and end coordinates, and the PCM-sampled data + that is output during the sweep. + + Additional information is provided to allow decoding (and/or encoding) of a + composite colour feed. + + Otherwise helpful: the ScanTarget vends all allocated memory. That should allow + for use of shared memory where available. +*/ +struct ScanTarget { + + /* + This top section of the interface deals with modal settings. A ScanTarget can + assume that the modals change very infrequently. + */ + + struct Modals { + /*! + Enumerates the potential formats of input data. + */ + enum class DataType { + + // The luminance types can be used to feed only two video pipelines: + // black and white video, or composite colour. + + Luminance1, // 1 byte/pixel; any bit set => white; no bits set => black. + Luminance8, // 1 byte/pixel; linear scale. + + // The luminance plus phase types describe a luminance and the phase offset + // 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. + + // The RGB types can directly feed an RGB pipeline, naturally, or can be mapped + // to phase+luminance, or just to luminance. + + Red1Green1Blue1, // 1 byte/pixel; bit 0 is blue on or off, bit 1 is green, bit 2 is red. + Red2Green2Blue2, // 1 byte/pixel; bits 0 and 1 are blue, bits 2 and 3 are green, bits 4 and 5 are blue. + Red4Green4Blue4, // 2 bytes/pixel; first nibble is red, second is green, third is blue. + 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. + ColourSpace composite_colour_space; + }; + + /// Sets the total format of input data. + virtual void set_modals(Modals); + + + /* + This second section of the interface allows provision of the streamed data, plus some control + over the streaming. + */ + + /*! + Defines a scan in terms of its two endpoints. + */ + 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. + uint16_t x, y; + + /// Provides the offset, in samples, into the most recently allocated write area, of data + /// at this end point. + uint16_t data_offset; + + /// For composite video, provides the angle of the colour subcarrier at this endpoint. + /// + /// This is a slightly weird fixed point, being: + /// + /// * a six-bit fractional part; + /// * a nine-bit integral part; and + /// * a sign. + /// + /// Positive numbers indicate that the colour subcarrier is 'running positively' on this + /// line; i.e. it is any NTSC line or an appropriate swing PAL line, encoded as + /// x*cos(a) + y*sin(a). + /// + /// Negative numbers indicate a 'negative running' colour subcarrier; i.e. it is one of + /// the phase alternated lines of PAL, encoded as x*cos(a) - y*sin(a), or x*cos(-a) + y*sin(-a), + /// whichever you prefer. + /// + /// It will produce undefined behaviour if signs differ on a single scan. + int16_t composite_angle; + } 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. + 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(); + + /// 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. + /// + /// Calls will be paired off with calls to @c reduce_previous_allocation_to. + /// + /// @returns a pointer to the allocated space if any was available; @c nullptr otherwise. + virtual uint8_t *allocate_write_area(std::size_t required_length, std::size_t required_alignment = 1) = 0; + + /// Announces that the owner is finished with the region created by the most recent @c allocate_write_area + /// and indicates that its actual final size was @c actual_length. + virtual void reduce_previous_allocation_to(std::size_t actual_length) = 0; + + /// Announces that all endpoint pairs and write areas obtained since the last @c submit have now been + /// populated with appropriate data. + /// + /// 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; + + /// 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 + /// a faulty total output. + virtual void reset() = 0; + + /// Announces that any submitted data not yet output should be output now, but needn't block while + /// doing so. This generally communicates that processing is now otherwise 'up to date', so no + /// further delay should be allowed. + virtual void flush() = 0; + + + /* + ScanTargets also receive notification of certain events that may be helpful in processing, particularly + for synchronising internal output to the outside world. + */ + + enum class Event { + HorizontalRetrace, + VerticalRetrace + }; + + /// Provides a hint that the named event has occurred. + virtual void announce(Event event) = 0; +}; + +} +} + +#endif /* Outputs_CRT_ScanTarget_h */ From da4d8833215751bbc66e2f65ed01d21ca1672906 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 3 Nov 2018 19:58:44 -0400 Subject: [PATCH 003/208] Adds first, incomplete attempts to talk to a ScanTarget from the CRT. Does away with the hassle of `unsigned` while I'm here; that was a schoolboy error. --- Components/6560/6560.hpp | 28 +- Components/9918/9918.cpp | 20 +- Machines/AmstradCPC/AmstradCPC.cpp | 24 +- Machines/AppleII/Video.cpp | 12 +- Machines/AppleII/Video.hpp | 12 +- Machines/Atari2600/Atari2600.cpp | 12 +- Machines/Atari2600/TIA.cpp | 62 ++-- Machines/CRTMachine.hpp | 2 +- Machines/ColecoVision/ColecoVision.cpp | 2 +- Machines/Electron/Video.cpp | 36 +-- Machines/Electron/Video.hpp | 4 +- Machines/MasterSystem/MasterSystem.cpp | 2 +- Machines/Oric/Video.cpp | 36 +-- Machines/ZX8081/Video.cpp | 18 +- .../Clock Signal.xcodeproj/project.pbxproj | 2 + .../Mac/Clock Signal/Machine/CSMachine.mm | 6 +- Outputs/CRT/CRT.cpp | 279 ++++++------------ Outputs/CRT/CRT.hpp | 186 +++--------- Outputs/CRT/CRTTypes.hpp | 14 - Outputs/CRT/Internals/Flywheel.hpp | 40 +-- .../CRT/Internals/Shaders/OutputShader.hpp | 1 + Outputs/CRT/ScanTarget.hpp | 48 ++- 22 files changed, 335 insertions(+), 511 deletions(-) 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) {} }; } From b40211d2c0febe36656d9fb01d7b05db938c78d3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 3 Nov 2018 21:54:25 -0400 Subject: [PATCH 004/208] Starts to bend 'CRTMachine' to a world farther from owning the GPU relationship. --- .../Implementation/MultiCRTMachine.cpp | 17 ++------ .../Implementation/MultiCRTMachine.hpp | 4 +- ClockReceiver/ClockDeferrer.hpp | 1 + Components/6560/6560.hpp | 14 +++---- Components/9918/9918.cpp | 10 ++--- Machines/AmstradCPC/AmstradCPC.cpp | 37 ++++++++--------- Machines/AppleII/AppleII.cpp | 16 ++++---- Machines/AppleII/Video.cpp | 6 +-- Machines/Atari2600/Atari2600.cpp | 18 ++++----- Machines/Atari2600/TIA.cpp | 12 +++--- Machines/Atari2600/TIA.hpp | 5 ++- Machines/CRTMachine.hpp | 30 ++++++-------- Machines/ColecoVision/ColecoVision.cpp | 18 ++++----- Machines/Commodore/Vic-20/Vic20.cpp | 16 ++++---- Machines/Electron/Electron.cpp | 16 ++++---- Machines/Electron/Video.cpp | 2 +- Machines/Electron/Video.hpp | 2 + Machines/MSX/MSX.cpp | 16 ++++---- Machines/MasterSystem/MasterSystem.cpp | 18 ++++----- Machines/Oric/Oric.cpp | 20 +++++----- Machines/Oric/Video.cpp | 12 +++--- Machines/Oric/Video.hpp | 8 +++- Machines/ZX8081/Video.cpp | 8 ++-- Machines/ZX8081/ZX8081.cpp | 10 +---- .../Clock Signal.xcodeproj/project.pbxproj | 40 ++----------------- .../Mac/Clock Signal/Machine/CSMachine.mm | 15 ++++--- Outputs/CRT/CRT.cpp | 31 +++++++------- Outputs/CRT/CRT.hpp | 34 ++++++++++------ Outputs/CRT/CRTTypes.hpp | 29 -------------- Outputs/CRT/Internals/CRTOpenGL.hpp | 1 - .../CRT/Internals/Shaders/OutputShader.cpp | 2 +- .../CRT/Internals/Shaders/OutputShader.hpp | 3 +- Outputs/{CRT => }/ScanTarget.hpp | 25 +++++++++--- 33 files changed, 222 insertions(+), 274 deletions(-) delete mode 100644 Outputs/CRT/CRTTypes.hpp rename Outputs/{CRT => }/ScanTarget.hpp (94%) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp index 2f075a1d0..aed3e1721 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp @@ -53,24 +53,13 @@ void MultiCRTMachine::perform_serial(const std::functionsetup_output(aspect_ratio); + // TODO. +// machine->setup_output(aspect_ratio); }); } -void MultiCRTMachine::close_output() { - perform_serial([=](::CRTMachine::Machine *machine) { - machine->close_output(); - }); -} - -Outputs::CRT::CRT *MultiCRTMachine::get_crt() { - std::lock_guard machines_lock(machines_mutex_); - CRTMachine::Machine *crt_machine = machines_.front()->crt_machine(); - return crt_machine ? crt_machine->get_crt() : nullptr; -} - Outputs::Speaker::Speaker *MultiCRTMachine::get_speaker() { return speaker_; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp index 93906d9ab..17b13992c 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp @@ -53,9 +53,7 @@ class MultiCRTMachine: public CRTMachine::Machine { } // Below is the standard CRTMachine::Machine interface; see there for documentation. - void setup_output(float aspect_ratio) override; - void close_output() override; - Outputs::CRT::CRT *get_crt() override; + void setup_output(Outputs::Display::ScanTarget *scan_target) override; Outputs::Speaker::Speaker *get_speaker() override; void run_for(Time::Seconds duration) override; diff --git a/ClockReceiver/ClockDeferrer.hpp b/ClockReceiver/ClockDeferrer.hpp index 838739e4b..cc41cbdd0 100644 --- a/ClockReceiver/ClockDeferrer.hpp +++ b/ClockReceiver/ClockDeferrer.hpp @@ -9,6 +9,7 @@ #ifndef ClockDeferrer_h #define ClockDeferrer_h +#include #include /*! diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index cf0f3f718..38b082b03 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -64,7 +64,7 @@ template class MOS6560 { public: MOS6560(BusHandler &bus_handler) : bus_handler_(bus_handler), - crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::DisplayType::NTSC60, 2)), + crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::Display::Type::NTSC60, 2)), audio_generator_(audio_queue_), speaker_(audio_generator_) { @@ -80,7 +80,7 @@ template class MOS6560 { // "}"); // default to s-video output -// crt_->set_video_signal(Outputs::CRT::VideoSignal::SVideo); +// crt_->set_video_signal(Outputs::Display::VideoSignal::SVideo); // default to NTSC set_output_mode(OutputMode::NTSC); @@ -131,12 +131,12 @@ template class MOS6560 { 103, 42, 80, 16, }; const uint8_t *chrominances; - Outputs::CRT::DisplayType display_type; + Outputs::Display::Type display_type; switch(output_mode) { default: chrominances = pal_chrominances; - display_type = Outputs::CRT::DisplayType::PAL50; + display_type = Outputs::Display::Type::PAL50; timing_.cycles_per_line = 71; timing_.line_counter_increment_offset = 4; timing_.final_line_increment_position = timing_.cycles_per_line - timing_.line_counter_increment_offset; @@ -146,7 +146,7 @@ template class MOS6560 { case OutputMode::NTSC: chrominances = ntsc_chrominances; - display_type = Outputs::CRT::DisplayType::NTSC60; + display_type = Outputs::Display::Type::NTSC60; timing_.cycles_per_line = 65; timing_.line_counter_increment_offset = 40; timing_.final_line_increment_position = 58; @@ -159,10 +159,10 @@ template class MOS6560 { switch(output_mode) { case OutputMode::PAL: - crt_->set_visible_area(Outputs::CRT::Rect(0.1f, 0.07f, 0.9f, 0.9f)); + crt_->set_visible_area(Outputs::Display::Rect(0.1f, 0.07f, 0.9f, 0.9f)); break; case OutputMode::NTSC: - crt_->set_visible_area(Outputs::CRT::Rect(0.05f, 0.05f, 0.9f, 0.9f)); + crt_->set_visible_area(Outputs::Display::Rect(0.05f, 0.05f, 0.9f, 0.9f)); break; } diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index 47501d994..d043b1913 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -50,7 +50,7 @@ struct ReverseTable { Base::Base(Personality p) : personality_(p), - crt_(new Outputs::CRT::CRT(CRTCyclesPerLine, CRTCyclesDivider, Outputs::CRT::DisplayType::NTSC60, 4)) { + crt_(new Outputs::CRT::CRT(CRTCyclesPerLine, CRTCyclesDivider, Outputs::Display::Type::NTSC60, 4)) { switch(p) { case TI::TMS::TMS9918A: @@ -91,8 +91,8 @@ TMS9918::TMS9918(Personality p): // "{" // "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)); +// crt_->set_video_signal(Outputs::Display::VideoSignal::RGB); + crt_->set_visible_area(Outputs::Display::Rect(0.055f, 0.025f, 0.9f, 0.9f)); // The TMS remains in-phase with the NTSC colour clock; this is an empirical measurement // intended to produce the correct relationship between the hard edges between pixels and @@ -107,12 +107,12 @@ void TMS9918::set_tv_standard(TVStandard standard) { case TVStandard::PAL: mode_timing_.total_lines = 313; mode_timing_.first_vsync_line = 253; - crt_->set_new_display_type(CRTCyclesPerLine, Outputs::CRT::DisplayType::PAL50); + crt_->set_new_display_type(CRTCyclesPerLine, Outputs::Display::Type::PAL50); break; default: mode_timing_.total_lines = 262; mode_timing_.first_vsync_line = 227; - crt_->set_new_display_type(CRTCyclesPerLine, Outputs::CRT::DisplayType::NTSC60); + crt_->set_new_display_type(CRTCyclesPerLine, Outputs::Display::Type::NTSC60); break; } } diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 71d3940ee..db547f6ef 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -30,6 +30,7 @@ #include "../../ClockReceiver/ForceInline.hpp" #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" +#include "../../Outputs/CRT/CRT.hpp" #include "../../Analyser/Static/AmstradCPC/Target.hpp" @@ -324,27 +325,27 @@ 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)); + void setup_output(Outputs::Display::ScanTarget *scan_target) { + crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::Display::Type::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_visible_area(Outputs::CRT::Rect(0.1072f, 0.1f, 0.842105263157895f, 0.842105263157895f)); -// crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB); + crt_->set_visible_area(Outputs::Display::Rect(0.1072f, 0.1f, 0.842105263157895f, 0.842105263157895f)); +// crt_->set_video_signal(Outputs::Display::VideoSignal::RGB); } /// Destructs the CRT. - void close_output() { - crt_.reset(); - } +// void close_output() { +// crt_.reset(); +// } /// @returns the CRT. - Outputs::CRT::CRT *get_crt() { - return crt_.get(); - } +// Outputs::CRT::CRT *get_crt() { +// return crt_.get(); +// } /*! Sets the next video mode. Per the documentation, mode changes take effect only at the end of line, @@ -982,19 +983,19 @@ template class ConcreteMachine: } /// A CRTMachine function; indicates that outputs should be created now. - void setup_output(float aspect_ratio) override final { - crtc_bus_handler_.setup_output(aspect_ratio); + void setup_output(Outputs::Display::ScanTarget *scan_target) override final { + crtc_bus_handler_.setup_output(scan_target); } /// A CRTMachine function; indicates that outputs should be destroyed now. - void close_output() override final { - crtc_bus_handler_.close_output(); - } +// void close_output() override final { +// crtc_bus_handler_.close_output(); +// } /// @returns the CRT in use. - Outputs::CRT::CRT *get_crt() override final { - return crtc_bus_handler_.get_crt(); - } +// Outputs::CRT::CRT *get_crt() override final { +// return crtc_bus_handler_.get_crt(); +// } /// @returns the speaker in use. Outputs::Speaker::Speaker *get_speaker() override final { diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 63371a316..56401a336 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -396,18 +396,18 @@ template class ConcreteMachine: audio_queue_.flush(); } - void setup_output(float aspect_ratio) override { + void setup_output(Outputs::Display::ScanTarget *scan_target) override { video_.reset(new AppleII::Video::Video(video_bus_handler_)); video_->set_character_rom(character_rom_); } - void close_output() override { - video_.reset(); - } - - Outputs::CRT::CRT *get_crt() override { - return video_->get_crt(); - } +// void close_output() override { +// video_.reset(); +// } +// +// Outputs::CRT::CRT *get_crt() override { +// return video_->get_crt(); +// } Outputs::Speaker::Speaker *get_speaker() override { return &speaker_; diff --git a/Machines/AppleII/Video.cpp b/Machines/AppleII/Video.cpp index 921427fb2..d7ce90d60 100644 --- a/Machines/AppleII/Video.cpp +++ b/Machines/AppleII/Video.cpp @@ -11,7 +11,7 @@ using namespace AppleII::Video; VideoBase::VideoBase(bool is_iie, std::function &&target) : - crt_(new Outputs::CRT::CRT(910, 1, Outputs::CRT::DisplayType::NTSC60, 1)), + crt_(new Outputs::CRT::CRT(910, 1, Outputs::Display::Type::NTSC60, 1)), is_iie_(is_iie), deferrer_(std::move(target)) { @@ -24,8 +24,8 @@ VideoBase::VideoBase(bool is_iie, std::function &&target) : // "}"); // Show only the centre 75% of the TV frame. -// 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_video_signal(Outputs::Display::VideoSignal::Composite); + crt_->set_visible_area(Outputs::Display::Rect(0.118f, 0.122f, 0.77f, 0.77f)); crt_->set_immediate_default_phase(0.0f); character_zones[0].xor_mask = 0; diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 09dda70d2..f0f7baf46 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -123,7 +123,7 @@ class ConcreteMachine: } ~ConcreteMachine() { - close_output(); +// close_output(); } std::vector> &get_joysticks() override { @@ -157,19 +157,19 @@ class ConcreteMachine: } // to satisfy CRTMachine::Machine - void setup_output(float aspect_ratio) override { + void setup_output(Outputs::Display::ScanTarget *scan_target) override { bus_->tia_.reset(new TIA); bus_->speaker_.set_input_rate(static_cast(get_clock_rate() / static_cast(CPUTicksPerAudioTick))); bus_->tia_->get_crt()->set_delegate(this); } - void close_output() override { - bus_.reset(); - } - - Outputs::CRT::CRT *get_crt() override { - return bus_->tia_->get_crt(); - } +// void close_output() override { +// bus_.reset(); +// } +// +// Outputs::CRT::CRT *get_crt() override { +// return bus_->tia_->get_crt(); +// } Outputs::Speaker::Speaker *get_speaker() override { return &bus_->speaker_; diff --git a/Machines/Atari2600/TIA.cpp b/Machines/Atari2600/TIA.cpp index 89861c84f..d748b482b 100644 --- a/Machines/Atari2600/TIA.cpp +++ b/Machines/Atari2600/TIA.cpp @@ -24,8 +24,8 @@ 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_.reset(new Outputs::CRT::CRT(cycles_per_line * 2 - 1, 1, Outputs::Display::Type::NTSC60, 1)); +// crt_->set_video_signal(Outputs::Display::VideoSignal::Composite); set_output_mode(OutputMode::NTSC); } @@ -120,7 +120,7 @@ TIA::TIA(std::function line_end_function) : TIA(fa } void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) { - Outputs::CRT::DisplayType display_type; + Outputs::Display::Type display_type; if(output_mode == OutputMode::NTSC) { // crt_->set_svideo_sampling_function( @@ -133,7 +133,7 @@ void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) { // "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; + display_type = Outputs::Display::Type::NTSC60; } else { // crt_->set_svideo_sampling_function( // "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)" @@ -147,9 +147,9 @@ void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) { // "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; + display_type = Outputs::Display::Type::PAL50; } -// crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); +// crt_->set_video_signal(Outputs::Display::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 diff --git a/Machines/Atari2600/TIA.hpp b/Machines/Atari2600/TIA.hpp index a860b0dd1..7657b0821 100644 --- a/Machines/Atari2600/TIA.hpp +++ b/Machines/Atari2600/TIA.hpp @@ -9,9 +9,12 @@ #ifndef TIA_hpp #define TIA_hpp +#include #include +#include -#include "../CRTMachine.hpp" +#include "../../Outputs/CRT/CRT.hpp" +#include "../../ClockReceiver/ClockReceiver.hpp" namespace Atari2600 { diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp index 956b26f33..42197f975 100644 --- a/Machines/CRTMachine.hpp +++ b/Machines/CRTMachine.hpp @@ -9,7 +9,7 @@ #ifndef CRTMachine_hpp #define CRTMachine_hpp -#include "../Outputs/CRT/CRT.hpp" +#include "../Outputs/ScanTarget.hpp" #include "../Outputs/Speaker/Speaker.hpp" #include "../ClockReceiver/ClockReceiver.hpp" #include "../ClockReceiver/TimeTypes.hpp" @@ -19,6 +19,7 @@ #include +// TODO: rename. namespace CRTMachine { /*! @@ -29,19 +30,12 @@ namespace CRTMachine { class Machine { public: /*! - Causes the machine to set up its CRT and, if it has one, speaker. The caller guarantees - that an OpenGL context is bound. - */ - virtual void setup_output(float aspect_ratio) = 0; + Causes the machine to set up its display and, if it has one, speaker. - /*! - Gives the machine a chance to release all owned resources. The caller guarantees that the - OpenGL context is bound. + The @c scan_target will receive all video output; the caller guarantees + that it is non-null. */ - virtual void close_output() = 0; - - /// @returns The CRT this machine is drawing to. Should not be @c nullptr. - virtual Outputs::CRT::CRT *get_crt() = 0; + virtual void setup_output(Outputs::Display::ScanTarget *scan_target) = 0; /// @returns The speaker that receives this machine's output, or @c nullptr if this machine is mute. virtual Outputs::Speaker::Speaker *get_speaker() = 0; @@ -68,21 +62,21 @@ class Machine { } /*! - Maps from Configurable::Display to Outputs::CRT::VideoSignal and calls + Maps from Configurable::Display to Outputs::Display::VideoSignal and calls @c set_video_signal with the result. */ void set_video_signal_configurable(Configurable::Display type) { - Outputs::CRT::VideoSignal signal; + Outputs::Display::VideoSignal signal; switch(type) { default: case Configurable::Display::RGB: - signal = Outputs::CRT::VideoSignal::RGB; + signal = Outputs::Display::VideoSignal::RGB; break; case Configurable::Display::SVideo: - signal = Outputs::CRT::VideoSignal::SVideo; + signal = Outputs::Display::VideoSignal::SVideo; break; case Configurable::Display::Composite: - signal = Outputs::CRT::VideoSignal::Composite; + signal = Outputs::Display::VideoSignal::Composite; break; } set_video_signal(signal); @@ -91,7 +85,7 @@ class Machine { /*! Forwards the video signal to the CRT returned by get_crt(). */ - virtual void set_video_signal(Outputs::CRT::VideoSignal video_signal) { + virtual void set_video_signal(Outputs::Display::VideoSignal video_signal) { // get_crt()->set_video_signal(video_signal); } diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 0c2794d38..b2840a76e 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -169,18 +169,18 @@ class ConcreteMachine: return joysticks_; } - void setup_output(float aspect_ratio) override { + void setup_output(Outputs::Display::ScanTarget *scan_target) 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::Display::VideoSignal::Composite); } - void close_output() override { - vdp_.reset(); - } - - Outputs::CRT::CRT *get_crt() override { - return vdp_->get_crt(); - } +// void close_output() override { +// vdp_.reset(); +// } +// +// Outputs::CRT::CRT *get_crt() override { +// return vdp_->get_crt(); +// } Outputs::Speaker::Speaker *get_speaker() override { return &speaker_; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 9a28bb588..ec415437b 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -620,20 +620,20 @@ class ConcreteMachine: m6502_.run_for(cycles); } - void setup_output(float aspect_ratio) override final { + void setup_output(Outputs::Display::ScanTarget *scan_target) override final { mos6560_.reset(new MOS::MOS6560::MOS6560(mos6560_bus_handler_)); mos6560_->set_high_frequency_cutoff(1600); // There is a 1.6Khz low-pass filter in the Vic-20. mos6560_->set_output_mode(output_mode_); mos6560_->set_clock_rate(get_clock_rate()); } - void close_output() override final { - mos6560_ = nullptr; - } - - Outputs::CRT::CRT *get_crt() override final { - return mos6560_->get_crt(); - } +// void close_output() override final { +// mos6560_ = nullptr; +// } +// +// Outputs::CRT::CRT *get_crt() override final { +// return mos6560_->get_crt(); +// } Outputs::Speaker::Speaker *get_speaker() override final { return mos6560_->get_speaker(); diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 74dc06721..2205212f9 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -373,17 +373,17 @@ class ConcreteMachine: audio_queue_.perform(); } - void setup_output(float aspect_ratio) override final { + void setup_output(Outputs::Display::ScanTarget *scan_target) override final { video_output_.reset(new VideoOutput(ram_)); } - void close_output() override final { - video_output_.reset(); - } - - Outputs::CRT::CRT *get_crt() override final { - return video_output_->get_crt(); - } +// void close_output() override final { +// video_output_.reset(); +// } +// +// Outputs::CRT::CRT *get_crt() override final { +// return video_output_->get_crt(); +// } Outputs::Speaker::Speaker *get_speaker() override final { return &speaker_; diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index f57bad88b..178e30094 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -43,7 +43,7 @@ VideoOutput::VideoOutput(uint8_t *memory) : ram_(memory) { setup_screen_map(); setup_base_address(); - crt_.reset(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::CRT::DisplayType::PAL50, 1)); + crt_.reset(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::Display::Type::PAL50, 1)); // crt_->set_rgb_sampling_function( // "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" // "{" diff --git a/Machines/Electron/Video.hpp b/Machines/Electron/Video.hpp index 232440281..d5df17ff0 100644 --- a/Machines/Electron/Video.hpp +++ b/Machines/Electron/Video.hpp @@ -13,6 +13,8 @@ #include "../../ClockReceiver/ClockReceiver.hpp" #include "Interrupts.hpp" +#include + namespace Electron { /*! diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index d7a34cf3a..dcc06db5e 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -218,17 +218,17 @@ class ConcreteMachine: audio_queue_.flush(); } - void setup_output(float aspect_ratio) override { + void setup_output(Outputs::Display::ScanTarget *scan_target) override { vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A)); } - void close_output() override { - vdp_.reset(); - } - - Outputs::CRT::CRT *get_crt() override { - return vdp_->get_crt(); - } +// void close_output() override { +// vdp_.reset(); +// } +// +// Outputs::CRT::CRT *get_crt() override { +// return vdp_->get_crt(); +// } Outputs::Speaker::Speaker *get_speaker() override { return &speaker_; diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 46bd480c7..8d2c587f1 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -161,7 +161,7 @@ class ConcreteMachine: audio_queue_.flush(); } - void setup_output(float aspect_ratio) override { + void setup_output(Outputs::Display::ScanTarget *scan_target) override { TI::TMS::Personality personality = TI::TMS::TMS9918A; switch(model_) { case Target::Model::SG1000: personality = TI::TMS::TMS9918A; break; @@ -172,18 +172,18 @@ 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::Display::VideoSignal::Composite); time_until_debounce_ = vdp_->get_time_until_line(-1); } - void close_output() override { - vdp_.reset(); - } - - Outputs::CRT::CRT *get_crt() override { - return vdp_->get_crt(); - } +// void close_output() override { +// vdp_.reset(); +// } +// +// Outputs::CRT::CRT *get_crt() override { +// return vdp_->get_crt(); +// } Outputs::Speaker::Speaker *get_speaker() override { return &speaker_; diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 1527973cb..aca16adc9 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -456,21 +456,21 @@ template class Co } // to satisfy CRTMachine::Machine - void setup_output(float aspect_ratio) override final { + void setup_output(Outputs::Display::ScanTarget *scan_target) override final { speaker_.set_input_rate(1000000.0f); video_output_.reset(new VideoOutput(ram_)); if(!colour_rom_.empty()) video_output_->set_colour_rom(colour_rom_); - set_video_signal(Outputs::CRT::VideoSignal::RGB); + set_video_signal(Outputs::Display::VideoSignal::RGB); } - void close_output() override final { - video_output_.reset(); - } - - Outputs::CRT::CRT *get_crt() override final { - return video_output_->get_crt(); - } +// void close_output() override final { +// video_output_.reset(); +// } +// +// Outputs::CRT::CRT *get_crt() override final { +// return video_output_->get_crt(); +// } Outputs::Speaker::Speaker *get_speaker() override final { return &speaker_; @@ -537,7 +537,7 @@ template class Co } } - void set_video_signal(Outputs::CRT::VideoSignal video_signal) override { + void set_video_signal(Outputs::Display::VideoSignal video_signal) override { video_output_->set_video_signal(video_signal); } diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index 113c05271..e0963be47 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -8,6 +8,8 @@ #include "Video.hpp" +#include + using namespace Oric; namespace { @@ -21,7 +23,7 @@ namespace { VideoOutput::VideoOutput(uint8_t *memory) : ram_(memory), - crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::CRT::DisplayType::PAL50, 2)), + crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::Display::Type::PAL50, 2)), v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition), counter_period_(PAL50Period) { // crt_->set_rgb_sampling_function( @@ -41,11 +43,11 @@ VideoOutput::VideoOutput(uint8_t *memory) : // ); crt_->set_composite_function_type(Outputs::CRT::CRT::CompositeSourceType::DiscreteFourSamplesPerCycle, 0.0f); - set_video_signal(Outputs::CRT::VideoSignal::Composite); + set_video_signal(Outputs::Display::VideoSignal::Composite); crt_->set_visible_area(crt_->get_rect_for_area(54, 224, 16 * 6, 40 * 6, 4.0f / 3.0f)); } -void VideoOutput::set_video_signal(Outputs::CRT::VideoSignal video_signal) { +void VideoOutput::set_video_signal(Outputs::Display::VideoSignal video_signal) { video_signal_ = video_signal; // crt_->set_video_signal(video_signal); } @@ -129,7 +131,7 @@ void VideoOutput::run_for(const Cycles cycles) { if(control_byte & 0x60) { if(pixel_target_) { uint16_t colours[2]; - if(video_signal_ == Outputs::CRT::VideoSignal::RGB) { + if(video_signal_ == Outputs::Display::VideoSignal::RGB) { colours[0] = static_cast(paper_ ^ inverse_mask); colours[1] = static_cast(ink_ ^ inverse_mask); } else { @@ -183,7 +185,7 @@ void VideoOutput::run_for(const Cycles cycles) { pixel_target_[0] = pixel_target_[1] = pixel_target_[2] = pixel_target_[3] = pixel_target_[4] = pixel_target_[5] = - (video_signal_ == Outputs::CRT::VideoSignal::RGB) ? paper_ ^ inverse_mask : colour_forms_[paper_ ^ inverse_mask]; + (video_signal_ == Outputs::Display::VideoSignal::RGB) ? paper_ ^ inverse_mask : colour_forms_[paper_ ^ inverse_mask]; } } if(pixel_target_) pixel_target_ += 6; diff --git a/Machines/Oric/Video.hpp b/Machines/Oric/Video.hpp index e65a54fc0..4eb937f0f 100644 --- a/Machines/Oric/Video.hpp +++ b/Machines/Oric/Video.hpp @@ -12,6 +12,10 @@ #include "../../Outputs/CRT/CRT.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" +#include +#include +#include + namespace Oric { class VideoOutput { @@ -20,7 +24,7 @@ class VideoOutput { Outputs::CRT::CRT *get_crt(); void run_for(const Cycles cycles); void set_colour_rom(const std::vector &rom); - void set_video_signal(Outputs::CRT::VideoSignal output_device); + void set_video_signal(Outputs::Display::VideoSignal output_device); private: uint8_t *ram_; @@ -33,7 +37,7 @@ class VideoOutput { // Output target and device uint16_t *pixel_target_; uint16_t colour_forms_[8]; - Outputs::CRT::VideoSignal video_signal_; + Outputs::Display::VideoSignal video_signal_; // Registers uint8_t ink_, paper_; diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index 26445c026..a2b763b61 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -8,6 +8,8 @@ #include "Video.hpp" +#include + using namespace ZX8081; namespace { @@ -21,7 +23,7 @@ const std::size_t StandardAllocationSize = 320; } Video::Video() : - crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::CRT::DisplayType::PAL50, 1)) { + crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::Display::Type::PAL50, 1)) { // 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. @@ -32,8 +34,8 @@ Video::Video() : // "}"); // Show only the centre 80% of the TV frame. -// crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); - crt_->set_visible_area(Outputs::CRT::Rect(0.1f, 0.1f, 0.8f, 0.8f)); +// crt_->set_video_signal(Outputs::Display::VideoSignal::Composite); + crt_->set_visible_area(Outputs::Display::Rect(0.1f, 0.1f, 0.8f, 0.8f)); } void Video::run_for(const HalfCycles half_cycles) { diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index dfc59bb3d..49b1053f1 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -310,18 +310,10 @@ template class ConcreteMachine: } } - void setup_output(float aspect_ratio) override final { + void setup_output(Outputs::Display::ScanTarget *scan_target) override final { video_.reset(new Video); } - void close_output() override final { - video_.reset(); - } - - Outputs::CRT::CRT *get_crt() override final { - return video_->get_crt(); - } - Outputs::Speaker::Speaker *get_speaker() override final { return is_zx81 ? &speaker_ : nullptr; } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index d0942d194..9e4732ad8 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -94,13 +94,6 @@ 4B055ADE1FAE9B4C0060FFFF /* 6522Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B83348B1F5DB99C0097E338 /* 6522Base.cpp */; }; 4B055ADF1FAE9B4C0060FFFF /* IRQDelegatePortHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334891F5DB94B0097E338 /* IRQDelegatePortHandler.cpp */; }; 4B055AE01FAE9B660060FFFF /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; }; - 4B055AE11FAE9B6F0060FFFF /* ArrayBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */; }; - 4B055AE21FAE9B6F0060FFFF /* CRTOpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */; }; - 4B055AE31FAE9B6F0060FFFF /* TextureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99081C8FBA6F0075DAFB /* TextureBuilder.cpp */; }; - 4B055AE41FAE9B6F0060FFFF /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; }; - 4B055AE51FAE9B6F0060FFFF /* IntermediateShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */; }; - 4B055AE61FAE9B6F0060FFFF /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; }; - 4B055AE71FAE9B6F0060FFFF /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; }; 4B055AE81FAE9B7B0060FFFF /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; }; 4B055AE91FAE9B990060FFFF /* 6502Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */; }; 4B055AEA1FAE9B990060FFFF /* 6502Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334851F5DA3780097E338 /* 6502Storage.cpp */; }; @@ -203,7 +196,6 @@ 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; }; 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; }; - 4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */; }; 4B50730A1DDFCFDF00C48FBD /* ArrayBuilderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */; }; 4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; }; 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; }; @@ -600,29 +592,21 @@ 4BB73EAC1B587A5100552FC2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BB73EAA1B587A5100552FC2 /* MainMenu.xib */; }; 4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */; }; 4BB73EC21B587A5100552FC2 /* Clock_SignalUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */; }; - 4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */; }; 4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; }; 4BBB70A5202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; }; 4BBB70A8202014E2002FE009 /* MultiCRTMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */; }; 4BBB70A9202014E2002FE009 /* MultiCRTMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */; }; 4BBC951E1F368D83008F4C34 /* i8272.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBC951C1F368D83008F4C34 /* i8272.cpp */; }; 4BBF49AF1ED2880200AB3669 /* FUSETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */; }; - 4BBF99141C8FBA6F0075DAFB /* TextureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99081C8FBA6F0075DAFB /* TextureBuilder.cpp */; }; - 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */; }; - 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; }; 4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */; }; 4BBFE83D21015D9C00BF1C40 /* CSJoystickManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFE83C21015D9C00BF1C40 /* CSJoystickManager.m */; }; 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFFEE51F7B27F1005F3FEB /* TrackSerialiser.cpp */; }; 4BC39568208EE6CF0044766B /* DiskIICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC39566208EE6CF0044766B /* DiskIICard.cpp */; }; 4BC39569208EE6CF0044766B /* DiskIICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC39566208EE6CF0044766B /* DiskIICard.cpp */; }; - 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; }; - 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; }; 4BC5FC3020CDDDEF00410AA0 /* AppleIIOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */; }; 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; }; 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; }; 4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; }; - 4BC891AD20F6EAB300EDE5B3 /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC891AB20F6EAB300EDE5B3 /* Rectangle.cpp */; }; - 4BC891AE20F6EAB300EDE5B3 /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC891AB20F6EAB300EDE5B3 /* Rectangle.cpp */; }; 4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */; }; 4BC9DF451D044FCA00F44158 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; }; 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9DF4D1D04691600F44158 /* 6560.cpp */; }; @@ -718,7 +702,6 @@ 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 = ""; }; @@ -1358,7 +1341,6 @@ 4BBF990F1C8FBA6F0075DAFB /* OpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OpenGL.hpp; sourceTree = ""; }; 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureTarget.cpp; sourceTree = ""; }; 4BBF99131C8FBA6F0075DAFB /* TextureTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureTarget.hpp; sourceTree = ""; }; - 4BBF99191C8FC2750075DAFB /* CRTTypes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CRTTypes.hpp; sourceTree = ""; }; 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Parsers/ZX8081.cpp; sourceTree = ""; }; 4BBFBB6B1EE8401E00C01E7A /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Parsers/ZX8081.hpp; sourceTree = ""; }; 4BBFE83C21015D9C00BF1C40 /* CSJoystickManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSJoystickManager.m; sourceTree = ""; }; @@ -1455,6 +1437,7 @@ 4BF437ED209D0F7E008CBD6B /* SegmentParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentParser.hpp; sourceTree = ""; }; 4BF437F0209D112F008CBD6B /* Sector.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sector.hpp; sourceTree = ""; }; 4BF4A2D91F534DB300B171F4 /* TargetPlatforms.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TargetPlatforms.hpp; sourceTree = ""; }; + 4BF52672218E752E00313227 /* ScanTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ScanTarget.hpp; path = ../../Outputs/ScanTarget.hpp; sourceTree = ""; }; 4BF6606A1F281573002CB053 /* ClockReceiver.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClockReceiver.hpp; sourceTree = ""; }; 4BF8295F1D8F3C87001BAE39 /* CRC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRC.hpp; path = ../../NumberTheory/CRC.hpp; sourceTree = ""; }; 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AllRAMProcessor.cpp; sourceTree = ""; }; @@ -1528,10 +1511,8 @@ 4B0CCC411C62D0B3001CAC5F /* CRT */ = { isa = PBXGroup; children = ( - 4B0CD7252189117C00665042 /* ScanTarget.hpp */, 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */, 4B0CCC431C62D0B3001CAC5F /* CRT.hpp */, - 4BBF99191C8FC2750075DAFB /* CRTTypes.hpp */, 4BBF99071C8FBA6F0075DAFB /* Internals */, ); name = CRT; @@ -1793,9 +1774,10 @@ 4B366DFD1B5C165F0026627B /* Outputs */ = { isa = PBXGroup; children = ( + 4BD601A920D89F2A00CBCE57 /* Log.hpp */, + 4BF52672218E752E00313227 /* ScanTarget.hpp */, 4B0CCC411C62D0B3001CAC5F /* CRT */, 4BD060A41FE49D3C006E14BE /* Speaker */, - 4BD601A920D89F2A00CBCE57 /* Log.hpp */, ); name = Outputs; sourceTree = ""; @@ -3657,11 +3639,9 @@ 4B055A9A1FAE85CB0060FFFF /* MFMDiskController.cpp in Sources */, 4B055ACB1FAE9AFB0060FFFF /* SerialBus.cpp in Sources */, 4B055AA41FAE85E50060FFFF /* DigitalPhaseLockedLoop.cpp in Sources */, - 4B055AE61FAE9B6F0060FFFF /* OutputShader.cpp in Sources */, 4B055A9B1FAE85DA0060FFFF /* AcornADF.cpp in Sources */, 4B0E04F11FC9EA9500F43484 /* MSX.cpp in Sources */, 4B055AD51FAE9B0B0060FFFF /* Video.cpp in Sources */, - 4B055AE11FAE9B6F0060FFFF /* ArrayBuilder.cpp in Sources */, 4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B7F188F2154825E00388727 /* MasterSystem.cpp in Sources */, 4B055AA51FAE85EF0060FFFF /* Encoder.cpp in Sources */, @@ -3675,10 +3655,8 @@ 4B055AD81FAE9B180060FFFF /* Video.cpp in Sources */, 4B89452F201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B894531201967B4007DE474 /* StaticAnalyser.cpp in Sources */, - 4BC891AE20F6EAB300EDE5B3 /* Rectangle.cpp in Sources */, 4B894539201967B4007DE474 /* Tape.cpp in Sources */, 4B7F1898215486A200388727 /* StaticAnalyser.cpp in Sources */, - 4B055AE51FAE9B6F0060FFFF /* IntermediateShader.cpp in Sources */, 4B15A9FD208249BB005E6C8D /* StaticAnalyser.cpp in Sources */, 4B055AD31FAE9B0B0060FFFF /* Microdisc.cpp in Sources */, 4B055AB41FAE860F0060FFFF /* OricTAP.cpp in Sources */, @@ -3702,7 +3680,6 @@ 4B89452B201967B4007DE474 /* File.cpp in Sources */, 4B055A981FAE85C50060FFFF /* Drive.cpp in Sources */, 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */, - 4B055AE21FAE9B6F0060FFFF /* CRTOpenGL.cpp in Sources */, 4B055AC31FAE9AE80060FFFF /* AmstradCPC.cpp in Sources */, 4B055A9E1FAE85DA0060FFFF /* G64.cpp in Sources */, 4B055AB81FAE860F0060FFFF /* ZX80O81P.cpp in Sources */, @@ -3723,7 +3700,6 @@ 4B055ADD1FAE9B460060FFFF /* i8272.cpp in Sources */, 4B055AC51FAE9AEE0060FFFF /* Atari2600.cpp in Sources */, 4B055A9C1FAE85DA0060FFFF /* CPCDSK.cpp in Sources */, - 4B055AE41FAE9B6F0060FFFF /* TextureTarget.cpp in Sources */, 4B055ABA1FAE86170060FFFF /* Commodore.cpp in Sources */, 4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */, 4B055AA61FAE85EF0060FFFF /* Parser.cpp in Sources */, @@ -3785,12 +3761,10 @@ 4B894537201967B4007DE474 /* Z80.cpp in Sources */, 4B055A9F1FAE85DA0060FFFF /* HFE.cpp in Sources */, 4B07835B1FC11D42001D12BB /* Configurable.cpp in Sources */, - 4B055AE71FAE9B6F0060FFFF /* Shader.cpp in Sources */, 4B894523201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B055AEC1FAE9BA20060FFFF /* Z80Base.cpp in Sources */, 4B0F94FF208C1A1600FE41D9 /* NIB.cpp in Sources */, 4B0E04EB1FC9E78800F43484 /* CAS.cpp in Sources */, - 4B055AE31FAE9B6F0060FFFF /* TextureBuilder.cpp in Sources */, 4BB0A65D2045009000FB3688 /* ColecoVision.cpp in Sources */, 4BB0A65C2044FD3000FB3688 /* SN76489.cpp in Sources */, 4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */, @@ -3823,7 +3797,6 @@ 4B58601E1F806AB200AEE2E3 /* MFMSectorDump.cpp in Sources */, 4B448E841F1C4C480009ABD6 /* PulseQueuedTape.cpp in Sources */, 4B0E61071FF34737002A9DBD /* MSX.cpp in Sources */, - 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B4518A01F75FD1C00926311 /* CPCDSK.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B322E041F5A2E3C004EB04C /* Z80Base.cpp in Sources */, @@ -3852,14 +3825,12 @@ 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */, - 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */, 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */, 4B894518201967B4007DE474 /* ConfidenceCounter.cpp in Sources */, 4B89452E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */, 4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */, 4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */, - 4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */, 4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */, 4BB0A65B2044FD3000FB3688 /* SN76489.cpp in Sources */, 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */, @@ -3867,7 +3838,6 @@ 4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */, 4BBB70A8202014E2002FE009 /* MultiCRTMachine.cpp in Sources */, 4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */, - 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */, 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, 4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */, 4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */, @@ -3903,9 +3873,7 @@ 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */, 4B7F1897215486A200388727 /* StaticAnalyser.cpp in Sources */, 4BD3A30B1EE755C800B5B501 /* Video.cpp in Sources */, - 4BBF99141C8FBA6F0075DAFB /* TextureBuilder.cpp in Sources */, 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */, - 4BC891AD20F6EAB300EDE5B3 /* Rectangle.cpp in Sources */, 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */, 4B1497881EE4A1DA00CE2596 /* ZX80O81P.cpp in Sources */, 4B894520201967B4007DE474 /* StaticAnalyser.cpp in Sources */, @@ -3951,8 +3919,6 @@ 4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */, 4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */, 4BFDD78C1F7F2DB4008579B9 /* ImplicitSectors.cpp in Sources */, - 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */, - 4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */, 4B894526201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */, 4B8805FB1DCFF807003085B1 /* Oric.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index de86c6019..497703324 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -26,6 +26,9 @@ #include +#import +#include + @interface CSMachine() - (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; - (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker; @@ -131,11 +134,11 @@ struct ActivityObserver: public Activity::Observer { _speakerDelegate.machine = nil; [_delegateMachineAccessLock unlock]; - [_view performWithGLContext:^{ - @synchronized(self) { - self->_machine->crt_machine()->close_output(); - } - }]; +// [_view performWithGLContext:^{ +// @synchronized(self) { +// self->_machine->crt_machine()->close_output(); +// } +// }]; } - (float)idealSamplingRateFromRange:(NSRange)range { @@ -228,7 +231,7 @@ struct ActivityObserver: public Activity::Observer { } - (void)setupOutputWithAspectRatio:(float)aspectRatio { - _machine->crt_machine()->setup_output(aspectRatio); +// _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); diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 9e0386a4a..5ecb4ef1e 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -7,7 +7,7 @@ // #include "CRT.hpp" -#include "Internals/CRTOpenGL.hpp" + #include #include #include @@ -15,7 +15,7 @@ using namespace Outputs::CRT; -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) { +void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::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 int millisecondsHorizontalRetraceTime = 7; // source: Dictionary of Video and Television Technology, p. 234 @@ -28,7 +28,8 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, ColourSpace // 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_ = IntermediateBufferWidth / cycles_per_line; + time_multiplier_ = 2048 / cycles_per_line; // TODO phase_denominator_ = cycles_per_line * colour_cycle_denominator * time_multiplier_; phase_numerator_ = 0; colour_cycle_numerator_ = colour_cycle_numerator; @@ -63,15 +64,15 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, ColourSpace // TODO: set scan_target modals. } -void CRT::set_new_display_type(int cycles_per_line, DisplayType displayType) { +void CRT::set_new_display_type(int cycles_per_line, Outputs::Display::Type 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 + case Outputs::Display::Type::PAL50: + 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 DisplayType::NTSC60: - set_new_timing(cycles_per_line, 262, ColourSpace::YIQ, 455, 2, 6, false); // i.e. 227.5, 3 lines = vertical sync + case Outputs::Display::Type::NTSC60: + 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; } @@ -96,7 +97,7 @@ CRT::CRT(int common_output_divisor, int buffer_depth) : CRT::CRT( int cycles_per_line, int common_output_divisor, int height_of_display, - ColourSpace colour_space, + Outputs::Display::ColourSpace colour_space, int colour_cycle_numerator, int colour_cycle_denominator, int vertical_sync_half_lines, bool should_alternate, @@ -105,7 +106,7 @@ CRT::CRT( int cycles_per_line, 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 common_output_divisor, DisplayType displayType, int buffer_depth) : +CRT::CRT(int cycles_per_line, int common_output_divisor, Outputs::Display::Type displayType, int buffer_depth) : CRT(common_output_divisor, buffer_depth) { set_new_display_type(cycles_per_line, displayType); } @@ -141,7 +142,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. bool is_output_segment = ((is_output_run && next_run_length) && !horizontal_flywheel_->is_in_retrace() && !vertical_flywheel_->is_in_retrace()); - 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_->get_scan() : nullptr; // If outputting, store the start location and if(next_scan) { @@ -171,14 +172,14 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ // 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::CRT::ScanTarget::Event::HorizontalRetrace); + scan_target_->announce(Outputs::Display::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); + scan_target_->announce(Outputs::Display::ScanTarget::Event::VerticalRetrace); } // if this is vertical retrace then adcance a field @@ -314,7 +315,7 @@ void CRT::output_data(int number_of_cycles, size_t number_of_samples) { output_scan(&scan); } -Outputs::CRT::Rect CRT::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) { +Outputs::Display::Rect CRT::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) { first_cycle_after_sync *= time_multiplier_; number_of_cycles *= time_multiplier_; @@ -359,5 +360,5 @@ Outputs::CRT::Rect CRT::get_rect_for_area(int first_line_after_sync, int number_ height = ideal_height; } - return Rect(start_x, start_y, width, height); + return Outputs::Display::Rect(start_x, start_y, width, height); } diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 973e4106b..bd68aa948 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -10,14 +10,10 @@ #define CRT_hpp #include +#include -#include "CRTTypes.hpp" +#include "../ScanTarget.hpp" #include "Internals/Flywheel.hpp" -#include "Internals/CRTOpenGL.hpp" -#include "Internals/ArrayBuilder.hpp" -#include "Internals/TextureBuilder.hpp" - -#include "ScanTarget.hpp" namespace Outputs { namespace CRT { @@ -85,7 +81,7 @@ class CRT { int cycles_per_line_ = 1; - ScanTarget *scan_target_ = nullptr; + Outputs::Display::ScanTarget *scan_target_ = nullptr; public: /*! Constructs the CRT with a specified clock rate, height and colour subcarrier frequency. @@ -122,7 +118,7 @@ class CRT { CRT(int cycles_per_line, int common_output_divisor, int height_of_display, - ColourSpace colour_space, + Outputs::Display::ColourSpace colour_space, int colour_cycle_numerator, int colour_cycle_denominator, int vertical_sync_half_lines, bool should_alternate, @@ -137,16 +133,23 @@ class CRT { */ CRT(int cycles_per_line, int common_output_divisor, - DisplayType displayType, + Outputs::Display::Type display_type, 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(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); + void set_new_timing( + int cycles_per_line, + int height_of_display, + Outputs::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(int cycles_per_line, DisplayType displayType); + void set_new_display_type(int cycles_per_line, Outputs::Display::Type display_type); /*! Output at the sync level. @@ -246,10 +249,15 @@ class CRT { */ void set_composite_function_type(CompositeSourceType type, float offset_of_first_sample = 0.0f); - inline void set_visible_area(Rect visible_area) { + inline void set_visible_area(Outputs::Display::Rect 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); + Outputs::Display::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); inline void set_delegate(Delegate *delegate) { delegate_ = delegate; diff --git a/Outputs/CRT/CRTTypes.hpp b/Outputs/CRT/CRTTypes.hpp deleted file mode 100644 index a674ed943..000000000 --- a/Outputs/CRT/CRTTypes.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// -// CRTTypes.hpp -// Clock Signal -// -// Created by Thomas Harte on 08/03/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#ifndef CRTTypes_h -#define CRTTypes_h - -namespace Outputs { -namespace CRT { - -enum class DisplayType { - PAL50, - NTSC60 -}; - -enum class VideoSignal { - RGB, - SVideo, - Composite -}; - -} -} - -#endif /* CRTTypes_h */ diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index bc246710a..c988b950e 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -9,7 +9,6 @@ #ifndef CRTOpenGL_h #define CRTOpenGL_h -#include "../CRTTypes.hpp" #include "CRTConstants.hpp" #include "OpenGL.hpp" #include "TextureTarget.hpp" diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.cpp b/Outputs/CRT/Internals/Shaders/OutputShader.cpp index 91ec8049a..2b48c04bf 100644 --- a/Outputs/CRT/Internals/Shaders/OutputShader.cpp +++ b/Outputs/CRT/Internals/Shaders/OutputShader.cpp @@ -92,7 +92,7 @@ std::unique_ptr OutputShader::make_shader(const char *fragment_met })); } -void OutputShader::set_output_size(unsigned int output_width, unsigned int output_height, Outputs::CRT::Rect visible_area) { +void OutputShader::set_output_size(unsigned int output_width, unsigned int output_height, Outputs::Display::Rect visible_area) { GLfloat outputAspectRatioMultiplier = (static_cast(output_width) / static_cast(output_height)) / (4.0f / 3.0f); GLfloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * visible_area.size.width; diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.hpp b/Outputs/CRT/Internals/Shaders/OutputShader.hpp index a6ffc50a4..117f94638 100644 --- a/Outputs/CRT/Internals/Shaders/OutputShader.hpp +++ b/Outputs/CRT/Internals/Shaders/OutputShader.hpp @@ -10,7 +10,6 @@ #define OutputShader_hpp #include "Shader.hpp" -#include "../../CRTTypes.hpp" #include "../../ScanTarget.hpp" #include @@ -64,7 +63,7 @@ public: the largest possible drawing size that allows everything within `visible_area` to be visible, to occur upon the next `bind`. */ - void set_output_size(unsigned int output_width, unsigned int output_height, Outputs::CRT::Rect visible_area); + void set_output_size(unsigned int output_width, unsigned int output_height, Outputs::Display::Rect visible_area); /*! Queues setting of the texture unit (as an enum, e.g. `GL_TEXTURE0`) for source data upon the next `bind`. diff --git a/Outputs/CRT/ScanTarget.hpp b/Outputs/ScanTarget.hpp similarity index 94% rename from Outputs/CRT/ScanTarget.hpp rename to Outputs/ScanTarget.hpp index 2f6773da9..71ac99ca2 100644 --- a/Outputs/CRT/ScanTarget.hpp +++ b/Outputs/ScanTarget.hpp @@ -6,11 +6,24 @@ // Copyright © 2018 Thomas Harte. All rights reserved. // -#ifndef Outputs_CRT_ScanTarget_h -#define Outputs_CRT_ScanTarget_h +#ifndef Outputs_Display_ScanTarget_h +#define Outputs_Display_ScanTarget_h + +#include namespace Outputs { -namespace CRT { +namespace Display { + +enum class Type { + PAL50, + NTSC60 +}; + +enum class VideoSignal { + RGB, + SVideo, + Composite +}; struct Rect { struct Point { @@ -161,11 +174,11 @@ struct ScanTarget { /// Calls will be paired off with calls to @c reduce_previous_allocation_to. /// /// @returns a pointer to the allocated space if any was available; @c nullptr otherwise. - virtual uint8_t *allocate_write_area(std::size_t required_length, std::size_t required_alignment = 1) = 0; + virtual uint8_t *allocate_write_area(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 /// and indicates that its actual final size was @c actual_length. - virtual void reduce_previous_allocation_to(std::size_t actual_length) = 0; + virtual void reduce_previous_allocation_to(size_t actual_length) = 0; /// Announces that all endpoint pairs and write areas obtained since the last @c submit have now been /// populated with appropriate data. @@ -202,4 +215,4 @@ struct ScanTarget { } } -#endif /* Outputs_CRT_ScanTarget_h */ +#endif /* Outputs_Display_ScanTarget_h */ From f6562de325b81071287a4c886ac3f71c79e847cf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 3 Nov 2018 23:40:39 -0400 Subject: [PATCH 005/208] Possibly adds enough for the Electron and ZX80 to start outputting dummy lines. Let's see! --- Components/6560/6560.hpp | 2 +- Components/9918/9918.cpp | 5 ++- Machines/AmstradCPC/AmstradCPC.cpp | 2 +- Machines/AppleII/Video.cpp | 2 +- Machines/Atari2600/TIA.cpp | 2 +- Machines/Electron/Electron.cpp | 10 +---- Machines/Electron/Video.cpp | 10 ++++- Machines/Electron/Video.hpp | 8 ++-- Machines/Oric/Video.cpp | 2 +- Machines/ZX8081/Video.cpp | 5 ++- Machines/ZX8081/Video.hpp | 2 +- Machines/ZX8081/ZX8081.cpp | 2 +- .../Mac/Clock Signal/Machine/CSMachine.mm | 27 +++++++++++++- Outputs/CRT/CRT.cpp | 34 +++++++++++------ Outputs/CRT/CRT.hpp | 37 ++++++++++--------- Outputs/ScanTarget.hpp | 11 ++---- 16 files changed, 97 insertions(+), 64 deletions(-) diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 38b082b03..70f5d9886 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -64,7 +64,7 @@ template class MOS6560 { public: MOS6560(BusHandler &bus_handler) : bus_handler_(bus_handler), - crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::Display::Type::NTSC60, 2)), +// crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::Display::Type::NTSC60, 2)), audio_generator_(audio_queue_), speaker_(audio_generator_) { diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index d043b1913..40710e0a6 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -49,8 +49,9 @@ struct ReverseTable { } Base::Base(Personality p) : - personality_(p), - crt_(new Outputs::CRT::CRT(CRTCyclesPerLine, CRTCyclesDivider, Outputs::Display::Type::NTSC60, 4)) { + personality_(p)//, +// crt_(new Outputs::CRT::CRT(CRTCyclesPerLine, CRTCyclesDivider, Outputs::Display::Type::NTSC60, 4)) + { switch(p) { case TI::TMS::TMS9918A: diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index db547f6ef..1b4097622 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -326,7 +326,7 @@ class CRTCBusHandler { /// Constructs an appropriate CRT for video output. void setup_output(Outputs::Display::ScanTarget *scan_target) { - crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::Display::Type::PAL50, 1)); +// crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::Display::Type::PAL50, 1)); // crt_->set_rgb_sampling_function( // "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" // "{" diff --git a/Machines/AppleII/Video.cpp b/Machines/AppleII/Video.cpp index d7ce90d60..19b7452f4 100644 --- a/Machines/AppleII/Video.cpp +++ b/Machines/AppleII/Video.cpp @@ -11,7 +11,7 @@ using namespace AppleII::Video; VideoBase::VideoBase(bool is_iie, std::function &&target) : - crt_(new Outputs::CRT::CRT(910, 1, Outputs::Display::Type::NTSC60, 1)), +// crt_(new Outputs::CRT::CRT(910, 1, Outputs::Display::Type::NTSC60, 1)), is_iie_(is_iie), deferrer_(std::move(target)) { diff --git a/Machines/Atari2600/TIA.cpp b/Machines/Atari2600/TIA.cpp index d748b482b..0e0ee5ff7 100644 --- a/Machines/Atari2600/TIA.cpp +++ b/Machines/Atari2600/TIA.cpp @@ -24,7 +24,7 @@ namespace { TIA::TIA(bool create_crt) { if(create_crt) { - crt_.reset(new Outputs::CRT::CRT(cycles_per_line * 2 - 1, 1, Outputs::Display::Type::NTSC60, 1)); +// crt_.reset(new Outputs::CRT::CRT(cycles_per_line * 2 - 1, 1, Outputs::Display::Type::NTSC60, 1)); // crt_->set_video_signal(Outputs::Display::VideoSignal::Composite); set_output_mode(OutputMode::NTSC); } diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 2205212f9..010db7391 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -374,17 +374,9 @@ class ConcreteMachine: } void setup_output(Outputs::Display::ScanTarget *scan_target) override final { - video_output_.reset(new VideoOutput(ram_)); + video_output_.reset(new VideoOutput(ram_, scan_target)); } -// void close_output() override final { -// video_output_.reset(); -// } -// -// Outputs::CRT::CRT *get_crt() override final { -// return video_output_->get_crt(); -// } - Outputs::Speaker::Speaker *get_speaker() override final { return &speaker_; } diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index 178e30094..65feffebb 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -38,12 +38,18 @@ namespace { // MARK: - Lifecycle -VideoOutput::VideoOutput(uint8_t *memory) : ram_(memory) { +VideoOutput::VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_target) : ram_(memory) { memset(palette_, 0xf, sizeof(palette_)); setup_screen_map(); setup_base_address(); - crt_.reset(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::Display::Type::PAL50, 1)); + crt_.reset(new Outputs::CRT::CRT( + crt_cycles_per_line, + 1024, + Outputs::Display::Type::PAL50, + Outputs::Display::ScanTarget::Modals::DataType::Red1Green1Blue1, + scan_target)); + // crt_->set_rgb_sampling_function( // "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" // "{" diff --git a/Machines/Electron/Video.hpp b/Machines/Electron/Video.hpp index d5df17ff0..716c6d808 100644 --- a/Machines/Electron/Video.hpp +++ b/Machines/Electron/Video.hpp @@ -27,10 +27,12 @@ namespace Electron { class VideoOutput { public: /*! - Instantiates a VideoOutput that will read its pixels from @c memory. The pointer supplied - should be to address 0 in the unexpanded Electron's memory map. + Instantiates a VideoOutput that will read its pixels from @c memory and output video + to @c scan_target. + + The pointer supplied should be to address 0 in the unexpanded Electron's memory map. */ - VideoOutput(uint8_t *memory); + VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_target); /// @returns the CRT to which output is being painted. Outputs::CRT::CRT *get_crt(); diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index e0963be47..a7c20a92b 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -23,7 +23,7 @@ namespace { VideoOutput::VideoOutput(uint8_t *memory) : ram_(memory), - crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::Display::Type::PAL50, 2)), +// crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::Display::Type::PAL50, 2)), v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition), counter_period_(PAL50Period) { // crt_->set_rgb_sampling_function( diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index a2b763b61..d16dae1d5 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -22,8 +22,9 @@ const std::size_t StandardAllocationSize = 320; } -Video::Video() : - crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::Display::Type::PAL50, 1)) { +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)) + { // 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. diff --git a/Machines/ZX8081/Video.hpp b/Machines/ZX8081/Video.hpp index dac52b361..bc5813582 100644 --- a/Machines/ZX8081/Video.hpp +++ b/Machines/ZX8081/Video.hpp @@ -27,7 +27,7 @@ namespace ZX8081 { class Video { public: /// Constructs an instance of the video feed; a CRT is also created. - Video(); + Video(Outputs::Display::ScanTarget *scan_target); /// @returns The CRT this video feed is feeding. Outputs::CRT::CRT *get_crt(); diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 49b1053f1..cfaa28bc0 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -311,7 +311,7 @@ template class ConcreteMachine: } void setup_output(Outputs::Display::ScanTarget *scan_target) override final { - video_.reset(new Video); + video_.reset(new Video(scan_target)); } Outputs::Speaker::Speaker *get_speaker() override final { diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 497703324..b3b7c7bba 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -29,6 +29,8 @@ #import #include +#include "ScanTarget.hpp" + @interface CSMachine() - (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; - (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker; @@ -71,6 +73,27 @@ struct ActivityObserver: public Activity::Observer { __unsafe_unretained CSMachine *machine; }; +class ScanTarget: public Outputs::Display::ScanTarget { + public: + void set_modals(Modals) {} + + Scan *get_scan() { + return &scan_; + } + + uint8_t *allocate_write_area(size_t required_length, size_t required_alignment) { + write_area_.resize(required_length); + return write_area_.data(); + } + + void submit(bool only_if_no_allocation_failures) { + } + + private: + Scan scan_; + std::vector write_area_; +}; + @implementation CSMachine { SpeakerDelegate _speakerDelegate; ActivityObserver _activityObserver; @@ -83,6 +106,8 @@ struct ActivityObserver: public Activity::Observer { CSJoystickManager *_joystickManager; std::bitset<65536> _depressedKeys; NSMutableArray *_leds; + + ScanTarget _scanTarget; } - (instancetype)initWithAnalyser:(CSStaticAnalyser *)result { @@ -231,7 +256,7 @@ struct ActivityObserver: public Activity::Observer { } - (void)setupOutputWithAspectRatio:(float)aspectRatio { -// _machine->crt_machine()->setup_output(aspectRatio); + _machine->crt_machine()->setup_output(&_scanTarget); // Since OS X v10.6, Macs have had a gamma of 2.2. // _machine->crt_machine()->get_crt()->set_output_gamma(2.2f); diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 5ecb4ef1e..61e0067bf 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -56,12 +56,14 @@ 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_ * common_output_divisor_); - vertical_flywheel_output_divider_ = static_cast(ceilf(real_clock_scan_period / 65536.0f) * (time_multiplier_ * common_output_divisor_)); +// 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_); - // TODO: set scan_target modals. + scan_target_modals_.expected_vertical_lines = height_of_display; + scan_target_modals_.composite_colour_space = colour_space; + scan_target_->set_modals(scan_target_modals_); } void CRT::set_new_display_type(int cycles_per_line, Outputs::Display::Type displayType) { @@ -91,24 +93,30 @@ void CRT::set_input_gamma(float gamma) { // update_gamma(); } -CRT::CRT(int common_output_divisor, int buffer_depth) : - common_output_divisor_(common_output_divisor) {} - CRT::CRT( int cycles_per_line, - int common_output_divisor, + int pixel_clock_least_common_multiple, int height_of_display, Outputs::Display::ColourSpace colour_space, int colour_cycle_numerator, int colour_cycle_denominator, int vertical_sync_half_lines, bool should_alternate, - int buffer_depth) : - CRT(common_output_divisor, buffer_depth) { + 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; 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 common_output_divisor, Outputs::Display::Type displayType, int buffer_depth) : - CRT(common_output_divisor, buffer_depth) { - set_new_display_type(cycles_per_line, displayType); +CRT::CRT( int cycles_per_line, + int pixel_clock_least_common_multiple, + 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; + set_new_display_type(cycles_per_line, display_type); } // MARK: - Sync loop @@ -193,6 +201,8 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ } } } + + scan_target_->submit(); } // MARK: - stream feeding methods diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index bd68aa948..1e4ad124a 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -27,12 +27,9 @@ class Delegate { class CRT { private: - 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 int time_multiplier_ = 1; - const int common_output_divisor_ = 1; // the two flywheels regulating scanning std::unique_ptr horizontal_flywheel_, vertical_flywheel_; @@ -82,6 +79,7 @@ class CRT { int cycles_per_line_ = 1; Outputs::Display::ScanTarget *scan_target_ = nullptr; + Outputs::Display::ScanTarget::Modals scan_target_modals_; public: /*! Constructs the CRT with a specified clock rate, height and colour subcarrier frequency. @@ -91,10 +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 common_output_divisor The greatest a priori common divisor of all cycle counts that will be - supplied to @c output_sync, @c output_data, etc; supply 1 if no greater divisor is known. For many - machines output will run at a fixed multiple of the clock rate; knowing this divisor can improve - internal precision. + @param pixel_clock_least_common_multiple TODO. @param height_of_display The number of lines that nominally form one field of the display, rounded up to the next whole integer. @@ -107,22 +102,22 @@ class CRT { @param vertical_sync_half_lines The expected length of vertical synchronisation (equalisation pulses aside), in multiples of half a line. - @param buffer_depth The depth per pixel of source data buffers to create for this machine. Machines - may provide per-clock-cycle data in the depth that they consider convenient, supplying a sampling - function to convert between their data format and either a composite or RGB signal, allowing that - work to be offloaded onto the GPU and allowing the output signal to be sampled at a rate appropriate - to the display size. + @param data_type TODO. + + @param scan_target TODO. @see @c set_rgb_sampling_function , @c set_composite_sampling_function */ CRT(int cycles_per_line, - int common_output_divisor, + int pixel_clock_least_common_multiple, int height_of_display, Outputs::Display::ColourSpace colour_space, - int colour_cycle_numerator, int colour_cycle_denominator, + int colour_cycle_numerator, + int colour_cycle_denominator, int vertical_sync_half_lines, bool should_alternate, - int buffer_depth); + Outputs::Display::ScanTarget::Modals::DataType data_type, + Outputs::Display::ScanTarget *scan_target); /*! 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 @@ -132,9 +127,10 @@ class CRT { looked up by display type. */ CRT(int cycles_per_line, - int common_output_divisor, + int pixel_clock_least_common_multiple, Outputs::Display::Type display_type, - int buffer_depth); + Outputs::Display::ScanTarget::Modals::DataType data_type, + Outputs::Display::ScanTarget *scan_target); /*! Resets the CRT with new timing information. The CRT then continues as though the new timing had been provided at construction. */ @@ -149,7 +145,12 @@ class CRT { /*! 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(int cycles_per_line, Outputs::Display::Type display_type); + void set_new_display_type( + int cycles_per_line, + Outputs::Display::Type display_type); + + // TODO. + void set_new_data_type(Outputs::Display::ScanTarget::Modals::DataType data_type); /*! Output at the sync level. diff --git a/Outputs/ScanTarget.hpp b/Outputs/ScanTarget.hpp index 71ac99ca2..a52cc79d6 100644 --- a/Outputs/ScanTarget.hpp +++ b/Outputs/ScanTarget.hpp @@ -34,7 +34,7 @@ struct Rect { float width, height; } size; - Rect() {} + Rect() : origin({0.0f, 0.0f}), size({1.0f, 1.0f}) {} Rect(float x, float y, float width, float height) : origin({x, y}), size({width, height}) {} }; @@ -178,7 +178,7 @@ struct ScanTarget { /// Announces that the owner is finished with the region created by the most recent @c allocate_write_area /// and indicates that its actual final size was @c actual_length. - virtual void reduce_previous_allocation_to(size_t actual_length) = 0; + virtual void reduce_previous_allocation_to(size_t actual_length) {}; /// Announces that all endpoint pairs and write areas obtained since the last @c submit have now been /// populated with appropriate data. @@ -187,15 +187,10 @@ struct ScanTarget { /// as long as it feels is appropriate subject to an @c flush. 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 - /// a faulty total output. - virtual void reset() = 0; - /// Announces that any submitted data not yet output should be output now, but needn't block while /// doing so. This generally communicates that processing is now otherwise 'up to date', so no /// further delay should be allowed. - virtual void flush() = 0; +// virtual void flush() = 0; /* From 05fb7db147252dfe03d10e7101ccbaf5f548c278 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 3 Nov 2018 23:47:41 -0400 Subject: [PATCH 006/208] Reduces CRT chattiness. --- OSBindings/Mac/Clock Signal/Machine/CSMachine.mm | 6 ++++-- Outputs/CRT/CRT.cpp | 10 +++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index b3b7c7bba..824eedfc1 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -78,7 +78,8 @@ class ScanTarget: public Outputs::Display::ScanTarget { void set_modals(Modals) {} Scan *get_scan() { - return &scan_; + scans_.emplace_back(); + return &scans_.back(); } uint8_t *allocate_write_area(size_t required_length, size_t required_alignment) { @@ -87,10 +88,11 @@ class ScanTarget: public Outputs::Display::ScanTarget { } void submit(bool only_if_no_allocation_failures) { + scans_.clear(); } private: - Scan scan_; + std::vector scans_; std::vector write_area_; }; diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 61e0067bf..9fd56ede3 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -134,6 +134,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ const bool is_output_run = ((type == Scan::Type::Level) || (type == Scan::Type::Data)); const auto total_cycles = number_of_cycles * time_multiplier_; + bool did_output = false; while(number_of_cycles) { @@ -143,14 +144,15 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ Flywheel::SyncEvent next_horizontal_sync_event = get_next_horizontal_sync_event(hsync_requested, time_until_vertical_sync_event, &time_until_horizontal_sync_event); // 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); + const 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()); + 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; + did_output |= is_output_segment; // If outputting, store the start location and if(next_scan) { @@ -202,7 +204,9 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ } } - scan_target_->submit(); + if(did_output) { + scan_target_->submit(); + } } // MARK: - stream feeding methods From 0446e350d313b275292b686f305d7bc0235261ba Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 3 Nov 2018 23:51:26 -0400 Subject: [PATCH 007/208] Resolves sizing of texture coordinates, and improves constness slightly. --- Outputs/CRT/CRT.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 9fd56ede3..b6f984fd5 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -133,15 +133,15 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ number_of_cycles *= time_multiplier_; const bool is_output_run = ((type == Scan::Type::Level) || (type == Scan::Type::Data)); - const auto total_cycles = number_of_cycles * time_multiplier_; + const auto total_cycles = number_of_cycles; bool did_output = false; while(number_of_cycles) { // 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); + const Flywheel::SyncEvent next_vertical_sync_event = get_next_vertical_sync_event(vsync_requested, number_of_cycles, &time_until_vertical_sync_event); + const Flywheel::SyncEvent next_horizontal_sync_event = get_next_horizontal_sync_event(hsync_requested, time_until_vertical_sync_event, &time_until_horizontal_sync_event); // Whichever event is scheduled to happen first is the one to advance to. const int next_run_length = std::min(time_until_vertical_sync_event, time_until_horizontal_sync_event); From 014da41471f36661485475d331cd052edf61c0e3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Nov 2018 21:06:25 -0500 Subject: [PATCH 008/208] 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 From eb28095041673c417d906d6c8a29e54e3a0a40dc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Nov 2018 21:44:22 -0500 Subject: [PATCH 009/208] Ensures proper accumulation and reporting of colour phase across lines. --- Outputs/CRT/CRT.cpp | 22 +++++++++++----------- Outputs/CRT/CRT.hpp | 4 +++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index a5b135d3c..8a3f81f96 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -29,9 +29,9 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di // in NTSC and PAL TV." time_multiplier_ = 65535 / cycles_per_line; - phase_denominator_ = cycles_per_line * colour_cycle_denominator * time_multiplier_; + phase_denominator_ = int64_t(cycles_per_line) * int64_t(colour_cycle_denominator) * int64_t(time_multiplier_); phase_numerator_ = 0; - colour_cycle_numerator_ = colour_cycle_numerator; + colour_cycle_numerator_ = int64_t(colour_cycle_numerator); phase_alternates_ = should_alternate; is_alernate_line_ &= phase_alternates_; cycles_per_line_ = cycles_per_line; @@ -157,16 +157,15 @@ 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() / 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->end_points[0].x = uint16_t(horizontal_flywheel_->get_current_output_position()); + next_scan->end_points[0].y = uint16_t(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_); + next_scan->end_points[0].composite_angle = int16_t((phase_numerator_ << 6) / phase_denominator_) * (is_alernate_line_ ? -1 : 1); + next_scan->end_points[0].data_offset = uint16_t((total_cycles - number_of_cycles) * number_of_samples / total_cycles); next_scan->composite_amplitude = colour_burst_amplitude_; } // 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. @@ -175,10 +174,10 @@ 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() / 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); + 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); } // If this is horizontal retrace then announce as such, and prepare for the next line. @@ -186,6 +185,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ scan_target_->announce(Outputs::Display::ScanTarget::Event::HorizontalRetrace); is_alernate_line_ ^= phase_alternates_; colour_burst_amplitude_ = 0; + phase_numerator_ = 0; } // Also announce if this is vertical retrace. diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 4db06643f..803006fe5 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -53,7 +53,9 @@ class CRT { int colour_burst_phase_adjustment_ = 0; bool is_writing_composite_run_ = false; - int phase_denominator_ = 1, phase_numerator_ = 1, colour_cycle_numerator_ = 1; + int64_t phase_denominator_ = 1; + int64_t phase_numerator_ = 0; + int64_t 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 From 1effb97b74869f88c4a2aeee73cc483e6271a0e4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Nov 2018 21:57:46 -0500 Subject: [PATCH 010/208] Reintroduces colour phase acquisition from the colour burst. --- Outputs/CRT/CRT.cpp | 16 ++++++++++------ Outputs/CRT/CRT.hpp | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 8a3f81f96..52f677e99 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -185,7 +185,6 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ scan_target_->announce(Outputs::Display::ScanTarget::Event::HorizontalRetrace); is_alernate_line_ ^= phase_alternates_; colour_burst_amplitude_ = 0; - phase_numerator_ = 0; } // Also announce if this is vertical retrace. @@ -216,12 +215,16 @@ 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) { -// 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; + // Load phase_numerator_ as a fixed-point quantity in the range [0, 255]. + phase_numerator_ = scan->phase; + if(colour_burst_phase_adjustment_ != 0xff) + phase_numerator_ = (phase_numerator_ & ~63) + colour_burst_phase_adjustment_; -// if(colour_burst_phase_adjustment_ != 0xff) -// colour_burst_phase_ = (colour_burst_phase_ & ~63) + colour_burst_phase_adjustment_; + // Multiply the phase_numerator_ up to be to the proper scale. + phase_numerator_ = (phase_numerator_ * phase_denominator_) >> 8; + + // Crib the colour burst amplitude. + colour_burst_amplitude_ = scan->amplitude; } } // TODO: inspect raw data for potential colour burst if required; the DPLL and some zero crossing logic @@ -313,6 +316,7 @@ void CRT::output_colour_burst(int number_of_cycles, uint8_t phase, uint8_t ampli } void CRT::output_default_colour_burst(int number_of_cycles) { + // TODO: avoid applying a rounding error here? output_colour_burst(number_of_cycles, static_cast((phase_numerator_ * 256) / phase_denominator_)); } diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 803006fe5..0dccfc5b3 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -50,7 +50,7 @@ class CRT { int16_t colour_burst_angle_ = 0; uint8_t colour_burst_amplitude_ = 30; - int colour_burst_phase_adjustment_ = 0; + int colour_burst_phase_adjustment_ = 0xff; bool is_writing_composite_run_ = false; int64_t phase_denominator_ = 1; From 9799aa0975715446e87e72bfe8d3558fffa2d27a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Nov 2018 22:17:33 -0500 Subject: [PATCH 011/208] Completes documentation and rounds out implementation. --- Outputs/CRT/CRT.cpp | 62 ++++++++++++++++++++++++++------------------- Outputs/CRT/CRT.hpp | 57 ++++++++++++++++++++--------------------- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 52f677e99..1820a7c94 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -16,10 +16,9 @@ using namespace Outputs::CRT; void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::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 int millisecondsHorizontalRetraceTime = 7; // source: Dictionary of Video and Television Technology, p. 234 - const 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: // @@ -37,9 +36,9 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di cycles_per_line_ = cycles_per_line; 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, + // 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 - // the gist for simple debugging + // the gist for simple debugging. sync_capacitor_charge_threshold_ = ((vertical_sync_half_lines - 2) * cycles_per_line) >> 1; // Create the two flywheels: @@ -54,10 +53,11 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di horizontal_flywheel_.reset(new Flywheel(multiplied_cycles_per_line, (millisecondsHorizontalRetraceTime * multiplied_cycles_per_line) >> 6, multiplied_cycles_per_line >> 5)); 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 + // 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; vertical_flywheel_output_divider_ = (real_clock_scan_period + 65534) / 65535; + // Communicate relevant fields to the scan target. 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; @@ -65,16 +65,26 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di scan_target_->set_modals(scan_target_modals_); } +void CRT::set_new_data_type(Outputs::Display::ScanTarget::Modals::DataType data_type) { + scan_target_modals_.source_data_type = data_type; + scan_target_->set_modals(scan_target_modals_); +} + +void CRT::set_visible_area(Outputs::Display::Rect visible_area) { + scan_target_modals_.visible_area = visible_area; + scan_target_->set_modals(scan_target_modals_); +} + 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_new_timing(cycles_per_line, 312, Outputs::Display::ColourSpace::YUV, 709379, 2500, 5, true); // i.e. 283.7516 colour cycles per line; 2.5 lines = vertical sync. 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_new_timing(cycles_per_line, 262, Outputs::Display::ColourSpace::YIQ, 455, 2, 6, false); // i.e. 227.5 colour cycles per line, 3 lines = vertical sync. break; } } @@ -155,7 +165,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ Outputs::Display::ScanTarget::Scan *const next_scan = is_output_segment ? scan_target_->get_scan() : nullptr; did_output |= is_output_segment; - // If outputting, store the start location and + // If outputting, store the start location and scan constants. if(next_scan) { next_scan->end_points[0].x = uint16_t(horizontal_flywheel_->get_current_output_position()); next_scan->end_points[0].y = uint16_t(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_); @@ -212,7 +222,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ // 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 + // 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) { // Load phase_numerator_ as a fixed-point quantity in the range [0, 255]. @@ -235,18 +245,18 @@ void CRT::output_scan(const Scan *const scan) { const bool is_leading_edge = (!is_receiving_sync_ && this_is_sync); is_receiving_sync_ = this_is_sync; - // horizontal sync is recognised on any leading edge that is not 'near' the expected vertical sync; + // Horizontal sync is recognised on any leading edge that is not 'near' the expected vertical sync; // the second limb is to avoid slightly horizontal sync shifting from the common pattern of - // equalisation pulses as the inverse of ordinary horizontal sync + // equalisation pulses as the inverse of ordinary horizontal sync. bool hsync_requested = is_leading_edge && !vertical_flywheel_->is_near_expected_sync(); if(this_is_sync) { - // if this is sync then either begin or continue a sync accumulation phase + // If this is sync then either begin or continue a sync accumulation phase. is_accumulating_sync_ = true; cycles_since_sync_ = 0; } else { - // if this is not sync then check how long it has been since sync. If it's more than - // half a line then end sync accumulation and zero out the accumulating count + // If this is not sync then check how long it has been since sync. If it's more than + // half a line then end sync accumulation and zero out the accumulating count. cycles_since_sync_ += scan->number_of_cycles; if(cycles_since_sync_ > (cycles_per_line_ >> 2)) { cycles_of_sync_ = 0; @@ -258,13 +268,13 @@ void CRT::output_scan(const Scan *const scan) { 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 - // divide this line at the crossing point and indicate vertical sync there + // If sync is being accumulated then accumulate it; if it crosses the vertical sync threshold then + // divide this line at the crossing point and indicate vertical sync there. if(is_accumulating_sync_ && !is_refusing_sync_) { cycles_of_sync_ += scan->number_of_cycles; if(this_is_sync && cycles_of_sync_ >= sync_capacitor_charge_threshold_) { - int overshoot = std::min(cycles_of_sync_ - sync_capacitor_charge_threshold_, number_of_cycles); + const 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, 0); @@ -342,9 +352,9 @@ Outputs::Display::Rect CRT::get_rect_for_area(int first_line_after_sync, int num number_of_lines += 4; // determine prima facie x extent - 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; + const int horizontal_period = horizontal_flywheel_->get_standard_period(); + const int horizontal_scan_period = horizontal_flywheel_->get_scan_period(); + const 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); @@ -354,9 +364,9 @@ Outputs::Display::Rect CRT::get_rect_for_area(int first_line_after_sync, int num float width = static_cast(number_of_cycles) / static_cast(horizontal_scan_period); // determine prima facie y extent - 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; + const int vertical_period = vertical_flywheel_->get_standard_period(); + const int vertical_scan_period = vertical_flywheel_->get_scan_period(); + const 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) @@ -368,8 +378,8 @@ Outputs::Display::Rect CRT::get_rect_for_area(int first_line_after_sync, int num 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); - float ideal_width = height * adjusted_aspect_ratio; + const float adjusted_aspect_ratio = (3.0f*aspect_ratio / 4.0f); + const float ideal_width = height * adjusted_aspect_ratio; if(ideal_width > width) { start_x -= (ideal_width - width) * 0.5f; width = ideal_width; diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 0dccfc5b3..a876c9f93 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -25,13 +25,20 @@ class Delegate { virtual void crt_did_end_batch_of_frames(CRT *crt, int number_of_frames, int number_of_unexpected_vertical_syncs) = 0; }; +/*! Models a class 2d analogue output device, accepting a serial stream of data including syncs + and generating the proper set of output spans. Attempts to act and react exactly as a real + TV would have to things like irregular or off-spec sync, and includes logic properly to track + colour phase for colour composite video. +*/ class CRT { private: - // the incoming clock lengths will be multiplied by something to give at least 1000 - // sample points per line + // The incoming clock lengths will be multiplied by @c time_multiplier_; this increases + // precision across the line. int time_multiplier_ = 1; - // the two flywheels regulating scanning + // Two flywheels regulate scanning; the vertical will have a range much greater than the horizontal; + // the output divider is what that'll need to be divided by to reduce it into a 16-bit range as + // posted on to the scan target. std::unique_ptr horizontal_flywheel_, vertical_flywheel_; int vertical_flywheel_output_divider_ = 1; @@ -58,25 +65,19 @@ class CRT { int64_t 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(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, 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; 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 - 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 + bool is_receiving_sync_ = false; // @c true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync); @c false otherwise. + bool is_accumulating_sync_ = false; // @c true if a sync level has triggered the suspicion that a vertical sync might be in progress; @c false otherwise. + bool is_refusing_sync_ = false; // @c true once a vertical sync has been detected, until a prolonged period of non-sync has ended suspicion of an ongoing vertical sync. + int sync_capacitor_charge_threshold_ = 0; // 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. int cycles_per_line_ = 1; @@ -91,7 +92,8 @@ 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 minimum_cycles_per_pixel TODO. + @param clocks_per_pixel_greatest_common_divisor The GCD of all potential lengths of a pixel + in terms of the clock rate given as @c cycles_per_line. @param height_of_display The number of lines that nominally form one field of the display, rounded up to the next whole integer. @@ -104,14 +106,12 @@ class CRT { @param vertical_sync_half_lines The expected length of vertical synchronisation (equalisation pulses aside), in multiples of half a line. - @param data_type TODO. + @param data_type The format that the caller will use for input data. - @param scan_target TODO. - - @see @c set_rgb_sampling_function , @c set_composite_sampling_function + @param scan_target The destination for generated scans. */ CRT(int cycles_per_line, - int minimum_cycles_per_pixel, + int clocks_per_pixel_greatest_common_divisor, int height_of_display, Outputs::Display::ColourSpace colour_space, int colour_cycle_numerator, @@ -121,11 +121,7 @@ class CRT { Outputs::Display::ScanTarget::Modals::DataType data_type, Outputs::Display::ScanTarget *scan_target); - /*! 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 - buffers, each with the requested number of bytes per pixel. - - Exactly identical to calling the designated constructor with colour subcarrier information + /*! Exactly identical to calling the designated constructor with colour subcarrier information looked up by display type. */ CRT(int cycles_per_line, @@ -151,7 +147,8 @@ class CRT { int cycles_per_line, Outputs::Display::Type display_type); - // TODO. + /*! Changes the type of data being supplied as input. + */ void set_new_data_type(Outputs::Display::ScanTarget::Modals::DataType data_type); /*! Output at the sync level. @@ -252,9 +249,10 @@ class CRT { */ void set_composite_function_type(CompositeSourceType type, float offset_of_first_sample = 0.0f); - inline void set_visible_area(Outputs::Display::Rect visible_area) { - } + /*! Nominates a section of the display to crop to for output. */ + void set_visible_area(Outputs::Display::Rect visible_area); + /*! @returns The rectangle describing a subset of the display, allowing for sync periods. */ Outputs::Display::Rect get_rect_for_area( int first_line_after_sync, int number_of_lines, @@ -262,6 +260,7 @@ class CRT { int number_of_cycles, float aspect_ratio); + /*! Sets the CRT delegate; set to @c nullptr if no delegate is desired. */ inline void set_delegate(Delegate *delegate) { delegate_ = delegate; } From 55da1e9c0fa1ddac2ea0c239a83a090e2264f100 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Nov 2018 22:23:38 -0500 Subject: [PATCH 012/208] Simplifies semantics a little and starts accepting a single buffer of pixel data. --- Machines/Electron/Video.cpp | 2 +- Machines/ZX8081/Video.cpp | 2 +- .../Clock Signal.xcodeproj/project.pbxproj | 109 +++++++++++------- .../Mac/Clock Signal/Machine/CSMachine.mm | 66 ++++++----- Outputs/CRT/CRT.cpp | 12 +- Outputs/CRT/CRT.hpp | 6 +- .../Internals => OpenGL}/ArrayBuilder.cpp | 0 .../Internals => OpenGL}/ArrayBuilder.hpp | 0 .../{CRT/Internals => OpenGL}/CRTOpenGL.cpp | 0 .../{CRT/Internals => OpenGL}/CRTOpenGL.hpp | 0 Outputs/{CRT/Internals => OpenGL}/OpenGL.hpp | 0 .../{CRT/Internals => OpenGL}/Rectangle.cpp | 0 .../{CRT/Internals => OpenGL}/Rectangle.hpp | 0 Outputs/OpenGL/ScanTarget.cpp | 93 +++++++++++++++ Outputs/OpenGL/ScanTarget.hpp | 71 ++++++++++++ .../Shaders/IntermediateShader.cpp | 2 +- .../Shaders/IntermediateShader.hpp | 0 .../Shaders/OutputShader.cpp | 0 .../Shaders/OutputShader.hpp | 0 .../Internals => OpenGL}/Shaders/Shader.cpp | 0 .../Internals => OpenGL}/Shaders/Shader.hpp | 0 .../Internals => OpenGL}/TextureBuilder.cpp | 0 .../Internals => OpenGL}/TextureBuilder.hpp | 0 .../Internals => OpenGL}/TextureTarget.cpp | 0 .../Internals => OpenGL}/TextureTarget.hpp | 0 Outputs/ScanTarget.hpp | 98 ++++++++++------ 26 files changed, 343 insertions(+), 118 deletions(-) rename Outputs/{CRT/Internals => OpenGL}/ArrayBuilder.cpp (100%) rename Outputs/{CRT/Internals => OpenGL}/ArrayBuilder.hpp (100%) rename Outputs/{CRT/Internals => OpenGL}/CRTOpenGL.cpp (100%) rename Outputs/{CRT/Internals => OpenGL}/CRTOpenGL.hpp (100%) rename Outputs/{CRT/Internals => OpenGL}/OpenGL.hpp (100%) rename Outputs/{CRT/Internals => OpenGL}/Rectangle.cpp (100%) rename Outputs/{CRT/Internals => OpenGL}/Rectangle.hpp (100%) create mode 100644 Outputs/OpenGL/ScanTarget.cpp create mode 100644 Outputs/OpenGL/ScanTarget.hpp rename Outputs/{CRT/Internals => OpenGL}/Shaders/IntermediateShader.cpp (99%) rename Outputs/{CRT/Internals => OpenGL}/Shaders/IntermediateShader.hpp (100%) rename Outputs/{CRT/Internals => OpenGL}/Shaders/OutputShader.cpp (100%) rename Outputs/{CRT/Internals => OpenGL}/Shaders/OutputShader.hpp (100%) rename Outputs/{CRT/Internals => OpenGL}/Shaders/Shader.cpp (100%) rename Outputs/{CRT/Internals => OpenGL}/Shaders/Shader.hpp (100%) rename Outputs/{CRT/Internals => OpenGL}/TextureBuilder.cpp (100%) rename Outputs/{CRT/Internals => OpenGL}/TextureBuilder.hpp (100%) rename Outputs/{CRT/Internals => OpenGL}/TextureTarget.cpp (100%) rename Outputs/{CRT/Internals => OpenGL}/TextureTarget.hpp (100%) diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index f62ad62ab..1b9dbd565 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -47,7 +47,7 @@ VideoOutput::VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_tar crt_cycles_per_line, 1, Outputs::Display::Type::PAL50, - Outputs::Display::ScanTarget::Modals::DataType::Red1Green1Blue1, + Outputs::Display::InputDataType::Red1Green1Blue1, scan_target)); // crt_->set_rgb_sampling_function( diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index ee98d1693..379ac2aa7 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, 1, 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::InputDataType::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.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 9e4732ad8..d584a56c6 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -613,6 +613,12 @@ 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; }; 4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCA6CC61D9DD9F000C2D7B2 /* CommodoreROM.cpp */; }; 4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */; }; + 4BD191F0219117F90042E144 /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191E6219113B80042E144 /* Rectangle.cpp */; }; + 4BD191F1219117FA0042E144 /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191E6219113B80042E144 /* Rectangle.cpp */; }; + 4BD191F42191180E0042E144 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191F22191180E0042E144 /* ScanTarget.cpp */; }; + 4BD191F52191180E0042E144 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191F22191180E0042E144 /* ScanTarget.cpp */; }; + 4BD191F6219118390042E144 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191E2219113B80042E144 /* Shader.cpp */; }; + 4BD191F72191183A0042E144 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191E2219113B80042E144 /* Shader.cpp */; }; 4BD3A30B1EE755C800B5B501 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD3A3091EE755C800B5B501 /* Video.cpp */; }; 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD468F51D8DF41D0084958B /* 1770.cpp */; }; 4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */; }; @@ -856,8 +862,6 @@ 4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = ""; }; 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialBus.cpp; sourceTree = ""; }; 4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SerialBus.hpp; sourceTree = ""; }; - 4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayBuilder.cpp; sourceTree = ""; }; - 4B5073061DDD3B9400C48FBD /* ArrayBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ArrayBuilder.hpp; sourceTree = ""; }; 4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ArrayBuilderTests.mm; sourceTree = ""; }; 4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = ""; }; 4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = ""; }; @@ -1322,8 +1326,6 @@ 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clock_SignalUITests.swift; sourceTree = ""; }; 4BB73EC31B587A5100552FC2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4BB73ECF1B587A6700552FC2 /* Clock Signal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Clock Signal.entitlements"; sourceTree = ""; }; - 4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntermediateShader.cpp; sourceTree = ""; }; - 4BBB14301CD2CECE00BDB55C /* IntermediateShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IntermediateShader.hpp; sourceTree = ""; }; 4BBB709C2020109C002FE009 /* DynamicMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DynamicMachine.hpp; sourceTree = ""; }; 4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMediaTarget.hpp; sourceTree = ""; }; 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMediaTarget.cpp; sourceTree = ""; }; @@ -1333,14 +1335,7 @@ 4BBC951C1F368D83008F4C34 /* i8272.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = i8272.cpp; path = 8272/i8272.cpp; sourceTree = ""; }; 4BBC951D1F368D83008F4C34 /* i8272.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = i8272.hpp; path = 8272/i8272.hpp; sourceTree = ""; }; 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FUSETests.swift; sourceTree = ""; }; - 4BBF99081C8FBA6F0075DAFB /* TextureBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureBuilder.cpp; sourceTree = ""; }; - 4BBF99091C8FBA6F0075DAFB /* TextureBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureBuilder.hpp; sourceTree = ""; }; - 4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTOpenGL.cpp; sourceTree = ""; }; - 4BBF990B1C8FBA6F0075DAFB /* CRTOpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTOpenGL.hpp; sourceTree = ""; }; 4BBF990E1C8FBA6F0075DAFB /* Flywheel.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Flywheel.hpp; sourceTree = ""; }; - 4BBF990F1C8FBA6F0075DAFB /* OpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OpenGL.hpp; sourceTree = ""; }; - 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureTarget.cpp; sourceTree = ""; }; - 4BBF99131C8FBA6F0075DAFB /* TextureTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureTarget.hpp; sourceTree = ""; }; 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Parsers/ZX8081.cpp; sourceTree = ""; }; 4BBFBB6B1EE8401E00C01E7A /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Parsers/ZX8081.hpp; sourceTree = ""; }; 4BBFE83C21015D9C00BF1C40 /* CSJoystickManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSJoystickManager.m; sourceTree = ""; }; @@ -1349,17 +1344,11 @@ 4BC39565208EDFCE0044766B /* Card.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Card.hpp; sourceTree = ""; }; 4BC39566208EE6CF0044766B /* DiskIICard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DiskIICard.cpp; sourceTree = ""; }; 4BC39567208EE6CF0044766B /* DiskIICard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskIICard.hpp; sourceTree = ""; }; - 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = ""; }; - 4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = ""; }; - 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; - 4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = ""; }; 4BC5FC2F20CDDDEE00410AA0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/AppleIIOptions.xib"; sourceTree = SOURCE_ROOT; }; 4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = ""; }; 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = ""; }; 4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = ""; }; 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; - 4BC891AB20F6EAB300EDE5B3 /* Rectangle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Rectangle.cpp; sourceTree = ""; }; - 4BC891AC20F6EAB300EDE5B3 /* Rectangle.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Rectangle.hpp; sourceTree = ""; }; 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommodoreTAP.cpp; sourceTree = ""; }; 4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CommodoreTAP.hpp; sourceTree = ""; }; 4BC9DF441D044FCA00F44158 /* ROMImages */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ROMImages; path = ../../../../ROMImages; sourceTree = ""; }; @@ -1372,6 +1361,25 @@ 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Oric.cpp; path = Oric/Oric.cpp; sourceTree = ""; }; 4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Oric/Oric.hpp; sourceTree = ""; }; 4BD060A51FE49D3C006E14BE /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = ""; }; + 4BD191D6219113B80042E144 /* ArrayBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayBuilder.cpp; sourceTree = ""; }; + 4BD191D7219113B80042E144 /* CRTOpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTOpenGL.hpp; sourceTree = ""; }; + 4BD191D8219113B80042E144 /* TextureBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureBuilder.cpp; sourceTree = ""; }; + 4BD191D9219113B80042E144 /* OpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OpenGL.hpp; sourceTree = ""; }; + 4BD191DA219113B80042E144 /* TextureTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureTarget.cpp; sourceTree = ""; }; + 4BD191DB219113B80042E144 /* Rectangle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Rectangle.hpp; sourceTree = ""; }; + 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTOpenGL.cpp; sourceTree = ""; }; + 4BD191DD219113B80042E144 /* TextureBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureBuilder.hpp; sourceTree = ""; }; + 4BD191DE219113B80042E144 /* TextureTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureTarget.hpp; sourceTree = ""; }; + 4BD191E0219113B80042E144 /* IntermediateShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IntermediateShader.hpp; sourceTree = ""; }; + 4BD191E1219113B80042E144 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; + 4BD191E2219113B80042E144 /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = ""; }; + 4BD191E3219113B80042E144 /* IntermediateShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntermediateShader.cpp; sourceTree = ""; }; + 4BD191E4219113B80042E144 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = ""; }; + 4BD191E5219113B80042E144 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = ""; }; + 4BD191E6219113B80042E144 /* Rectangle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Rectangle.cpp; sourceTree = ""; }; + 4BD191E7219113B90042E144 /* ArrayBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ArrayBuilder.hpp; sourceTree = ""; }; + 4BD191F22191180E0042E144 /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = ""; }; + 4BD191F32191180E0042E144 /* ScanTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanTarget.hpp; sourceTree = ""; }; 4BD388411FE34E010042B588 /* 9918Base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 9918Base.hpp; path = 9918/Implementation/9918Base.hpp; sourceTree = ""; }; 4BD3A3091EE755C800B5B501 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = ZX8081/Video.cpp; sourceTree = ""; }; 4BD3A30A1EE755C800B5B501 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = ZX8081/Video.hpp; sourceTree = ""; }; @@ -1774,6 +1782,7 @@ 4B366DFD1B5C165F0026627B /* Outputs */ = { isa = PBXGroup; children = ( + 4BD191D5219113B80042E144 /* OpenGL */, 4BD601A920D89F2A00CBCE57 /* Log.hpp */, 4BF52672218E752E00313227 /* ScanTarget.hpp */, 4B0CCC411C62D0B3001CAC5F /* CRT */, @@ -2923,20 +2932,8 @@ 4BBF99071C8FBA6F0075DAFB /* Internals */ = { isa = PBXGroup; children = ( - 4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */, - 4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */, - 4BC891AB20F6EAB300EDE5B3 /* Rectangle.cpp */, - 4BBF99081C8FBA6F0075DAFB /* TextureBuilder.cpp */, - 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */, - 4B5073061DDD3B9400C48FBD /* ArrayBuilder.hpp */, 4B0B6E121C9DBD5D00FFB60D /* CRTConstants.hpp */, - 4BBF990B1C8FBA6F0075DAFB /* CRTOpenGL.hpp */, 4BBF990E1C8FBA6F0075DAFB /* Flywheel.hpp */, - 4BBF990F1C8FBA6F0075DAFB /* OpenGL.hpp */, - 4BC891AC20F6EAB300EDE5B3 /* Rectangle.hpp */, - 4BBF99091C8FBA6F0075DAFB /* TextureBuilder.hpp */, - 4BBF99131C8FBA6F0075DAFB /* TextureTarget.hpp */, - 4BC3B74C1CD194CC00F86E85 /* Shaders */, ); path = Internals; sourceTree = ""; @@ -2950,19 +2947,6 @@ path = "Joystick Manager"; sourceTree = ""; }; - 4BC3B74C1CD194CC00F86E85 /* Shaders */ = { - isa = PBXGroup; - children = ( - 4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */, - 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */, - 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */, - 4BBB14301CD2CECE00BDB55C /* IntermediateShader.hpp */, - 4BC3B7511CD1956900F86E85 /* OutputShader.hpp */, - 4BC3B74E1CD194CC00F86E85 /* Shader.hpp */, - ); - path = Shaders; - sourceTree = ""; - }; 4BC9DF4A1D04691600F44158 /* Components */ = { isa = PBXGroup; children = ( @@ -3036,6 +3020,41 @@ path = ../../Outputs/Speaker; sourceTree = ""; }; + 4BD191D5219113B80042E144 /* OpenGL */ = { + isa = PBXGroup; + children = ( + 4BD191D6219113B80042E144 /* ArrayBuilder.cpp */, + 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */, + 4BD191E6219113B80042E144 /* Rectangle.cpp */, + 4BD191F22191180E0042E144 /* ScanTarget.cpp */, + 4BD191D8219113B80042E144 /* TextureBuilder.cpp */, + 4BD191DA219113B80042E144 /* TextureTarget.cpp */, + 4BD191E7219113B90042E144 /* ArrayBuilder.hpp */, + 4BD191D7219113B80042E144 /* CRTOpenGL.hpp */, + 4BD191D9219113B80042E144 /* OpenGL.hpp */, + 4BD191DB219113B80042E144 /* Rectangle.hpp */, + 4BD191F32191180E0042E144 /* ScanTarget.hpp */, + 4BD191DD219113B80042E144 /* TextureBuilder.hpp */, + 4BD191DE219113B80042E144 /* TextureTarget.hpp */, + 4BD191DF219113B80042E144 /* Shaders */, + ); + name = OpenGL; + path = ../../Outputs/OpenGL; + sourceTree = ""; + }; + 4BD191DF219113B80042E144 /* Shaders */ = { + isa = PBXGroup; + children = ( + 4BD191E3219113B80042E144 /* IntermediateShader.cpp */, + 4BD191E1219113B80042E144 /* OutputShader.cpp */, + 4BD191E2219113B80042E144 /* Shader.cpp */, + 4BD191E0219113B80042E144 /* IntermediateShader.hpp */, + 4BD191E4219113B80042E144 /* OutputShader.hpp */, + 4BD191E5219113B80042E144 /* Shader.hpp */, + ); + path = Shaders; + sourceTree = ""; + }; 4BD388431FE34E060042B588 /* Implementation */ = { isa = PBXGroup; children = ( @@ -3676,8 +3695,10 @@ 4B89451B201967B4007DE474 /* ConfidenceSummary.cpp in Sources */, 4B1B88C1202E3DB200B67DFF /* MultiConfigurable.cpp in Sources */, 4B055AA31FAE85DF0060FFFF /* ImplicitSectors.cpp in Sources */, + 4BD191F1219117FA0042E144 /* Rectangle.cpp in Sources */, 4B055AAE1FAE85FD0060FFFF /* TrackSerialiser.cpp in Sources */, 4B89452B201967B4007DE474 /* File.cpp in Sources */, + 4BD191F6219118390042E144 /* Shader.cpp in Sources */, 4B055A981FAE85C50060FFFF /* Drive.cpp in Sources */, 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */, 4B055AC31FAE9AE80060FFFF /* AmstradCPC.cpp in Sources */, @@ -3761,6 +3782,7 @@ 4B894537201967B4007DE474 /* Z80.cpp in Sources */, 4B055A9F1FAE85DA0060FFFF /* HFE.cpp in Sources */, 4B07835B1FC11D42001D12BB /* Configurable.cpp in Sources */, + 4BD191F52191180E0042E144 /* ScanTarget.cpp in Sources */, 4B894523201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B055AEC1FAE9BA20060FFFF /* Z80Base.cpp in Sources */, 4B0F94FF208C1A1600FE41D9 /* NIB.cpp in Sources */, @@ -3826,11 +3848,13 @@ 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */, 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */, + 4BD191F72191183A0042E144 /* Shader.cpp in Sources */, 4B894518201967B4007DE474 /* ConfidenceCounter.cpp in Sources */, 4B89452E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */, 4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */, 4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */, + 4BD191F0219117F90042E144 /* Rectangle.cpp in Sources */, 4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */, 4BB0A65B2044FD3000FB3688 /* SN76489.cpp in Sources */, 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */, @@ -3864,6 +3888,7 @@ 4B4518851F75E91A00926311 /* DiskController.cpp in Sources */, 4B8334841F5DA0360097E338 /* Z80Storage.cpp in Sources */, 4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */, + 4BD191F42191180E0042E144 /* ScanTarget.cpp in Sources */, 4B0F94FE208C1A1600FE41D9 /* NIB.cpp in Sources */, 4B89452A201967B4007DE474 /* File.cpp in Sources */, 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index df53820fd..93c8d4d59 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -29,7 +29,7 @@ #import #include -#include "ScanTarget.hpp" +#include "../../../../Outputs/OpenGL/ScanTarget.hpp" @interface CSMachine() - (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; @@ -73,31 +73,43 @@ struct ActivityObserver: public Activity::Observer { __unsafe_unretained CSMachine *machine; }; -class ScanTarget: public Outputs::Display::ScanTarget { - public: - void set_modals(Modals m) { - modals_ = m; - } - - Scan *get_scan() { - scans_.emplace_back(); - return &scans_.back(); - } - - uint8_t *allocate_write_area(size_t required_length, size_t required_alignment) { - write_area_.resize(required_length); - return write_area_.data(); - } - - void submit(bool only_if_no_allocation_failures) { - scans_.clear(); - } - - private: - std::vector scans_; - std::vector write_area_; - Modals modals_; -}; +//class ScanTarget: public Outputs::Display::ScanTarget { +// public: +// void set_modals(Modals m) { +// modals_ = m; +// } +// +// Scan *get_scan() { +// scans_.emplace_back(); +// return &scans_.back(); +// } +// +// uint8_t *allocate_write_area(size_t required_length, size_t required_alignment) { +// write_area_.resize(required_length); +// return write_area_.data(); +// } +// +// void submit(bool only_if_no_allocation_failures) { +// for(const auto &scan: scans_) { +// printf("%0.2f %0.2f [%0.2f] -> %0.2f %0.2f [%0.2f] => %0.2f\n", +// float(scan.end_points[0].x) / float(modals_.output_scale.x), +// float(scan.end_points[0].y) / float(modals_.output_scale.y), +// float(scan.end_points[0].composite_angle) / 64.0f, +// float(scan.end_points[1].x) / float(modals_.output_scale.x), +// float(scan.end_points[1].y) / float(modals_.output_scale.y), +// float(scan.end_points[1].composite_angle) / 64.0f, +// (float(scan.end_points[1].composite_angle - scan.end_points[0].composite_angle) / 64.0f) / (float(scan.end_points[1].x - scan.end_points[0].x) / float(modals_.output_scale.x)) +// ); +// } +// +// scans_.clear(); +// } +// +// private: +// std::vector scans_; +// std::vector write_area_; +// Modals modals_; +//}; @implementation CSMachine { SpeakerDelegate _speakerDelegate; @@ -112,7 +124,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { std::bitset<65536> _depressedKeys; NSMutableArray *_leds; - ScanTarget _scanTarget; + Outputs::Display::OpenGL::ScanTarget _scanTarget; } - (instancetype)initWithAnalyser:(CSStaticAnalyser *)result { diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 1820a7c94..a2bfec632 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -65,8 +65,8 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di scan_target_->set_modals(scan_target_modals_); } -void CRT::set_new_data_type(Outputs::Display::ScanTarget::Modals::DataType data_type) { - scan_target_modals_.source_data_type = data_type; +void CRT::set_new_data_type(Outputs::Display::InputDataType data_type) { + scan_target_modals_.input_data_type = data_type; scan_target_->set_modals(scan_target_modals_); } @@ -109,10 +109,10 @@ CRT::CRT( int cycles_per_line, int colour_cycle_numerator, int colour_cycle_denominator, int vertical_sync_half_lines, bool should_alternate, - Outputs::Display::ScanTarget::Modals::DataType data_type, + Outputs::Display::InputDataType data_type, Outputs::Display::ScanTarget *scan_target) { scan_target_ = scan_target; - scan_target_modals_.source_data_type = data_type; + scan_target_modals_.input_data_type = data_type; 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); @@ -121,10 +121,10 @@ CRT::CRT( int cycles_per_line, CRT::CRT( int cycles_per_line, int clocks_per_pixel_greatest_common_divisor, Outputs::Display::Type display_type, - Outputs::Display::ScanTarget::Modals::DataType data_type, + Outputs::Display::InputDataType data_type, Outputs::Display::ScanTarget *scan_target) { scan_target_ = scan_target; - scan_target_modals_.source_data_type = data_type; + scan_target_modals_.input_data_type = data_type; 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); diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index a876c9f93..04e9f91a0 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -118,7 +118,7 @@ class CRT { int colour_cycle_denominator, int vertical_sync_half_lines, bool should_alternate, - Outputs::Display::ScanTarget::Modals::DataType data_type, + Outputs::Display::InputDataType data_type, Outputs::Display::ScanTarget *scan_target); /*! Exactly identical to calling the designated constructor with colour subcarrier information @@ -127,7 +127,7 @@ class CRT { CRT(int cycles_per_line, int minimum_cycles_per_pixel, Outputs::Display::Type display_type, - Outputs::Display::ScanTarget::Modals::DataType data_type, + Outputs::Display::InputDataType data_type, Outputs::Display::ScanTarget *scan_target); /*! Resets the CRT with new timing information. The CRT then continues as though the new timing had @@ -149,7 +149,7 @@ class CRT { /*! Changes the type of data being supplied as input. */ - void set_new_data_type(Outputs::Display::ScanTarget::Modals::DataType data_type); + void set_new_data_type(Outputs::Display::InputDataType data_type); /*! Output at the sync level. diff --git a/Outputs/CRT/Internals/ArrayBuilder.cpp b/Outputs/OpenGL/ArrayBuilder.cpp similarity index 100% rename from Outputs/CRT/Internals/ArrayBuilder.cpp rename to Outputs/OpenGL/ArrayBuilder.cpp diff --git a/Outputs/CRT/Internals/ArrayBuilder.hpp b/Outputs/OpenGL/ArrayBuilder.hpp similarity index 100% rename from Outputs/CRT/Internals/ArrayBuilder.hpp rename to Outputs/OpenGL/ArrayBuilder.hpp diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/OpenGL/CRTOpenGL.cpp similarity index 100% rename from Outputs/CRT/Internals/CRTOpenGL.cpp rename to Outputs/OpenGL/CRTOpenGL.cpp diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/OpenGL/CRTOpenGL.hpp similarity index 100% rename from Outputs/CRT/Internals/CRTOpenGL.hpp rename to Outputs/OpenGL/CRTOpenGL.hpp diff --git a/Outputs/CRT/Internals/OpenGL.hpp b/Outputs/OpenGL/OpenGL.hpp similarity index 100% rename from Outputs/CRT/Internals/OpenGL.hpp rename to Outputs/OpenGL/OpenGL.hpp diff --git a/Outputs/CRT/Internals/Rectangle.cpp b/Outputs/OpenGL/Rectangle.cpp similarity index 100% rename from Outputs/CRT/Internals/Rectangle.cpp rename to Outputs/OpenGL/Rectangle.cpp diff --git a/Outputs/CRT/Internals/Rectangle.hpp b/Outputs/OpenGL/Rectangle.hpp similarity index 100% rename from Outputs/CRT/Internals/Rectangle.hpp rename to Outputs/OpenGL/Rectangle.hpp diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp new file mode 100644 index 000000000..f3727d26f --- /dev/null +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -0,0 +1,93 @@ +// +// ScanTarget.cpp +// Clock Signal +// +// Created by Thomas Harte on 05/11/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "ScanTarget.hpp" + +using namespace Outputs::Display::OpenGL; + +namespace { + +const int WriteAreaWidth = 2048; +const int WriteAreaHeight = 2048; + +} + +void ScanTarget::set_modals(Modals modals) { + // TODO: consider resizing the write_area_texture_, and setting + // write_area_texture_line_length_ appropriately. + modals_ = modals; + + const auto data_type_size = Outputs::Display::size_for_data_type(modals.input_data_type); + if(data_type_size != data_type_size_) { + // TODO: flush output. + + data_type_size_ = data_type_size; + write_area_texture_.resize(2048*2048*data_type_size_); + write_area_x_ = 0; + write_area_pointers_.write_pointer = 0; + } +} + +Outputs::Display::ScanTarget::Scan *ScanTarget::get_scan() { + if(allocation_has_failed_) return nullptr; + + const auto result = &scan_buffer_[scan_buffer_pointers_.write_pointer]; + + // Advance the pointer. + const auto next_write_pointer = (scan_buffer_pointers_.write_pointer + 1) % scan_buffer_.size(); + + // Check whether that's too many. + if(next_write_pointer == scan_buffer_pointers_.read_pointer) { + allocation_has_failed_ = true; + return nullptr; + } + scan_buffer_pointers_.write_pointer = next_write_pointer; + + // Fill in extra OpenGL-specific details. + result->data_y = write_area_pointers_.write_pointer; + result->composite_y = 0; + + return static_cast(result); +} + +uint8_t *ScanTarget::allocate_write_area(size_t required_length, size_t required_alignment) { + if(allocation_has_failed_) return nullptr; + + // Will this fit on the current line? If so, job done. + uint16_t aligned_start = write_area_x_ + 1; + aligned_start += uint16_t((required_alignment - aligned_start%required_alignment)%required_alignment); + const uint16_t end = + aligned_start + + uint16_t(2 + required_length); + if(end <= WriteAreaWidth) { + last_supplied_x_ = aligned_start; + return &write_area_texture_[write_area_pointers_.write_pointer*WriteAreaHeight + aligned_start]; + } + + // Otherwise, look for the next line. But if that's where the read pointer is, don't proceed. + const uint16_t next_y = (write_area_pointers_.write_pointer + 1) % WriteAreaHeight; + if(next_y == write_area_pointers_.read_pointer) { + allocation_has_failed_ = true; + return nullptr; + } + + // Advance then. + last_supplied_x_ = uint16_t(required_alignment); + write_area_pointers_.write_pointer = next_y; + return &write_area_texture_[write_area_pointers_.write_pointer*WriteAreaHeight + last_supplied_x_]; +} + +void ScanTarget::reduce_previous_allocation_to(size_t actual_length) { + if(allocation_has_failed_) return; + + write_area_x_ = 2 + uint16_t(actual_length) + last_supplied_x_; +} + +void ScanTarget::submit() { + // TODO. +} diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp new file mode 100644 index 000000000..d2fa2eff6 --- /dev/null +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -0,0 +1,71 @@ +// +// ScanTarget.hpp +// Clock Signal +// +// Created by Thomas Harte on 05/11/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef ScanTarget_hpp +#define ScanTarget_hpp + +#include "../ScanTarget.hpp" + +#include +#include +#include +#include + +namespace Outputs { +namespace Display { +namespace OpenGL { + +class ScanTarget: public Outputs::Display::ScanTarget { + public: + 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; + void submit() override; + + private: + // Extends the definition of a Scan to include two extra fields, + // relevant to the way that this scan target processes video. + struct Scan: public Outputs::Display::ScanTarget::Scan { + /// Stores the y coordinate that this scan's data is at, within the write area texture. + uint16_t data_y; + /// Stores the y coordinate of this continuous composite segment within the conversion buffer. + uint16_t composite_y; + }; + + template struct PointerSet { + /// A pointer to the final thing currently cleared for submission. + T submit_pointer; + /// A pointer to the next thing that should be provided to the caller for data. + T write_pointer; + /// A pointer to the first thing not yet submitted for display. + std::atomic read_pointer; + }; + + // Maintains a buffer of the most recent 3072 scans. + std::array scan_buffer_; + PointerSet scan_buffer_pointers_; + + // Uses a texture to vend write areas. + std::vector write_area_texture_; + size_t data_type_size_ = 0; + uint16_t write_area_x_ = 0, last_supplied_x_ = 0; + PointerSet write_area_pointers_; + + // Track allocation failures. + bool allocation_has_failed_ = false; + + // Receives scan target modals. + Modals modals_; +}; + +} +} +} + +#endif /* ScanTarget_hpp */ diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/OpenGL/Shaders/IntermediateShader.cpp similarity index 99% rename from Outputs/CRT/Internals/Shaders/IntermediateShader.cpp rename to Outputs/OpenGL/Shaders/IntermediateShader.cpp index 2442e671e..af90e0b43 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp +++ b/Outputs/OpenGL/Shaders/IntermediateShader.cpp @@ -12,7 +12,7 @@ #include #include -#include "../../../../SignalProcessing/FIRFilter.hpp" +#include "../../../SignalProcessing/FIRFilter.hpp" using namespace OpenGL; diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp b/Outputs/OpenGL/Shaders/IntermediateShader.hpp similarity index 100% rename from Outputs/CRT/Internals/Shaders/IntermediateShader.hpp rename to Outputs/OpenGL/Shaders/IntermediateShader.hpp diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.cpp b/Outputs/OpenGL/Shaders/OutputShader.cpp similarity index 100% rename from Outputs/CRT/Internals/Shaders/OutputShader.cpp rename to Outputs/OpenGL/Shaders/OutputShader.cpp diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.hpp b/Outputs/OpenGL/Shaders/OutputShader.hpp similarity index 100% rename from Outputs/CRT/Internals/Shaders/OutputShader.hpp rename to Outputs/OpenGL/Shaders/OutputShader.hpp diff --git a/Outputs/CRT/Internals/Shaders/Shader.cpp b/Outputs/OpenGL/Shaders/Shader.cpp similarity index 100% rename from Outputs/CRT/Internals/Shaders/Shader.cpp rename to Outputs/OpenGL/Shaders/Shader.cpp diff --git a/Outputs/CRT/Internals/Shaders/Shader.hpp b/Outputs/OpenGL/Shaders/Shader.hpp similarity index 100% rename from Outputs/CRT/Internals/Shaders/Shader.hpp rename to Outputs/OpenGL/Shaders/Shader.hpp diff --git a/Outputs/CRT/Internals/TextureBuilder.cpp b/Outputs/OpenGL/TextureBuilder.cpp similarity index 100% rename from Outputs/CRT/Internals/TextureBuilder.cpp rename to Outputs/OpenGL/TextureBuilder.cpp diff --git a/Outputs/CRT/Internals/TextureBuilder.hpp b/Outputs/OpenGL/TextureBuilder.hpp similarity index 100% rename from Outputs/CRT/Internals/TextureBuilder.hpp rename to Outputs/OpenGL/TextureBuilder.hpp diff --git a/Outputs/CRT/Internals/TextureTarget.cpp b/Outputs/OpenGL/TextureTarget.cpp similarity index 100% rename from Outputs/CRT/Internals/TextureTarget.cpp rename to Outputs/OpenGL/TextureTarget.cpp diff --git a/Outputs/CRT/Internals/TextureTarget.hpp b/Outputs/OpenGL/TextureTarget.hpp similarity index 100% rename from Outputs/CRT/Internals/TextureTarget.hpp rename to Outputs/OpenGL/TextureTarget.hpp diff --git a/Outputs/ScanTarget.hpp b/Outputs/ScanTarget.hpp index 7cdbeb5eb..fe30471f4 100644 --- a/Outputs/ScanTarget.hpp +++ b/Outputs/ScanTarget.hpp @@ -10,6 +10,7 @@ #define Outputs_Display_ScanTarget_h #include +#include namespace Outputs { namespace Display { @@ -47,6 +48,52 @@ enum class ColourSpace { YUV }; +/*! + Enumerates the potential formats of input data. +*/ +enum class InputDataType { + + // The luminance types can be used to feed only two video pipelines: + // black and white video, or composite colour. + + Luminance1, // 1 byte/pixel; any bit set => white; no bits set => black. + Luminance8, // 1 byte/pixel; linear scale. + + // The luminance plus phase types describe a luminance and the phase offset + // of a colour subcarrier. So they can be used to generate a luminance signal, + // or an s-video pipeline. + + 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. + + Red1Green1Blue1, // 1 byte/pixel; bit 0 is blue on or off, bit 1 is green, bit 2 is red. + Red2Green2Blue2, // 1 byte/pixel; bits 0 and 1 are blue, bits 2 and 3 are green, bits 4 and 5 are blue. + Red4Green4Blue4, // 2 bytes/pixel; first nibble is red, second is green, third is blue. + Red8Green8Blue8, // 4 bytes/pixel; first is red, second is green, third is blue, fourth is vacant. +}; + +inline size_t size_for_data_type(InputDataType data_type) { + switch(data_type) { + case InputDataType::Luminance1: + case InputDataType::Luminance8: + case InputDataType::Red1Green1Blue1: + case InputDataType::Red2Green2Blue2: + return 1; + + case InputDataType::Phase8Luminance8: + case InputDataType::Red4Green4Blue4: + return 2; + + case InputDataType::Red8Green8Blue8: + return 4; + } +} + /*! Provides an abstract target for 'scans' i.e. continuous sweeps of output data, which are identified by 2d start and end coordinates, and the PCM-sampled data @@ -66,34 +113,8 @@ struct ScanTarget { */ struct Modals { - /*! - Enumerates the potential formats of input data. - */ - enum class DataType { - - // The luminance types can be used to feed only two video pipelines: - // black and white video, or composite colour. - - Luminance1, // 1 byte/pixel; any bit set => white; no bits set => black. - Luminance8, // 1 byte/pixel; linear scale. - - // The luminance plus phase types describe a luminance and the phase offset - // of a colour subcarrier. So they can be used to generate a luminance signal, - // or an s-video pipeline. - - 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. - - Red1Green1Blue1, // 1 byte/pixel; bit 0 is blue on or off, bit 1 is green, bit 2 is red. - Red2Green2Blue2, // 1 byte/pixel; bits 0 and 1 are blue, bits 2 and 3 are green, bits 4 and 5 are blue. - Red4Green4Blue4, // 2 bytes/pixel; first nibble is red, second is green, third is blue. - Red8Green8Blue8, // 4 bytes/pixel; first is red, second is green, third is blue, fourth is vacant. - } source_data_type; + /// Describes the format of input data. + InputDataType input_data_type; /// If being fed composite data, this defines the colour space in use. ColourSpace composite_colour_space; @@ -187,19 +208,22 @@ struct ScanTarget { /// Announces that the owner is finished with the region created by the most recent @c allocate_write_area /// and indicates that its actual final size was @c actual_length. - virtual void reduce_previous_allocation_to(size_t 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) {} - /// Announces that all endpoint pairs and write areas obtained since the last @c submit have now been - /// populated with appropriate data. + /// 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) + /// for any other reason. /// /// 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(bool only_if_no_allocation_failures = true) = 0; - - /// Announces that any submitted data not yet output should be output now, but needn't block while - /// doing so. This generally communicates that processing is now otherwise 'up to date', so no - /// further delay should be allowed. -// virtual void flush() = 0; + virtual void submit() = 0; /* From 7881e40e0b6b88fc20b86eecd1fbb6654b32e52e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Nov 2018 19:11:01 -0500 Subject: [PATCH 013/208] Shuffles the OpenGL primitives into their own collection. --- .../Clock Signal.xcodeproj/project.pbxproj | 52 ++++++++++++------- Outputs/OpenGL/{ => Primitives}/Rectangle.cpp | 0 Outputs/OpenGL/{ => Primitives}/Rectangle.hpp | 2 +- .../OpenGL/{Shaders => Primitives}/Shader.cpp | 0 .../OpenGL/{Shaders => Primitives}/Shader.hpp | 0 .../OpenGL/{ => Primitives}/TextureTarget.cpp | 0 .../OpenGL/{ => Primitives}/TextureTarget.hpp | 2 +- 7 files changed, 34 insertions(+), 22 deletions(-) rename Outputs/OpenGL/{ => Primitives}/Rectangle.cpp (100%) rename Outputs/OpenGL/{ => Primitives}/Rectangle.hpp (96%) rename Outputs/OpenGL/{Shaders => Primitives}/Shader.cpp (100%) rename Outputs/OpenGL/{Shaders => Primitives}/Shader.hpp (100%) rename Outputs/OpenGL/{ => Primitives}/TextureTarget.cpp (100%) rename Outputs/OpenGL/{ => Primitives}/TextureTarget.hpp (98%) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index d584a56c6..4600e3c2b 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -613,13 +613,15 @@ 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; }; 4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCA6CC61D9DD9F000C2D7B2 /* CommodoreROM.cpp */; }; 4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */; }; - 4BD191F0219117F90042E144 /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191E6219113B80042E144 /* Rectangle.cpp */; }; - 4BD191F1219117FA0042E144 /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191E6219113B80042E144 /* Rectangle.cpp */; }; 4BD191F42191180E0042E144 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191F22191180E0042E144 /* ScanTarget.cpp */; }; 4BD191F52191180E0042E144 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191F22191180E0042E144 /* ScanTarget.cpp */; }; - 4BD191F6219118390042E144 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191E2219113B80042E144 /* Shader.cpp */; }; - 4BD191F72191183A0042E144 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191E2219113B80042E144 /* Shader.cpp */; }; 4BD3A30B1EE755C800B5B501 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD3A3091EE755C800B5B501 /* Video.cpp */; }; + 4BD424DF2193B5340097291A /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424DD2193B5340097291A /* TextureTarget.cpp */; }; + 4BD424E02193B5340097291A /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424DD2193B5340097291A /* TextureTarget.cpp */; }; + 4BD424E52193B5830097291A /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424E12193B5820097291A /* Shader.cpp */; }; + 4BD424E62193B5830097291A /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424E12193B5820097291A /* Shader.cpp */; }; + 4BD424E72193B5830097291A /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424E22193B5820097291A /* Rectangle.cpp */; }; + 4BD424E82193B5830097291A /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424E22193B5820097291A /* Rectangle.cpp */; }; 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD468F51D8DF41D0084958B /* 1770.cpp */; }; 4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */; }; @@ -1365,24 +1367,24 @@ 4BD191D7219113B80042E144 /* CRTOpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTOpenGL.hpp; sourceTree = ""; }; 4BD191D8219113B80042E144 /* TextureBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureBuilder.cpp; sourceTree = ""; }; 4BD191D9219113B80042E144 /* OpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OpenGL.hpp; sourceTree = ""; }; - 4BD191DA219113B80042E144 /* TextureTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureTarget.cpp; sourceTree = ""; }; - 4BD191DB219113B80042E144 /* Rectangle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Rectangle.hpp; sourceTree = ""; }; 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTOpenGL.cpp; sourceTree = ""; }; 4BD191DD219113B80042E144 /* TextureBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureBuilder.hpp; sourceTree = ""; }; - 4BD191DE219113B80042E144 /* TextureTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureTarget.hpp; sourceTree = ""; }; 4BD191E0219113B80042E144 /* IntermediateShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IntermediateShader.hpp; sourceTree = ""; }; 4BD191E1219113B80042E144 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; - 4BD191E2219113B80042E144 /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = ""; }; 4BD191E3219113B80042E144 /* IntermediateShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntermediateShader.cpp; sourceTree = ""; }; 4BD191E4219113B80042E144 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = ""; }; - 4BD191E5219113B80042E144 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = ""; }; - 4BD191E6219113B80042E144 /* Rectangle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Rectangle.cpp; sourceTree = ""; }; 4BD191E7219113B90042E144 /* ArrayBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ArrayBuilder.hpp; sourceTree = ""; }; 4BD191F22191180E0042E144 /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = ""; }; 4BD191F32191180E0042E144 /* ScanTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanTarget.hpp; sourceTree = ""; }; 4BD388411FE34E010042B588 /* 9918Base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 9918Base.hpp; path = 9918/Implementation/9918Base.hpp; sourceTree = ""; }; 4BD3A3091EE755C800B5B501 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = ZX8081/Video.cpp; sourceTree = ""; }; 4BD3A30A1EE755C800B5B501 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = ZX8081/Video.hpp; sourceTree = ""; }; + 4BD424DD2193B5340097291A /* TextureTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureTarget.cpp; sourceTree = ""; }; + 4BD424DE2193B5340097291A /* TextureTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureTarget.hpp; sourceTree = ""; }; + 4BD424E12193B5820097291A /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = ""; }; + 4BD424E22193B5820097291A /* Rectangle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Rectangle.cpp; sourceTree = ""; }; + 4BD424E32193B5830097291A /* Rectangle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Rectangle.hpp; sourceTree = ""; }; + 4BD424E42193B5830097291A /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = ""; }; 4BD468F51D8DF41D0084958B /* 1770.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 1770.cpp; path = 1770/1770.cpp; sourceTree = ""; }; 4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = ""; }; 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMTrackTests.mm; sourceTree = ""; }; @@ -3025,17 +3027,14 @@ children = ( 4BD191D6219113B80042E144 /* ArrayBuilder.cpp */, 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */, - 4BD191E6219113B80042E144 /* Rectangle.cpp */, 4BD191F22191180E0042E144 /* ScanTarget.cpp */, 4BD191D8219113B80042E144 /* TextureBuilder.cpp */, - 4BD191DA219113B80042E144 /* TextureTarget.cpp */, 4BD191E7219113B90042E144 /* ArrayBuilder.hpp */, 4BD191D7219113B80042E144 /* CRTOpenGL.hpp */, 4BD191D9219113B80042E144 /* OpenGL.hpp */, - 4BD191DB219113B80042E144 /* Rectangle.hpp */, 4BD191F32191180E0042E144 /* ScanTarget.hpp */, 4BD191DD219113B80042E144 /* TextureBuilder.hpp */, - 4BD191DE219113B80042E144 /* TextureTarget.hpp */, + 4BD424DC2193B5340097291A /* Primitives */, 4BD191DF219113B80042E144 /* Shaders */, ); name = OpenGL; @@ -3047,10 +3046,8 @@ children = ( 4BD191E3219113B80042E144 /* IntermediateShader.cpp */, 4BD191E1219113B80042E144 /* OutputShader.cpp */, - 4BD191E2219113B80042E144 /* Shader.cpp */, 4BD191E0219113B80042E144 /* IntermediateShader.hpp */, 4BD191E4219113B80042E144 /* OutputShader.hpp */, - 4BD191E5219113B80042E144 /* Shader.hpp */, ); path = Shaders; sourceTree = ""; @@ -3063,6 +3060,19 @@ name = Implementation; sourceTree = ""; }; + 4BD424DC2193B5340097291A /* Primitives */ = { + isa = PBXGroup; + children = ( + 4BD424E22193B5820097291A /* Rectangle.cpp */, + 4BD424E12193B5820097291A /* Shader.cpp */, + 4BD424DD2193B5340097291A /* TextureTarget.cpp */, + 4BD424E32193B5830097291A /* Rectangle.hpp */, + 4BD424E42193B5830097291A /* Shader.hpp */, + 4BD424DE2193B5340097291A /* TextureTarget.hpp */, + ); + path = Primitives; + sourceTree = ""; + }; 4BD468F81D8DF4290084958B /* 1770 */ = { isa = PBXGroup; children = ( @@ -3695,11 +3705,10 @@ 4B89451B201967B4007DE474 /* ConfidenceSummary.cpp in Sources */, 4B1B88C1202E3DB200B67DFF /* MultiConfigurable.cpp in Sources */, 4B055AA31FAE85DF0060FFFF /* ImplicitSectors.cpp in Sources */, - 4BD191F1219117FA0042E144 /* Rectangle.cpp in Sources */, 4B055AAE1FAE85FD0060FFFF /* TrackSerialiser.cpp in Sources */, 4B89452B201967B4007DE474 /* File.cpp in Sources */, - 4BD191F6219118390042E144 /* Shader.cpp in Sources */, 4B055A981FAE85C50060FFFF /* Drive.cpp in Sources */, + 4BD424E62193B5830097291A /* Shader.cpp in Sources */, 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */, 4B055AC31FAE9AE80060FFFF /* AmstradCPC.cpp in Sources */, 4B055A9E1FAE85DA0060FFFF /* G64.cpp in Sources */, @@ -3766,11 +3775,13 @@ 4B055ACD1FAE9B030060FFFF /* Keyboard.cpp in Sources */, 4B055AB21FAE860F0060FFFF /* CommodoreTAP.cpp in Sources */, 4B055ADF1FAE9B4C0060FFFF /* IRQDelegatePortHandler.cpp in Sources */, + 4BD424E02193B5340097291A /* TextureTarget.cpp in Sources */, 4B055AB51FAE860F0060FFFF /* TapePRG.cpp in Sources */, 4B055AE01FAE9B660060FFFF /* CRT.cpp in Sources */, 4B894527201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4BAF2B4F2004580C00480230 /* DMK.cpp in Sources */, 4B055AD01FAE9B030060FFFF /* Tape.cpp in Sources */, + 4BD424E82193B5830097291A /* Rectangle.cpp in Sources */, 4B055A961FAE85BB0060FFFF /* Commodore.cpp in Sources */, 4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */, 4B055ADE1FAE9B4C0060FFFF /* 6522Base.cpp in Sources */, @@ -3820,6 +3831,7 @@ 4B448E841F1C4C480009ABD6 /* PulseQueuedTape.cpp in Sources */, 4B0E61071FF34737002A9DBD /* MSX.cpp in Sources */, 4B4518A01F75FD1C00926311 /* CPCDSK.cpp in Sources */, + 4BD424DF2193B5340097291A /* TextureTarget.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B322E041F5A2E3C004EB04C /* Z80Base.cpp in Sources */, 4B894530201967B4007DE474 /* StaticAnalyser.cpp in Sources */, @@ -3847,14 +3859,13 @@ 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */, + 4BD424E52193B5830097291A /* Shader.cpp in Sources */, 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */, - 4BD191F72191183A0042E144 /* Shader.cpp in Sources */, 4B894518201967B4007DE474 /* ConfidenceCounter.cpp in Sources */, 4B89452E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */, 4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */, 4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */, - 4BD191F0219117F90042E144 /* Rectangle.cpp in Sources */, 4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */, 4BB0A65B2044FD3000FB3688 /* SN76489.cpp in Sources */, 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */, @@ -3964,6 +3975,7 @@ 4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */, 4B8334821F5D9FF70097E338 /* PartialMachineCycle.cpp in Sources */, + 4BD424E72193B5830097291A /* Rectangle.cpp in Sources */, 4B1B88C0202E3DB200B67DFF /* MultiConfigurable.cpp in Sources */, 4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */, 4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */, diff --git a/Outputs/OpenGL/Rectangle.cpp b/Outputs/OpenGL/Primitives/Rectangle.cpp similarity index 100% rename from Outputs/OpenGL/Rectangle.cpp rename to Outputs/OpenGL/Primitives/Rectangle.cpp diff --git a/Outputs/OpenGL/Rectangle.hpp b/Outputs/OpenGL/Primitives/Rectangle.hpp similarity index 96% rename from Outputs/OpenGL/Rectangle.hpp rename to Outputs/OpenGL/Primitives/Rectangle.hpp index b98e9a4df..07a5152d5 100644 --- a/Outputs/OpenGL/Rectangle.hpp +++ b/Outputs/OpenGL/Primitives/Rectangle.hpp @@ -10,7 +10,7 @@ #define Rectangle_hpp #include "OpenGL.hpp" -#include "Shaders/Shader.hpp" +#include "Shader.hpp" #include namespace OpenGL { diff --git a/Outputs/OpenGL/Shaders/Shader.cpp b/Outputs/OpenGL/Primitives/Shader.cpp similarity index 100% rename from Outputs/OpenGL/Shaders/Shader.cpp rename to Outputs/OpenGL/Primitives/Shader.cpp diff --git a/Outputs/OpenGL/Shaders/Shader.hpp b/Outputs/OpenGL/Primitives/Shader.hpp similarity index 100% rename from Outputs/OpenGL/Shaders/Shader.hpp rename to Outputs/OpenGL/Primitives/Shader.hpp diff --git a/Outputs/OpenGL/TextureTarget.cpp b/Outputs/OpenGL/Primitives/TextureTarget.cpp similarity index 100% rename from Outputs/OpenGL/TextureTarget.cpp rename to Outputs/OpenGL/Primitives/TextureTarget.cpp diff --git a/Outputs/OpenGL/TextureTarget.hpp b/Outputs/OpenGL/Primitives/TextureTarget.hpp similarity index 98% rename from Outputs/OpenGL/TextureTarget.hpp rename to Outputs/OpenGL/Primitives/TextureTarget.hpp index 8d05dd1bc..c6b9b14f5 100644 --- a/Outputs/OpenGL/TextureTarget.hpp +++ b/Outputs/OpenGL/Primitives/TextureTarget.hpp @@ -10,7 +10,7 @@ #define TextureTarget_hpp #include "OpenGL.hpp" -#include "Shaders/Shader.hpp" +#include "Shader.hpp" #include namespace OpenGL { From 36bf640c6fbcbc4dec95190d44cf397555a26727 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Nov 2018 22:53:46 -0500 Subject: [PATCH 014/208] Acts as if it is going to submit scans, at least. --- .../Mac/Clock Signal/Machine/CSMachine.mm | 44 ++----------------- Outputs/OpenGL/ScanTarget.cpp | 36 ++++++++++++++- Outputs/OpenGL/ScanTarget.hpp | 11 ++++- 3 files changed, 48 insertions(+), 43 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 93c8d4d59..5a26574e3 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -73,44 +73,6 @@ struct ActivityObserver: public Activity::Observer { __unsafe_unretained CSMachine *machine; }; -//class ScanTarget: public Outputs::Display::ScanTarget { -// public: -// void set_modals(Modals m) { -// modals_ = m; -// } -// -// Scan *get_scan() { -// scans_.emplace_back(); -// return &scans_.back(); -// } -// -// uint8_t *allocate_write_area(size_t required_length, size_t required_alignment) { -// write_area_.resize(required_length); -// return write_area_.data(); -// } -// -// void submit(bool only_if_no_allocation_failures) { -// for(const auto &scan: scans_) { -// printf("%0.2f %0.2f [%0.2f] -> %0.2f %0.2f [%0.2f] => %0.2f\n", -// float(scan.end_points[0].x) / float(modals_.output_scale.x), -// float(scan.end_points[0].y) / float(modals_.output_scale.y), -// float(scan.end_points[0].composite_angle) / 64.0f, -// float(scan.end_points[1].x) / float(modals_.output_scale.x), -// float(scan.end_points[1].y) / float(modals_.output_scale.y), -// float(scan.end_points[1].composite_angle) / 64.0f, -// (float(scan.end_points[1].composite_angle - scan.end_points[0].composite_angle) / 64.0f) / (float(scan.end_points[1].x - scan.end_points[0].x) / float(modals_.output_scale.x)) -// ); -// } -// -// scans_.clear(); -// } -// -// private: -// std::vector scans_; -// std::vector write_area_; -// Modals modals_; -//}; - @implementation CSMachine { SpeakerDelegate _speakerDelegate; ActivityObserver _activityObserver; @@ -124,7 +86,7 @@ struct ActivityObserver: public Activity::Observer { std::bitset<65536> _depressedKeys; NSMutableArray *_leds; - Outputs::Display::OpenGL::ScanTarget _scanTarget; + std::unique_ptr _scanTarget; } - (instancetype)initWithAnalyser:(CSStaticAnalyser *)result { @@ -273,7 +235,8 @@ struct ActivityObserver: public Activity::Observer { } - (void)setupOutputWithAspectRatio:(float)aspectRatio { - _machine->crt_machine()->setup_output(&_scanTarget); + _scanTarget.reset(new Outputs::Display::OpenGL::ScanTarget); + _machine->crt_machine()->setup_output(_scanTarget.get()); // Since OS X v10.6, Macs have had a gamma of 2.2. // _machine->crt_machine()->get_crt()->set_output_gamma(2.2f); @@ -281,6 +244,7 @@ struct ActivityObserver: public Activity::Observer { } - (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty { + _scanTarget->draw(); // _machine->crt_machine()->get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); } diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index f3727d26f..9bb8487d5 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -7,6 +7,7 @@ // #include "ScanTarget.hpp" +#include "Primitives/Rectangle.hpp" using namespace Outputs::Display::OpenGL; @@ -17,6 +18,19 @@ const int WriteAreaHeight = 2048; } +ScanTarget::ScanTarget() { + // Allocate space for the spans. + glGenBuffers(1, &scan_buffer_name_); + glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); + const auto buffer_size = scan_buffer_.size() * sizeof(Scan); + glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(buffer_size), NULL, GL_STREAM_DRAW); +} + +ScanTarget::~ScanTarget() { + // Release span space. + glDeleteBuffers(1, &scan_buffer_name_); +} + void ScanTarget::set_modals(Modals modals) { // TODO: consider resizing the write_area_texture_, and setting // write_area_texture_line_length_ appropriately. @@ -89,5 +103,25 @@ void ScanTarget::reduce_previous_allocation_to(size_t actual_length) { } void ScanTarget::submit() { - // TODO. + if(allocation_has_failed_) { + // Reset all pointers to where they were. + scan_buffer_pointers_.write_pointer = scan_buffer_pointers_.submit_pointer; + } else { + // Advance submit pointer. + scan_buffer_pointers_.submit_pointer = scan_buffer_pointers_.write_pointer; + } + + allocation_has_failed_ = false; +} + +void ScanTarget::draw() { + // Submit spans. + if(scan_buffer_pointers_.submit_pointer != scan_buffer_pointers_.read_pointer) { + // TODO: submit all scans from scan_buffer_pointers_.read_pointer to scan_buffer_pointers_.submit_pointer. + scan_buffer_pointers_.read_pointer = scan_buffer_pointers_.submit_pointer; + } + + glClear(GL_COLOR_BUFFER_BIT); + ::OpenGL::Rectangle rect(-0.8f, -0.8f, 1.6f, 1.6f); + rect.draw(1, 1, 0); } diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index d2fa2eff6..6011c6397 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -10,6 +10,7 @@ #define ScanTarget_hpp #include "../ScanTarget.hpp" +#include "OpenGL.hpp" #include #include @@ -22,6 +23,11 @@ namespace OpenGL { class ScanTarget: public Outputs::Display::ScanTarget { public: + ScanTarget(); + ~ScanTarget(); + void draw(); + + // 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; @@ -39,10 +45,10 @@ class ScanTarget: public Outputs::Display::ScanTarget { }; template struct PointerSet { - /// A pointer to the final thing currently cleared for submission. - T submit_pointer; /// A pointer to the next thing that should be provided to the caller for data. T write_pointer; + /// A pointer to the final thing currently cleared for submission. + T submit_pointer; /// A pointer to the first thing not yet submitted for display. std::atomic read_pointer; }; @@ -50,6 +56,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { // Maintains a buffer of the most recent 3072 scans. std::array scan_buffer_; PointerSet scan_buffer_pointers_; + GLuint scan_buffer_name_ = 0; // Uses a texture to vend write areas. std::vector write_area_texture_; From 4fe5c7c24ed9f7f66b35a56f1d642b2812562643 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Nov 2018 21:57:28 -0500 Subject: [PATCH 015/208] Conspires to handle multithreading side of things in a lockless fashion. At least on x86-64. --- Outputs/OpenGL/ScanTarget.cpp | 95 +++++++++++++++++++++++------------ Outputs/OpenGL/ScanTarget.hpp | 27 ++++++---- 2 files changed, 80 insertions(+), 42 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 9bb8487d5..ca4db5ee1 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -13,8 +13,13 @@ using namespace Outputs::Display::OpenGL; namespace { -const int WriteAreaWidth = 2048; -const int WriteAreaHeight = 2048; +constexpr int WriteAreaWidth = 2048; +constexpr int WriteAreaHeight = 2048; + +#define TextureAddress(x, y) (((x) << 11) | (y)) +#define TextureAddressGetX(v) ((v) >> 11) +#define TextureAddressGetY(v) ((v) & 0x7ff) +#define TextureSub(x, y) (((x) - (y)) & 0x7ff) } @@ -42,28 +47,30 @@ void ScanTarget::set_modals(Modals modals) { data_type_size_ = data_type_size; write_area_texture_.resize(2048*2048*data_type_size_); - write_area_x_ = 0; - write_area_pointers_.write_pointer = 0; + + write_pointers_.scan_buffer = 0; + write_pointers_.write_area = 0; } } Outputs::Display::ScanTarget::Scan *ScanTarget::get_scan() { if(allocation_has_failed_) return nullptr; - const auto result = &scan_buffer_[scan_buffer_pointers_.write_pointer]; + const auto result = &scan_buffer_[write_pointers_.scan_buffer]; + const auto read_pointers = read_pointers_.load(); // Advance the pointer. - const auto next_write_pointer = (scan_buffer_pointers_.write_pointer + 1) % scan_buffer_.size(); + const auto next_write_pointer = decltype(write_pointers_.scan_buffer)((write_pointers_.scan_buffer + 1) % scan_buffer_.size()); // Check whether that's too many. - if(next_write_pointer == scan_buffer_pointers_.read_pointer) { + if(next_write_pointer == read_pointers.scan_buffer) { allocation_has_failed_ = true; return nullptr; } - scan_buffer_pointers_.write_pointer = next_write_pointer; + write_pointers_.scan_buffer = next_write_pointer; // Fill in extra OpenGL-specific details. - result->data_y = write_area_pointers_.write_pointer; +// result->data_y = write_pointers_.write_area; result->composite_y = 0; return static_cast(result); @@ -72,56 +79,80 @@ Outputs::Display::ScanTarget::Scan *ScanTarget::get_scan() { uint8_t *ScanTarget::allocate_write_area(size_t required_length, size_t required_alignment) { if(allocation_has_failed_) return nullptr; - // Will this fit on the current line? If so, job done. - uint16_t aligned_start = write_area_x_ + 1; - aligned_start += uint16_t((required_alignment - aligned_start%required_alignment)%required_alignment); - const uint16_t end = - aligned_start - + uint16_t(2 + required_length); - if(end <= WriteAreaWidth) { - last_supplied_x_ = aligned_start; - return &write_area_texture_[write_area_pointers_.write_pointer*WriteAreaHeight + aligned_start]; + // Determine where the proposed write area would start and end. + uint16_t output_y = TextureAddressGetY(write_pointers_.write_area); + + uint16_t aligned_start_x = TextureAddressGetX(write_pointers_.write_area & 0xffff) + 1; + aligned_start_x += uint16_t((required_alignment - aligned_start_x%required_alignment)%required_alignment); + + uint16_t end_x = aligned_start_x + uint16_t(1 + required_length); + + if(end_x > WriteAreaWidth) { + output_y = (output_y + 1) % WriteAreaHeight; + aligned_start_x = uint16_t(required_alignment); + end_x = aligned_start_x + uint16_t(1 + required_length); } - // Otherwise, look for the next line. But if that's where the read pointer is, don't proceed. - const uint16_t next_y = (write_area_pointers_.write_pointer + 1) % WriteAreaHeight; - if(next_y == write_area_pointers_.read_pointer) { + // Check whether that steps over the read pointer. + const auto new_address = TextureAddress(end_x, output_y); + const auto read_pointers = read_pointers_.load(); + + const auto new_distance = TextureSub(read_pointers.write_area, new_address); + const auto previous_distance = TextureSub(read_pointers.write_area, write_pointers_.write_area); + + // If allocating this would somehow make the write pointer further away from the read pointer, + // there must not be enough space left. + if(new_distance > previous_distance) { allocation_has_failed_ = true; return nullptr; } - // Advance then. - last_supplied_x_ = uint16_t(required_alignment); - write_area_pointers_.write_pointer = next_y; - return &write_area_texture_[write_area_pointers_.write_pointer*WriteAreaHeight + last_supplied_x_]; + // Everything checks out, return the pointer. + last_supplied_x_ = aligned_start_x; + return &write_area_texture_[size_t(new_address) * data_type_size_]; } void ScanTarget::reduce_previous_allocation_to(size_t actual_length) { if(allocation_has_failed_) return; - write_area_x_ = 2 + uint16_t(actual_length) + last_supplied_x_; + // The span was allocated in the knowledge that there's sufficient distance + // left on the current line, so there's no need to worry about carry. + write_pointers_.write_area += actual_length + 1; } void ScanTarget::submit() { if(allocation_has_failed_) { // Reset all pointers to where they were. - scan_buffer_pointers_.write_pointer = scan_buffer_pointers_.submit_pointer; + write_pointers_ = submit_pointers_.load(); } else { // Advance submit pointer. - scan_buffer_pointers_.submit_pointer = scan_buffer_pointers_.write_pointer; + submit_pointers_.store(write_pointers_); } allocation_has_failed_ = false; } void ScanTarget::draw() { + // Grab the current read and submit pointers. + const auto submit_pointers = submit_pointers_.load(); + const auto read_pointers = read_pointers_.load(); + // Submit spans. - if(scan_buffer_pointers_.submit_pointer != scan_buffer_pointers_.read_pointer) { + if(submit_pointers.scan_buffer != read_pointers.scan_buffer) { + +// uint8_t *destination = static_cast(glMapBufferRange(GL_ARRAY_BUFFER, 0, (GLsizeiptr)length, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT)); +// if(!glGetError() && destination) { +// } + // TODO: submit all scans from scan_buffer_pointers_.read_pointer to scan_buffer_pointers_.submit_pointer. - scan_buffer_pointers_.read_pointer = scan_buffer_pointers_.submit_pointer; +// read_pointers_.scan_buffer = submit_pointers.scan_buffer; } + // All data now having been spooled to the GPU, update the read pointers to + // the submit pointer location. + read_pointers_.store(submit_pointers); + glClear(GL_COLOR_BUFFER_BIT); - ::OpenGL::Rectangle rect(-0.8f, -0.8f, 1.6f, 1.6f); - rect.draw(1, 1, 0); +// ::OpenGL::Rectangle rect(-0.8f, -0.8f, 1.6f, 1.6f); +// rect.draw(1, 1, 0); } diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 6011c6397..df456ad10 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -44,25 +44,32 @@ class ScanTarget: public Outputs::Display::ScanTarget { uint16_t composite_y; }; - template struct PointerSet { - /// A pointer to the next thing that should be provided to the caller for data. - T write_pointer; - /// A pointer to the final thing currently cleared for submission. - T submit_pointer; - /// A pointer to the first thing not yet submitted for display. - std::atomic read_pointer; + struct PointerSet { + // The sizes below might be less hassle as something more natural like ints, + // but squeezing this struct into 64 bits makes the std::atomics more likely + // to be lock free; they are under LLVM x86-64. + int write_area; + uint16_t scan_buffer; + uint16_t composite_y; }; + /// A pointer to the next thing that should be provided to the caller for data. + PointerSet write_pointers_; + + /// A pointer to the final thing currently cleared for submission. + std::atomic submit_pointers_; + + /// A pointer to the first thing not yet submitted for display. + std::atomic read_pointers_; + // Maintains a buffer of the most recent 3072 scans. std::array scan_buffer_; - PointerSet scan_buffer_pointers_; GLuint scan_buffer_name_ = 0; // Uses a texture to vend write areas. std::vector write_area_texture_; size_t data_type_size_ = 0; - uint16_t write_area_x_ = 0, last_supplied_x_ = 0; - PointerSet write_area_pointers_; + uint16_t last_supplied_x_ = 0; // Track allocation failures. bool allocation_has_failed_ = false; From 20faf4e477410de97c59fdccebb284f9c391f9f1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Nov 2018 22:21:11 -0500 Subject: [PATCH 016/208] Adds submission of scans to the GPU. --- Outputs/OpenGL/ScanTarget.cpp | 51 ++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index ca4db5ee1..286a8499e 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -24,15 +24,20 @@ constexpr int WriteAreaHeight = 2048; } ScanTarget::ScanTarget() { - // Allocate space for the spans. + // Allocate space for the scans. glGenBuffers(1, &scan_buffer_name_); glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); + const auto buffer_size = scan_buffer_.size() * sizeof(Scan); glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(buffer_size), NULL, GL_STREAM_DRAW); + + // TODO: if this is OpenGL 4.4 or newer, use glBufferStorage rather than glBufferData + // and specify GL_MAP_PERSISTENT_BIT. Then map the buffer now, and let the client + // write straight into it. } ScanTarget::~ScanTarget() { - // Release span space. + // Release scan space. glDeleteBuffers(1, &scan_buffer_name_); } @@ -115,8 +120,8 @@ uint8_t *ScanTarget::allocate_write_area(size_t required_length, size_t required void ScanTarget::reduce_previous_allocation_to(size_t actual_length) { if(allocation_has_failed_) return; - // The span was allocated in the knowledge that there's sufficient distance - // left on the current line, so there's no need to worry about carry. + // The write area was allocated in the knowledge that there's sufficient + // distance left on the current line, so there's no need to worry about carry. write_pointers_.write_area += actual_length + 1; } @@ -137,17 +142,43 @@ void ScanTarget::draw() { const auto submit_pointers = submit_pointers_.load(); const auto read_pointers = read_pointers_.load(); - // Submit spans. + // Submit scans. if(submit_pointers.scan_buffer != read_pointers.scan_buffer) { -// uint8_t *destination = static_cast(glMapBufferRange(GL_ARRAY_BUFFER, 0, (GLsizeiptr)length, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT)); -// if(!glGetError() && destination) { -// } + const auto buffer_size = scan_buffer_.size() * sizeof(Scan); + uint8_t *destination = static_cast( + glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT) + ); - // TODO: submit all scans from scan_buffer_pointers_.read_pointer to scan_buffer_pointers_.submit_pointer. -// read_pointers_.scan_buffer = submit_pointers.scan_buffer; + if(submit_pointers.scan_buffer > read_pointers.scan_buffer) { + // Submit the direct region from the submit pointer to the read pointer. + const size_t offset = read_pointers.scan_buffer * sizeof(Scan); + const size_t length = (submit_pointers.scan_buffer - read_pointers.scan_buffer) * sizeof(Scan); + memcpy(&destination[offset], &scan_buffer_[read_pointers.scan_buffer], length); + + glFlushMappedBufferRange(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(length)); + } else { + // The circular buffer wrapped around; submit the data from the read pointer to the end of + // the buffer and from the start of the buffer to the submit pointer. + const size_t offset = read_pointers.scan_buffer * sizeof(Scan); + const size_t end_length = (scan_buffer_.size() - read_pointers.scan_buffer) * sizeof(Scan); + const size_t start_length = submit_pointers.scan_buffer * sizeof(Scan); + + memcpy(&destination[offset], &scan_buffer_[read_pointers.scan_buffer], end_length); + memcpy(&destination[0], &scan_buffer_[0], start_length); + + glFlushMappedBufferRange(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(end_length)); + glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(start_length)); + } + + glUnmapBuffer(GL_ARRAY_BUFFER); } + // TODO: submit texture. + // TODO: clear composite buffer (if needed). + // TODO: drawing (!) + + // All data now having been spooled to the GPU, update the read pointers to // the submit pointer location. read_pointers_.store(submit_pointers); From 491817d85c919a8959dc9d82894ce234582a76ff Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Nov 2018 23:02:36 -0500 Subject: [PATCH 017/208] Corrects allocation error and begins submitting raw textures. --- Outputs/OpenGL/ScanTarget.cpp | 100 ++++++++++++++++++++++++++++++---- Outputs/OpenGL/ScanTarget.hpp | 10 ++-- 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 286a8499e..9b382e79e 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -16,10 +16,30 @@ namespace { constexpr int WriteAreaWidth = 2048; constexpr int WriteAreaHeight = 2048; -#define TextureAddress(x, y) (((x) << 11) | (y)) -#define TextureAddressGetX(v) ((v) >> 11) -#define TextureAddressGetY(v) ((v) & 0x7ff) -#define TextureSub(x, y) (((x) - (y)) & 0x7ff) +#define TextureAddress(x, y) (((y) << 11) | (x)) +#define TextureAddressGetY(v) uint16_t((v) >> 11) +#define TextureAddressGetX(v) uint16_t((v) & 0x7ff) +#define TextureSub(a, b) (((a) - (b)) & 0x3fffff) + +const GLint internalFormatForDepth(std::size_t depth) { + switch(depth) { + default: return GL_FALSE; + case 1: return GL_R8UI; + case 2: return GL_RG8UI; + case 3: return GL_RGB8UI; + case 4: return GL_RGBA8UI; + } +} + +const GLenum formatForDepth(std::size_t depth) { + switch(depth) { + default: return GL_FALSE; + case 1: return GL_RED_INTEGER; + case 2: return GL_RG_INTEGER; + case 3: return GL_RGB_INTEGER; + case 4: return GL_RGBA_INTEGER; + } +} } @@ -34,11 +54,14 @@ ScanTarget::ScanTarget() { // TODO: if this is OpenGL 4.4 or newer, use glBufferStorage rather than glBufferData // and specify GL_MAP_PERSISTENT_BIT. Then map the buffer now, and let the client // write straight into it. + + glGenTextures(1, &write_area_texture_name_); } ScanTarget::~ScanTarget() { // Release scan space. glDeleteBuffers(1, &scan_buffer_name_); + glDeleteTextures(1, &write_area_texture_name_); } void ScanTarget::set_modals(Modals modals) { @@ -99,22 +122,25 @@ uint8_t *ScanTarget::allocate_write_area(size_t required_length, size_t required } // Check whether that steps over the read pointer. - const auto new_address = TextureAddress(end_x, output_y); + const auto end_address = TextureAddress(end_x, output_y); const auto read_pointers = read_pointers_.load(); - const auto new_distance = TextureSub(read_pointers.write_area, new_address); - const auto previous_distance = TextureSub(read_pointers.write_area, write_pointers_.write_area); + const auto end_distance = TextureSub(end_address, read_pointers.write_area); + const auto previous_distance = TextureSub(write_pointers_.write_area, read_pointers.write_area); - // If allocating this would somehow make the write pointer further away from the read pointer, + // If allocating this would somehow make the write pointer back away from the read pointer, // there must not be enough space left. - if(new_distance > previous_distance) { + if(end_distance < previous_distance) { allocation_has_failed_ = true; return nullptr; } // Everything checks out, return the pointer. - last_supplied_x_ = aligned_start_x; - return &write_area_texture_[size_t(new_address) * data_type_size_]; + write_pointers_.write_area = TextureAddress(aligned_start_x, output_y); + return &write_area_texture_[size_t(write_pointers_.write_area) * data_type_size_]; + + // Note state at exit: + // 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) { @@ -174,7 +200,57 @@ void ScanTarget::draw() { glUnmapBuffer(GL_ARRAY_BUFFER); } - // TODO: submit texture. + // Submit texture. + if(submit_pointers.write_area != read_pointers.write_area) { + glBindTexture(GL_TEXTURE_2D, write_area_texture_name_); + + // Create storage for the texture if it doesn't yet exist; this was deferred until here + // because the pixel format wasn't initially known. + if(!texture_exists_) { + glTexImage2D( + GL_TEXTURE_2D, + 0, + internalFormatForDepth(data_type_size_), + WriteAreaWidth, + WriteAreaHeight, + 0, + formatForDepth(data_type_size_), + GL_UNSIGNED_BYTE, + nullptr); + texture_exists_ = true; + } + + const auto start_y = TextureAddressGetY(read_pointers.write_area); + const auto end_y = TextureAddressGetY(submit_pointers.write_area); + if(end_y >= start_y) { + // Submit the direct region from the submit pointer to the read pointer. + glTexSubImage2D( GL_TEXTURE_2D, 0, + 0, start_y, + WriteAreaWidth, + 1 + end_y - start_y, + formatForDepth(data_type_size_), + GL_UNSIGNED_BYTE, + &write_area_texture_[size_t(TextureAddress(0, start_y))]); + } else { + // The circular buffer wrapped around; submit the data from the read pointer to the end of + // the buffer and from the start of the buffer to the submit pointer. + glTexSubImage2D( GL_TEXTURE_2D, 0, + 0, 0, + WriteAreaWidth, + 1 + end_y, + formatForDepth(data_type_size_), + GL_UNSIGNED_BYTE, + &write_area_texture_[0]); + glTexSubImage2D( GL_TEXTURE_2D, 0, + 0, start_y, + WriteAreaWidth, + WriteAreaHeight - start_y, + formatForDepth(data_type_size_), + GL_UNSIGNED_BYTE, + &write_area_texture_[size_t(TextureAddress(0, start_y))]); + } + } + // TODO: clear composite buffer (if needed). // TODO: drawing (!) diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index df456ad10..8fd362881 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -48,9 +48,9 @@ class ScanTarget: public Outputs::Display::ScanTarget { // The sizes below might be less hassle as something more natural like ints, // but squeezing this struct into 64 bits makes the std::atomics more likely // to be lock free; they are under LLVM x86-64. - int write_area; - uint16_t scan_buffer; - uint16_t composite_y; + int write_area = 0; + uint16_t scan_buffer = 0; + uint16_t composite_y = 0; }; /// A pointer to the next thing that should be provided to the caller for data. @@ -69,7 +69,9 @@ class ScanTarget: public Outputs::Display::ScanTarget { // Uses a texture to vend write areas. std::vector write_area_texture_; size_t data_type_size_ = 0; - uint16_t last_supplied_x_ = 0; + + GLuint write_area_texture_name_ = 0; + bool texture_exists_ = false; // Track allocation failures. bool allocation_has_failed_ = false; From 6d277fecd58f7f74a8115cdb81a5d32d5ed733bd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Nov 2018 19:52:57 -0500 Subject: [PATCH 018/208] 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) {} }; } From b70227ac1b6e65a5c0c363f2dcadba0ddbe467eb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Nov 2018 21:10:33 -0500 Subject: [PATCH 019/208] Ensures proper write area locations end up in the scans. --- Outputs/OpenGL/ScanTarget.cpp | 15 ++++++++++++--- Outputs/OpenGL/ScanTarget.hpp | 5 +++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index f9b119fe2..7a77da148 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -100,12 +100,21 @@ Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() { write_pointers_.scan_buffer = next_write_pointer; // Fill in extra OpenGL-specific details. -// result->data_y = write_pointers_.write_area; - result->composite_y = 0; + result->composite_y = 0; // TODO. + vended_scan_ = result; return static_cast(result); } +void ScanTarget::end_scan() { + if(vended_scan_) { + vended_scan_->data_y = TextureAddressGetY(vended_write_area_pointer_); + vended_scan_->end_points[0].data_offset += TextureAddressGetX(vended_write_area_pointer_); + vended_scan_->end_points[1].data_offset += TextureAddressGetX(vended_write_area_pointer_); + } + vended_scan_ = nullptr; +} + uint8_t *ScanTarget::begin_data(size_t required_length, size_t required_alignment) { if(allocation_has_failed_) return nullptr; @@ -138,7 +147,7 @@ uint8_t *ScanTarget::begin_data(size_t required_length, size_t required_alignmen } // Everything checks out, return the pointer. - write_pointers_.write_area = TextureAddress(aligned_start_x, output_y); + vended_write_area_pointer_ = write_pointers_.write_area = TextureAddress(aligned_start_x, output_y); return &write_area_texture_[size_t(write_pointers_.write_area) * data_type_size_]; // Note state at exit: diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 149762c59..58bf5ed0e 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -30,6 +30,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { // Outputs::Display::ScanTarget overrides. void set_modals(Modals) override; Scan *begin_scan() override; + void end_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; @@ -74,6 +75,10 @@ class ScanTarget: public Outputs::Display::ScanTarget { GLuint write_area_texture_name_ = 0; bool texture_exists_ = false; + // Ephemeral information for the begin/end functions. + Scan *vended_scan_ = nullptr; + int vended_write_area_pointer_ = 0; + // Track allocation failures. bool allocation_has_failed_ = false; From be12d78c832d0a8bc63f889b287109e6bbc699dd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Nov 2018 15:11:32 -0500 Subject: [PATCH 020/208] Corrects vertical event announcement, and adjusts namespaces for OpenGL primitives. --- Outputs/CRT/CRT.cpp | 4 ++-- Outputs/OpenGL/Primitives/Rectangle.cpp | 2 +- Outputs/OpenGL/Primitives/Rectangle.hpp | 4 ++++ Outputs/OpenGL/Primitives/Shader.cpp | 2 +- Outputs/OpenGL/Primitives/Shader.hpp | 4 ++++ Outputs/OpenGL/Primitives/TextureTarget.cpp | 2 +- Outputs/OpenGL/Primitives/TextureTarget.hpp | 4 ++++ Outputs/OpenGL/ScanTarget.cpp | 16 ++++++++++++---- Outputs/OpenGL/ScanTarget.hpp | 17 +++++++++++++++-- 9 files changed, 44 insertions(+), 11 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 9e1867e56..3a34709cd 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -209,9 +209,9 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_ } // Also announce vertical retrace events. - if(next_run_length == time_until_vertical_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) { + if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event != Flywheel::SyncEvent::None) { const auto event = - (next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) + (next_vertical_sync_event == Flywheel::SyncEvent::StartRetrace) ? Outputs::Display::ScanTarget::Event::BeginVerticalRetrace : Outputs::Display::ScanTarget::Event::EndVerticalRetrace; scan_target_->announce( event, diff --git a/Outputs/OpenGL/Primitives/Rectangle.cpp b/Outputs/OpenGL/Primitives/Rectangle.cpp index 6600ef5bd..94fdf3db9 100644 --- a/Outputs/OpenGL/Primitives/Rectangle.cpp +++ b/Outputs/OpenGL/Primitives/Rectangle.cpp @@ -8,7 +8,7 @@ #include "Rectangle.hpp" -using namespace OpenGL; +using namespace Outputs::Display::OpenGL; Rectangle::Rectangle(float x, float y, float width, float height): pixel_shader_( diff --git a/Outputs/OpenGL/Primitives/Rectangle.hpp b/Outputs/OpenGL/Primitives/Rectangle.hpp index 07a5152d5..fc1a4e58a 100644 --- a/Outputs/OpenGL/Primitives/Rectangle.hpp +++ b/Outputs/OpenGL/Primitives/Rectangle.hpp @@ -13,6 +13,8 @@ #include "Shader.hpp" #include +namespace Outputs { +namespace Display { namespace OpenGL { /*! @@ -36,6 +38,8 @@ class Rectangle { GLint colour_uniform_; }; +} +} } #endif /* Rectangle_hpp */ diff --git a/Outputs/OpenGL/Primitives/Shader.cpp b/Outputs/OpenGL/Primitives/Shader.cpp index a82b7705d..879ca2167 100644 --- a/Outputs/OpenGL/Primitives/Shader.cpp +++ b/Outputs/OpenGL/Primitives/Shader.cpp @@ -11,7 +11,7 @@ #include #include -using namespace OpenGL; +using namespace Outputs::Display::OpenGL; namespace { // The below is disabled because it isn't context/thread-specific. Which makes it diff --git a/Outputs/OpenGL/Primitives/Shader.hpp b/Outputs/OpenGL/Primitives/Shader.hpp index 572218cb6..ca1cc17cf 100644 --- a/Outputs/OpenGL/Primitives/Shader.hpp +++ b/Outputs/OpenGL/Primitives/Shader.hpp @@ -16,6 +16,8 @@ #include #include +namespace Outputs { +namespace Display { namespace OpenGL { /*! @@ -118,6 +120,8 @@ protected: void enqueue_function(std::function function); }; +} +} } #endif /* Shader_hpp */ diff --git a/Outputs/OpenGL/Primitives/TextureTarget.cpp b/Outputs/OpenGL/Primitives/TextureTarget.cpp index c2d6f8f57..a4061b21a 100644 --- a/Outputs/OpenGL/Primitives/TextureTarget.cpp +++ b/Outputs/OpenGL/Primitives/TextureTarget.cpp @@ -11,7 +11,7 @@ #include #include -using namespace OpenGL; +using namespace Outputs::Display::OpenGL; TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit, GLint mag_filter) : width_(width), diff --git a/Outputs/OpenGL/Primitives/TextureTarget.hpp b/Outputs/OpenGL/Primitives/TextureTarget.hpp index c6b9b14f5..269477da5 100644 --- a/Outputs/OpenGL/Primitives/TextureTarget.hpp +++ b/Outputs/OpenGL/Primitives/TextureTarget.hpp @@ -13,6 +13,8 @@ #include "Shader.hpp" #include +namespace Outputs { +namespace Display { namespace OpenGL { /*! @@ -87,6 +89,8 @@ class TextureTarget { GLint threshold_uniform_; }; +} +} } #endif /* TextureTarget_hpp */ diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 7a77da148..1cb9304db 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -16,6 +16,10 @@ namespace { constexpr int WriteAreaWidth = 2048; constexpr int WriteAreaHeight = 2048; +constexpr int CompositeLineBufferWidth = 2048; +constexpr int CompositeLineBufferHeight = 2048; +constexpr int CompositeLineBufferTextureUnit = 0; + #define TextureAddress(x, y) (((y) << 11) | (x)) #define TextureAddressGetY(v) uint16_t((v) >> 11) #define TextureAddressGetX(v) uint16_t((v) & 0x7ff) @@ -43,7 +47,9 @@ const GLenum formatForDepth(std::size_t depth) { } -ScanTarget::ScanTarget() { +ScanTarget::ScanTarget() : + composite_line_texture_(CompositeLineBufferWidth, CompositeLineBufferHeight, CompositeLineBufferTextureUnit, GL_LINEAR) { + // Allocate space for the scans. glGenBuffers(1, &scan_buffer_name_); glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); @@ -56,14 +62,13 @@ ScanTarget::ScanTarget() { // write straight into it. glGenTextures(1, &write_area_texture_name_); - glGenVertexArrays(1, &vertex_array_); + glGenVertexArrays(1, &scan_vertex_array_); } ScanTarget::~ScanTarget() { - // Release scan space. glDeleteBuffers(1, &scan_buffer_name_); glDeleteTextures(1, &write_area_texture_name_); - glDeleteVertexArrays(1, &vertex_array_); + glDeleteVertexArrays(1, &scan_vertex_array_); } void ScanTarget::set_modals(Modals modals) { @@ -174,6 +179,9 @@ void ScanTarget::submit() { allocation_has_failed_ = false; } +void ScanTarget::announce(Event event, uint16_t x, uint16_t y) { +} + void ScanTarget::draw() { // Grab the current read and submit pointers. const auto submit_pointers = submit_pointers_.load(); diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 58bf5ed0e..7cf507478 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -11,6 +11,7 @@ #include "../ScanTarget.hpp" #include "OpenGL.hpp" +#include "Primitives/TextureTarget.hpp" #include #include @@ -27,6 +28,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { ~ScanTarget(); void draw(); + private: // Outputs::Display::ScanTarget overrides. void set_modals(Modals) override; Scan *begin_scan() override; @@ -34,8 +36,8 @@ class ScanTarget: public Outputs::Display::ScanTarget { uint8_t *begin_data(size_t required_length, size_t required_alignment) override; void end_data(size_t actual_length) override; void submit() override; + void announce(Event event, uint16_t x, uint16_t y) override; - private: // Extends the definition of a Scan to include two extra fields, // relevant to the way that this scan target processes video. struct Scan: public Outputs::Display::ScanTarget::Scan { @@ -66,7 +68,18 @@ 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_; + GLuint scan_vertex_array_ = 0; + + // Maintains a list of composite scan buffer coordinates. + struct CompositeLine { + struct EndPoint { + uint16_t x, y; + } end_points[2]; + uint16_t composite_y; + }; + std::array composite_line_buffer_; + TextureTarget composite_line_texture_; + GLuint composite_line_vertex_array_ = 0; // Uses a texture to vend write areas. std::vector write_area_texture_; From 0ba3ae53abe68e42d8beb0514279774fddf31c7c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Nov 2018 15:20:18 -0500 Subject: [PATCH 021/208] Connects up the necessary recording to use intermediate composite buffers. --- Outputs/OpenGL/ScanTarget.cpp | 29 ++++++++++++++++++++++++++++- Outputs/OpenGL/ScanTarget.hpp | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 1cb9304db..b3d600ffe 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -105,7 +105,7 @@ Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() { write_pointers_.scan_buffer = next_write_pointer; // Fill in extra OpenGL-specific details. - result->composite_y = 0; // TODO. + result->composite_y = write_pointers_.composite_y; vended_scan_ = result; return static_cast(result); @@ -180,6 +180,33 @@ void ScanTarget::submit() { } void ScanTarget::announce(Event event, uint16_t x, uint16_t y) { + switch(event) { + default: break; + case ScanTarget::Event::BeginHorizontalRetrace: + if(active_composite_line_) { + active_composite_line_->end_points[1].x = x; + active_composite_line_->end_points[1].y = y; + active_composite_line_ = nullptr; + } + break; + case ScanTarget::Event::EndHorizontalRetrace: { + const auto read_pointers = read_pointers_.load(); + + // Attempt to allocate a new line; note allocation failure if necessary. + const auto next_composite_y = uint16_t((write_pointers_.composite_y + 1) % CompositeLineBufferHeight); + + // Check whether that's too many. + if(next_composite_y == read_pointers.composite_y) { + allocation_has_failed_ = true; + } else { + write_pointers_.composite_y = next_composite_y; + active_composite_line_ = &composite_line_buffer_[size_t(write_pointers_.composite_y)]; + active_composite_line_->end_points[0].x = x; + active_composite_line_->end_points[0].y = y; + active_composite_line_->composite_y = write_pointers_.composite_y; + } + } break; + } } void ScanTarget::draw() { diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 7cf507478..e9d352599 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -80,6 +80,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { std::array composite_line_buffer_; TextureTarget composite_line_texture_; GLuint composite_line_vertex_array_ = 0; + CompositeLine *active_composite_line_ = nullptr; // Uses a texture to vend write areas. std::vector write_area_texture_; From d5af1f39489849b901963560c3b027ea93046b98 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Nov 2018 16:22:14 -0500 Subject: [PATCH 022/208] Removes some migrated work. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 - Outputs/OpenGL/ArrayBuilder.cpp | 149 -------------- Outputs/OpenGL/ArrayBuilder.hpp | 101 ---------- Outputs/OpenGL/CRTOpenGL.hpp | 181 ------------------ Outputs/OpenGL/ScanTarget.cpp | 26 ++- Outputs/OpenGL/ScanTarget.hpp | 2 +- 6 files changed, 23 insertions(+), 442 deletions(-) delete mode 100644 Outputs/OpenGL/ArrayBuilder.cpp delete mode 100644 Outputs/OpenGL/ArrayBuilder.hpp delete mode 100644 Outputs/OpenGL/CRTOpenGL.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 4600e3c2b..aab5669dc 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1363,8 +1363,6 @@ 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Oric.cpp; path = Oric/Oric.cpp; sourceTree = ""; }; 4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Oric/Oric.hpp; sourceTree = ""; }; 4BD060A51FE49D3C006E14BE /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = ""; }; - 4BD191D6219113B80042E144 /* ArrayBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayBuilder.cpp; sourceTree = ""; }; - 4BD191D7219113B80042E144 /* CRTOpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTOpenGL.hpp; sourceTree = ""; }; 4BD191D8219113B80042E144 /* TextureBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureBuilder.cpp; sourceTree = ""; }; 4BD191D9219113B80042E144 /* OpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OpenGL.hpp; sourceTree = ""; }; 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTOpenGL.cpp; sourceTree = ""; }; @@ -1373,7 +1371,6 @@ 4BD191E1219113B80042E144 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; 4BD191E3219113B80042E144 /* IntermediateShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntermediateShader.cpp; sourceTree = ""; }; 4BD191E4219113B80042E144 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = ""; }; - 4BD191E7219113B90042E144 /* ArrayBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ArrayBuilder.hpp; sourceTree = ""; }; 4BD191F22191180E0042E144 /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = ""; }; 4BD191F32191180E0042E144 /* ScanTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanTarget.hpp; sourceTree = ""; }; 4BD388411FE34E010042B588 /* 9918Base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 9918Base.hpp; path = 9918/Implementation/9918Base.hpp; sourceTree = ""; }; @@ -3025,12 +3022,9 @@ 4BD191D5219113B80042E144 /* OpenGL */ = { isa = PBXGroup; children = ( - 4BD191D6219113B80042E144 /* ArrayBuilder.cpp */, 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */, 4BD191F22191180E0042E144 /* ScanTarget.cpp */, 4BD191D8219113B80042E144 /* TextureBuilder.cpp */, - 4BD191E7219113B90042E144 /* ArrayBuilder.hpp */, - 4BD191D7219113B80042E144 /* CRTOpenGL.hpp */, 4BD191D9219113B80042E144 /* OpenGL.hpp */, 4BD191F32191180E0042E144 /* ScanTarget.hpp */, 4BD191DD219113B80042E144 /* TextureBuilder.hpp */, diff --git a/Outputs/OpenGL/ArrayBuilder.cpp b/Outputs/OpenGL/ArrayBuilder.cpp deleted file mode 100644 index 04a35bb49..000000000 --- a/Outputs/OpenGL/ArrayBuilder.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// -// ArrayBuilder.cpp -// Clock Signal -// -// Created by Thomas Harte on 17/11/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#include "ArrayBuilder.hpp" - -#include - -using namespace Outputs::CRT; - -ArrayBuilder::ArrayBuilder(std::size_t input_size, std::size_t output_size) : - output_(output_size, nullptr), - input_(input_size, nullptr) {} - -ArrayBuilder::ArrayBuilder(std::size_t input_size, std::size_t output_size, std::function submission_function) : - output_(output_size, submission_function), - input_(input_size, submission_function) {} - -bool ArrayBuilder::is_full() { - bool was_full; - was_full = is_full_; - return was_full; -} - -uint8_t *ArrayBuilder::get_input_storage(std::size_t size) { - return get_storage(size, input_); -} - -uint8_t *ArrayBuilder::get_output_storage(std::size_t size) { - return get_storage(size, output_); -} - -void ArrayBuilder::flush(const std::function &function) { - if(!is_full_) { - std::size_t input_size = 0, output_size = 0; - uint8_t *input = input_.get_unflushed(input_size); - uint8_t *output = output_.get_unflushed(output_size); - function(input, input_size, output, output_size); - - input_.flush(); - output_.flush(); - } -} - -void ArrayBuilder::bind_input() { - input_.bind(); -} - -void ArrayBuilder::bind_output() { - output_.bind(); -} - -ArrayBuilder::Submission ArrayBuilder::submit() { - ArrayBuilder::Submission submission; - - submission.input_size = input_.submit(true); - submission.output_size = output_.submit(false); - if(is_full_) { - is_full_ = false; - input_.reset(); - output_.reset(); - } - - return submission; -} - -ArrayBuilder::Buffer::Buffer(std::size_t size, std::function submission_function) : - submission_function_(submission_function) { - if(!submission_function_) { - glGenBuffers(1, &buffer); - glBindBuffer(GL_ARRAY_BUFFER, buffer); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)size, NULL, GL_STREAM_DRAW); - } - data.resize(size); -} - -ArrayBuilder::Buffer::~Buffer() { - if(!submission_function_) - glDeleteBuffers(1, &buffer); -} - -uint8_t *ArrayBuilder::get_storage(std::size_t size, Buffer &buffer) { - uint8_t *pointer = buffer.get_storage(size); - if(!pointer) is_full_ = true; - return pointer; -} - -uint8_t *ArrayBuilder::Buffer::get_storage(std::size_t size) { - if(is_full || allocated_data + size > data.size()) { - is_full = true; - return nullptr; - } - uint8_t *pointer = &data[allocated_data]; - allocated_data += size; - return pointer; -} - -uint8_t *ArrayBuilder::Buffer::get_unflushed(std::size_t &size) { - if(is_full) { - return nullptr; - } - size = allocated_data - flushed_data; - return &data[flushed_data]; -} - -void ArrayBuilder::Buffer::flush() { - if(submitted_data) { - memmove(data.data(), &data[submitted_data], allocated_data - submitted_data); - allocated_data -= submitted_data; - flushed_data -= submitted_data; - submitted_data = 0; - } - - flushed_data = allocated_data; -} - -std::size_t ArrayBuilder::Buffer::submit(bool is_input) { - std::size_t length = flushed_data; - if(submission_function_) { - submission_function_(is_input, data.data(), length); - } else { - glBindBuffer(GL_ARRAY_BUFFER, buffer); - uint8_t *destination = static_cast(glMapBufferRange(GL_ARRAY_BUFFER, 0, (GLsizeiptr)length, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT)); - if(!glGetError() && destination) { - std::memcpy(destination, data.data(), length); - glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, (GLsizeiptr)length); - glUnmapBuffer(GL_ARRAY_BUFFER); - } else { - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)length, data.data(), GL_STREAM_DRAW); - } - } - submitted_data = flushed_data; - return length; -} - -void ArrayBuilder::Buffer::bind() { - glBindBuffer(GL_ARRAY_BUFFER, buffer); -} - -void ArrayBuilder::Buffer::reset() { - is_full = false; - allocated_data = 0; - flushed_data = 0; - submitted_data = 0; -} diff --git a/Outputs/OpenGL/ArrayBuilder.hpp b/Outputs/OpenGL/ArrayBuilder.hpp deleted file mode 100644 index 8b47b6ecd..000000000 --- a/Outputs/OpenGL/ArrayBuilder.hpp +++ /dev/null @@ -1,101 +0,0 @@ -// -// ArrayBuilder.hpp -// Clock Signal -// -// Created by Thomas Harte on 17/11/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#ifndef ArrayBuilder_hpp -#define ArrayBuilder_hpp - -#include -#include -#include - -#include "OpenGL.hpp" - -namespace Outputs { -namespace CRT { - -/*! - Owns two array buffers, an 'input' and an 'output' and vends pointers to allow an owner to write provisional data into those - plus a flush function to lock provisional data into place. Also supplies a submit method to transfer all currently locked - data to the GPU and bind_input/output methods to bind the internal buffers. - - It is safe for one thread to communicate via the get_*_storage and flush inputs asynchronously from another that is making - use of the bind and submit outputs. -*/ -class ArrayBuilder { - public: - /// Creates an instance of ArrayBuilder with @c output_size bytes of storage for the output buffer and - /// @c input_size bytes of storage for the input buffer. - ArrayBuilder(std::size_t input_size, std::size_t output_size); - - /// Creates an instance of ArrayBuilder with @c output_size bytes of storage for the output buffer and - /// @c input_size bytes of storage for the input buffer that, rather than using OpenGL, will submit data - /// to the @c submission_function. [Teleological: this is provided as a testing hook.] - ArrayBuilder(std::size_t input_size, std::size_t output_size, std::function submission_function); - - /// Attempts to add @c size bytes to the input set. - /// @returns a pointer to the allocated area if allocation was possible; @c nullptr otherwise. - uint8_t *get_input_storage(std::size_t size); - - /// Attempts to add @c size bytes to the output set. - /// @returns a pointer to the allocated area if allocation was possible; @c nullptr otherwise. - uint8_t *get_output_storage(std::size_t size); - - /// @returns @c true if either of the input or output storage areas is currently exhausted; @c false otherwise. - bool is_full(); - - /// If neither input nor output was exhausted since the last flush, atomically commits both input and output - /// up to the currently allocated size for use upon the next @c submit, giving the supplied function a - /// chance to perform last-minute processing. Otherwise acts as a no-op. - void flush(const std::function &); - - /// Binds the input array to GL_ARRAY_BUFFER. - void bind_input(); - - /// Binds the output array to GL_ARRAY_BUFFER. - void bind_output(); - - struct Submission { - std::size_t input_size, output_size; - }; - - /// Submits all flushed input and output data to the corresponding arrays. - /// @returns A @c Submission record, indicating how much data of each type was submitted. - Submission submit(); - - private: - class Buffer { - public: - Buffer(std::size_t size, std::function submission_function); - ~Buffer(); - - uint8_t *get_storage(std::size_t size); - uint8_t *get_unflushed(std::size_t &size); - - void flush(); - std::size_t submit(bool is_input); - void bind(); - void reset(); - - private: - bool is_full = false; - GLuint buffer = 0; - std::function submission_function_; - std::vector data; - std::size_t allocated_data = 0; - std::size_t flushed_data = 0; - std::size_t submitted_data = 0; - } output_, input_; - uint8_t *get_storage(std::size_t size, Buffer &buffer); - - bool is_full_ = false; -}; - -} -} - -#endif /* ArrayBuilder_hpp */ diff --git a/Outputs/OpenGL/CRTOpenGL.hpp b/Outputs/OpenGL/CRTOpenGL.hpp deleted file mode 100644 index c988b950e..000000000 --- a/Outputs/OpenGL/CRTOpenGL.hpp +++ /dev/null @@ -1,181 +0,0 @@ -// -// CRTOpenGL.hpp -// Clock Signal -// -// Created by Thomas Harte on 13/02/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#ifndef CRTOpenGL_h -#define CRTOpenGL_h - -#include "CRTConstants.hpp" -#include "OpenGL.hpp" -#include "TextureTarget.hpp" -#include "Shaders/Shader.hpp" - -#include "ArrayBuilder.hpp" -#include "TextureBuilder.hpp" - -#include "Shaders/OutputShader.hpp" -#include "Shaders/IntermediateShader.hpp" -#include "Rectangle.hpp" - -#include "../ScanTarget.hpp" - -#include -#include - -namespace Outputs { -namespace CRT { - -class OpenGLOutputBuilder { - private: - // colour information - ColourSpace colour_space_; - unsigned int colour_cycle_numerator_; - unsigned int colour_cycle_denominator_; - VideoSignal video_signal_; - float gamma_; - - // timing information to allow reasoning about input information - unsigned int input_frequency_; - unsigned int cycles_per_line_; - unsigned int height_of_display_; - unsigned int horizontal_scan_period_; - unsigned int vertical_scan_period_; - unsigned int vertical_period_divider_; - - // The user-supplied visible area - Rect visible_area_; - - // Other things the caller may have provided. - std::string composite_shader_; - std::string svideo_shader_; - std::string rgb_shader_; - GLint target_framebuffer_ = 0; - - // Methods used by the OpenGL code - void prepare_output_shader(); - void prepare_rgb_input_shaders(); - void prepare_svideo_input_shaders(); - void prepare_composite_input_shaders(); - - void prepare_output_vertex_array(); - void prepare_source_vertex_array(); - - // the run and input data buffers - std::mutex output_mutex_; - std::mutex draw_mutex_; - - // transient buffers indicating composite data not yet decoded - GLsizei composite_src_output_y_; - - std::unique_ptr output_shader_program_; - - std::unique_ptr composite_input_shader_program_; - std::unique_ptr composite_separation_filter_program_; - std::unique_ptr composite_chrominance_filter_shader_program_; - - std::unique_ptr svideo_input_shader_program_; - - std::unique_ptr rgb_input_shader_program_; - std::unique_ptr rgb_filter_shader_program_; - - std::unique_ptr composite_texture_; // receives raw composite levels - std::unique_ptr separated_texture_; // receives filtered Y in the R channel plus unfiltered but demodulated chrominance in G and B - std::unique_ptr filtered_texture_; // receives filtered YIQ or YUV - - std::unique_ptr work_texture_; // used for all intermediate rendering if texture fences are supported - - std::unique_ptr framebuffer_; // the current pixel output - - GLuint output_vertex_array_; - GLuint source_vertex_array_; - - unsigned int last_output_width_, last_output_height_; - - void set_timing_uniforms(); - void set_colour_space_uniforms(); - void set_gamma(); - - void establish_OpenGL_state(); - void reset_all_OpenGL_state(); - - GLsync fence_; - float get_composite_output_width() const; - void set_output_shader_width(); - - // Maintain a couple of rectangles for masking off the extreme edge of the display; - // this is a bit of a cheat: there's some tolerance in when a sync pulse will be - // generated. So it might be slightly later than expected. Which might cause a scan - // that is slightly longer than expected. Which means that from then on, those scans - // might have touched parts of the extreme edge of the display which are not rescanned. - // Which because I've implemented persistence-of-vision as an in-buffer effect will - // cause perpetual persistence. - // - // The fix: just always treat that area as invisible. This is acceptable thanks to - // the concept of overscan. One is allowed not to display extreme ends of the image. - std::unique_ptr right_overlay_; - std::unique_ptr left_overlay_; - - public: - // These two are protected by output_mutex_. - TextureBuilder texture_builder; - ArrayBuilder array_builder; - - OpenGLOutputBuilder(std::size_t bytes_per_pixel); - ~OpenGLOutputBuilder(); - - inline void set_colour_format(ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator) { - std::lock_guard output_guard(output_mutex_); - colour_space_ = colour_space; - colour_cycle_numerator_ = colour_cycle_numerator; - colour_cycle_denominator_ = colour_cycle_denominator; - set_colour_space_uniforms(); - } - - inline void set_visible_area(Rect visible_area) { - visible_area_ = visible_area; - } - - inline void set_gamma(float gamma) { - gamma_ = gamma; - set_gamma(); - } - - inline std::unique_lock get_output_lock() { - return std::unique_lock(output_mutex_); - } - - inline VideoSignal get_output_device() { - return video_signal_; - } - - inline uint16_t get_composite_output_y() { - return static_cast(composite_src_output_y_); - } - - inline bool composite_output_buffer_is_full() { - return composite_src_output_y_ == IntermediateBufferHeight; - } - - inline void increment_composite_output_y() { - if(!composite_output_buffer_is_full()) - composite_src_output_y_++; - } - - void set_target_framebuffer(GLint); - void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty); - void set_openGL_context_will_change(bool should_delete_resources); - void set_composite_sampling_function(const std::string &); - void set_svideo_sampling_function(const std::string &); - void set_rgb_sampling_function(const std::string &); - void set_video_signal(VideoSignal); - void set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider); -}; - -} -} - -#endif /* CRTOpenGL_h */ diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index b3d600ffe..be1f6cec8 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -18,7 +18,26 @@ constexpr int WriteAreaHeight = 2048; constexpr int CompositeLineBufferWidth = 2048; constexpr int CompositeLineBufferHeight = 2048; -constexpr int CompositeLineBufferTextureUnit = 0; + +/// The texture unit from which to source 1bpp input data. +constexpr GLenum SourceData1BppTextureUnit = GL_TEXTURE0; +/// The texture unit from which to source 2bpp input data. +constexpr GLenum SourceData2BppTextureUnit = GL_TEXTURE1; +/// The texture unit from which to source 4bpp input data. +constexpr GLenum SourceData4BppTextureUnit = GL_TEXTURE2; + +/// The texture unit which contains raw line-by-line composite or RGB data. +constexpr GLenum UnprocessedLineBufferTextureUnit = GL_TEXTURE3; +/// The texture unit which contains line-by-line records of luminance and amplitude-modulated chrominance. +constexpr GLenum CompositeSeparatedTextureUnit = GL_TEXTURE4; +/// The texture unit which contains line-by-line records of luminance and demodulated chrominance. +constexpr GLenum DemodulatedCompositeTextureUnit = GL_TEXTURE5; + +/// The texture unit which contains line-by-line RGB. +constexpr GLenum LineBufferTextureUnit = GL_TEXTURE6; + +/// The texture unit that contains the current display. +constexpr GLenum AccumulationTextureUnit = GL_TEXTURE7; #define TextureAddress(x, y) (((y) << 11) | (x)) #define TextureAddressGetY(v) uint16_t((v) >> 11) @@ -48,13 +67,12 @@ const GLenum formatForDepth(std::size_t depth) { } ScanTarget::ScanTarget() : - composite_line_texture_(CompositeLineBufferWidth, CompositeLineBufferHeight, CompositeLineBufferTextureUnit, GL_LINEAR) { + unprocessed_line_texture_(CompositeLineBufferWidth, CompositeLineBufferHeight, UnprocessedLineBufferTextureUnit, GL_LINEAR) { // Allocate space for the scans. + const auto buffer_size = scan_buffer_.size() * sizeof(Scan); glGenBuffers(1, &scan_buffer_name_); glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); - - const auto buffer_size = scan_buffer_.size() * sizeof(Scan); glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(buffer_size), NULL, GL_STREAM_DRAW); // TODO: if this is OpenGL 4.4 or newer, use glBufferStorage rather than glBufferData diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index e9d352599..bceee1bac 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -78,7 +78,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { uint16_t composite_y; }; std::array composite_line_buffer_; - TextureTarget composite_line_texture_; + TextureTarget unprocessed_line_texture_; GLuint composite_line_vertex_array_ = 0; CompositeLine *active_composite_line_ = nullptr; From 3ae333fa84012ef92ce70d52bc3fd6532153ea4f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Nov 2018 21:41:13 -0500 Subject: [PATCH 023/208] Edges further towards reviving the shaders. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 ++ Outputs/OpenGL/ScanTarget.cpp | 36 +++---- Outputs/OpenGL/ScanTarget.hpp | 39 ++++++-- .../OpenGL/ScanTargetVertexArrayAttributs.cpp | 93 +++++++++++++++++++ 4 files changed, 147 insertions(+), 27 deletions(-) create mode 100644 Outputs/OpenGL/ScanTargetVertexArrayAttributs.cpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index aab5669dc..58d69b723 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -624,6 +624,8 @@ 4BD424E82193B5830097291A /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424E22193B5820097291A /* Rectangle.cpp */; }; 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD468F51D8DF41D0084958B /* 1770.cpp */; }; 4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */; }; + 4BD5D2682199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp */; }; + 4BD5D2692199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */; }; 4BD61664206B2AC800236112 /* QuickLoadOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BD61662206B2AC700236112 /* QuickLoadOptions.xib */; }; 4BD67DCB209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */; }; @@ -1385,6 +1387,7 @@ 4BD468F51D8DF41D0084958B /* 1770.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 1770.cpp; path = 1770/1770.cpp; sourceTree = ""; }; 4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = ""; }; 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMTrackTests.mm; sourceTree = ""; }; + 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTargetVertexArrayAttributs.cpp; sourceTree = ""; }; 4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = ""; }; 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSBestEffortUpdater.mm; path = Updater/CSBestEffortUpdater.mm; sourceTree = ""; }; 4BD601A920D89F2A00CBCE57 /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Log.hpp; path = ../../Outputs/Log.hpp; sourceTree = ""; }; @@ -3024,6 +3027,7 @@ children = ( 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */, 4BD191F22191180E0042E144 /* ScanTarget.cpp */, + 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp */, 4BD191D8219113B80042E144 /* TextureBuilder.cpp */, 4BD191D9219113B80042E144 /* OpenGL.hpp */, 4BD191F32191180E0042E144 /* ScanTarget.hpp */, @@ -3668,6 +3672,7 @@ 4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B7F188F2154825E00388727 /* MasterSystem.cpp in Sources */, 4B055AA51FAE85EF0060FFFF /* Encoder.cpp in Sources */, + 4BD5D2692199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp in Sources */, 4B894529201967B4007DE474 /* Disk.cpp in Sources */, 4B055AEA1FAE9B990060FFFF /* 6502Storage.cpp in Sources */, 4B055AA71FAE85EF0060FFFF /* SegmentParser.cpp in Sources */, @@ -3857,6 +3862,7 @@ 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */, 4B894518201967B4007DE474 /* ConfidenceCounter.cpp in Sources */, 4B89452E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, + 4BD5D2682199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp in Sources */, 4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */, 4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */, 4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */, diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index be1f6cec8..291efcddf 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -16,8 +16,8 @@ namespace { constexpr int WriteAreaWidth = 2048; constexpr int WriteAreaHeight = 2048; -constexpr int CompositeLineBufferWidth = 2048; -constexpr int CompositeLineBufferHeight = 2048; +constexpr int LineBufferWidth = 2048; +constexpr int LineBufferHeight = 2048; /// The texture unit from which to source 1bpp input data. constexpr GLenum SourceData1BppTextureUnit = GL_TEXTURE0; @@ -67,7 +67,7 @@ const GLenum formatForDepth(std::size_t depth) { } ScanTarget::ScanTarget() : - unprocessed_line_texture_(CompositeLineBufferWidth, CompositeLineBufferHeight, UnprocessedLineBufferTextureUnit, GL_LINEAR) { + unprocessed_line_texture_(LineBufferWidth, LineBufferHeight, UnprocessedLineBufferTextureUnit, GL_LINEAR) { // Allocate space for the scans. const auto buffer_size = scan_buffer_.size() * sizeof(Scan); @@ -123,17 +123,17 @@ Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() { write_pointers_.scan_buffer = next_write_pointer; // Fill in extra OpenGL-specific details. - result->composite_y = write_pointers_.composite_y; + result->line = write_pointers_.line; vended_scan_ = result; - return static_cast(result); + return &result->scan; } void ScanTarget::end_scan() { if(vended_scan_) { vended_scan_->data_y = TextureAddressGetY(vended_write_area_pointer_); - vended_scan_->end_points[0].data_offset += TextureAddressGetX(vended_write_area_pointer_); - vended_scan_->end_points[1].data_offset += TextureAddressGetX(vended_write_area_pointer_); + vended_scan_->scan.end_points[0].data_offset += TextureAddressGetX(vended_write_area_pointer_); + vended_scan_->scan.end_points[1].data_offset += TextureAddressGetX(vended_write_area_pointer_); } vended_scan_ = nullptr; } @@ -201,27 +201,27 @@ void ScanTarget::announce(Event event, uint16_t x, uint16_t y) { switch(event) { default: break; case ScanTarget::Event::BeginHorizontalRetrace: - if(active_composite_line_) { - active_composite_line_->end_points[1].x = x; - active_composite_line_->end_points[1].y = y; - active_composite_line_ = nullptr; + if(active_line_) { + active_line_->end_points[1].x = x; + active_line_->end_points[1].y = y; + active_line_ = nullptr; } break; case ScanTarget::Event::EndHorizontalRetrace: { const auto read_pointers = read_pointers_.load(); // Attempt to allocate a new line; note allocation failure if necessary. - const auto next_composite_y = uint16_t((write_pointers_.composite_y + 1) % CompositeLineBufferHeight); + const auto next_line = uint16_t((write_pointers_.line + 1) % LineBufferHeight); // Check whether that's too many. - if(next_composite_y == read_pointers.composite_y) { + if(next_line == read_pointers.line) { allocation_has_failed_ = true; } else { - write_pointers_.composite_y = next_composite_y; - active_composite_line_ = &composite_line_buffer_[size_t(write_pointers_.composite_y)]; - active_composite_line_->end_points[0].x = x; - active_composite_line_->end_points[0].y = y; - active_composite_line_->composite_y = write_pointers_.composite_y; + write_pointers_.line = next_line; + active_line_ = &line_buffer_[size_t(write_pointers_.line)]; + active_line_->end_points[0].x = x; + active_line_->end_points[0].y = y; + active_line_->line = write_pointers_.line; } } break; } diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index bceee1bac..8b0b521cf 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include namespace Outputs { @@ -40,11 +41,13 @@ class ScanTarget: public Outputs::Display::ScanTarget { // Extends the definition of a Scan to include two extra fields, // relevant to the way that this scan target processes video. - struct Scan: public Outputs::Display::ScanTarget::Scan { + struct Scan { + Outputs::Display::ScanTarget::Scan scan; + /// Stores the y coordinate that this scan's data is at, within the write area texture. uint16_t data_y; - /// Stores the y coordinate of this continuous composite segment within the conversion buffer. - uint16_t composite_y; + /// Stores the y coordinate of this scan within the line buffer. + uint16_t line; }; struct PointerSet { @@ -53,7 +56,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { // to be lock free; they are under LLVM x86-64. int write_area = 0; uint16_t scan_buffer = 0; - uint16_t composite_y = 0; + uint16_t line = 0; }; /// A pointer to the next thing that should be provided to the caller for data. @@ -71,16 +74,16 @@ class ScanTarget: public Outputs::Display::ScanTarget { GLuint scan_vertex_array_ = 0; // Maintains a list of composite scan buffer coordinates. - struct CompositeLine { + struct Line { struct EndPoint { uint16_t x, y; } end_points[2]; - uint16_t composite_y; + uint16_t line; }; - std::array composite_line_buffer_; + std::array line_buffer_; TextureTarget unprocessed_line_texture_; - GLuint composite_line_vertex_array_ = 0; - CompositeLine *active_composite_line_ = nullptr; + GLuint line_vertex_array_ = 0; + Line *active_line_ = nullptr; // Uses a texture to vend write areas. std::vector write_area_texture_; @@ -98,6 +101,24 @@ class ScanTarget: public Outputs::Display::ScanTarget { // Receives scan target modals. Modals modals_; + + enum class ShaderType { + Scan, + Line + }; + + /*! + @returns A string containing GLSL code describing the standard set of + @c in and @c uniform variables to bind to the relevant struct + from [...]OpenGL::ScanTarget. + */ + std::string globals(ShaderType type); + + /*! + Calls @c taret.enable_vertex_attribute_with_pointer to attach all + globals for shaders of @c type to @c target. + */ + void enable_vertex_attributes(ShaderType type, Shader &target); }; } diff --git a/Outputs/OpenGL/ScanTargetVertexArrayAttributs.cpp b/Outputs/OpenGL/ScanTargetVertexArrayAttributs.cpp new file mode 100644 index 000000000..41aa86324 --- /dev/null +++ b/Outputs/OpenGL/ScanTargetVertexArrayAttributs.cpp @@ -0,0 +1,93 @@ +// +// ScanTargetVertexArrayAttributs.cpp +// Clock Signal +// +// Created by Thomas Harte on 11/11/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "ScanTarget.hpp" + +using namespace Outputs::Display::OpenGL; + +std::string ScanTarget::globals(ShaderType type) { + switch(type) { + case ShaderType::Scan: + return + "uniform vec2 scale;" + "uniform float rowHeight;" + "uniform mat3 lumaChromaToRGB;" + "uniform mat3 rgbToLumaChroma;" + + "in vec2 startPoint;" + "in float startDataX;" + "in float startCompositeAngle;" + + "in vec2 endPoint;" + "in float endDataX;" + "in float endCompositeAngle;" + + "in float dataY;" + "in float lineY;"; + + case ShaderType::Line: + return + "in vec2 startPoint;" + "in vec2 endPoint;"; + } +} + +void ScanTarget::enable_vertex_attributes(ShaderType type, Shader &target) { + switch(type) { + case ShaderType::Scan: + for(int c = 0; c < 2; ++c) { + const std::string prefix = c ? "end" : "start"; + + target.enable_vertex_attribute_with_pointer( + prefix + "Point", + 2, GL_UNSIGNED_SHORT, GL_FALSE, + sizeof(Scan), + reinterpret_cast(offsetof(Scan, scan.end_points[c].x)), + 1); + target.enable_vertex_attribute_with_pointer( + prefix + "DataX", + 1, GL_UNSIGNED_SHORT, GL_FALSE, + sizeof(Scan), + reinterpret_cast(offsetof(Scan, scan.end_points[c].data_offset)), + 1); + target.enable_vertex_attribute_with_pointer( + prefix + "CompositeAngle", + 1, GL_UNSIGNED_SHORT, GL_FALSE, + sizeof(Scan), + reinterpret_cast(offsetof(Scan, scan.end_points[c].composite_angle)), + 1); + } + + target.enable_vertex_attribute_with_pointer( + "dataY", + 1, GL_UNSIGNED_SHORT, GL_FALSE, + sizeof(Scan), + reinterpret_cast(offsetof(Scan, data_y)), + 1); + target.enable_vertex_attribute_with_pointer( + "lineY", + 1, GL_UNSIGNED_SHORT, GL_FALSE, + sizeof(Scan), + reinterpret_cast(offsetof(Scan, line)), + 1); + break; + + case ShaderType::Line: + for(int c = 0; c < 2; ++c) { + const std::string prefix = c ? "end" : "start"; + + target.enable_vertex_attribute_with_pointer( + prefix + "Point", + 2, GL_UNSIGNED_SHORT, GL_FALSE, + sizeof(Line), + reinterpret_cast(offsetof(Line, end_points[c].x)), + 1); + } + break; + } +} From dce52d740dbd3ca259c5fc59308fab50aae0c42b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Nov 2018 23:23:42 -0500 Subject: [PATCH 024/208] Finally gets some pixels back on screen. For now, just the raw scans, direct to the framebuffer, with no intermediate processing. But it seems to prove that at least some of the proper data is reaching the GPU. --- .../Clock Signal.xcodeproj/project.pbxproj | 12 ++++---- Outputs/OpenGL/ScanTarget.cpp | 28 +++++++++++++++++-- Outputs/OpenGL/ScanTarget.hpp | 5 +++- ...pp => ScanTargetVertexArrayAttributes.cpp} | 17 ++++++++++- 4 files changed, 51 insertions(+), 11 deletions(-) rename Outputs/OpenGL/{ScanTargetVertexArrayAttributs.cpp => ScanTargetVertexArrayAttributes.cpp} (85%) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 58d69b723..f29712c67 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -624,8 +624,8 @@ 4BD424E82193B5830097291A /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424E22193B5820097291A /* Rectangle.cpp */; }; 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD468F51D8DF41D0084958B /* 1770.cpp */; }; 4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */; }; - 4BD5D2682199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp */; }; - 4BD5D2692199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp */; }; + 4BD5D2682199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp */; }; + 4BD5D2692199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */; }; 4BD61664206B2AC800236112 /* QuickLoadOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BD61662206B2AC700236112 /* QuickLoadOptions.xib */; }; 4BD67DCB209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */; }; @@ -1387,7 +1387,7 @@ 4BD468F51D8DF41D0084958B /* 1770.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 1770.cpp; path = 1770/1770.cpp; sourceTree = ""; }; 4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = ""; }; 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMTrackTests.mm; sourceTree = ""; }; - 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTargetVertexArrayAttributs.cpp; sourceTree = ""; }; + 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTargetVertexArrayAttributes.cpp; sourceTree = ""; }; 4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = ""; }; 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSBestEffortUpdater.mm; path = Updater/CSBestEffortUpdater.mm; sourceTree = ""; }; 4BD601A920D89F2A00CBCE57 /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Log.hpp; path = ../../Outputs/Log.hpp; sourceTree = ""; }; @@ -3027,7 +3027,7 @@ children = ( 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */, 4BD191F22191180E0042E144 /* ScanTarget.cpp */, - 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp */, + 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp */, 4BD191D8219113B80042E144 /* TextureBuilder.cpp */, 4BD191D9219113B80042E144 /* OpenGL.hpp */, 4BD191F32191180E0042E144 /* ScanTarget.hpp */, @@ -3672,7 +3672,7 @@ 4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B7F188F2154825E00388727 /* MasterSystem.cpp in Sources */, 4B055AA51FAE85EF0060FFFF /* Encoder.cpp in Sources */, - 4BD5D2692199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp in Sources */, + 4BD5D2692199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp in Sources */, 4B894529201967B4007DE474 /* Disk.cpp in Sources */, 4B055AEA1FAE9B990060FFFF /* 6502Storage.cpp in Sources */, 4B055AA71FAE85EF0060FFFF /* SegmentParser.cpp in Sources */, @@ -3862,7 +3862,7 @@ 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */, 4B894518201967B4007DE474 /* ConfidenceCounter.cpp in Sources */, 4B89452E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, - 4BD5D2682199148100DDF17D /* ScanTargetVertexArrayAttributs.cpp in Sources */, + 4BD5D2682199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp in Sources */, 4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */, 4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */, 4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */, diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 291efcddf..b9e7d323f 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -81,6 +81,18 @@ ScanTarget::ScanTarget() : glGenTextures(1, &write_area_texture_name_); glGenVertexArrays(1, &scan_vertex_array_); + + glBindVertexArray(scan_vertex_array_); + glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); + test_shader_.reset(new Shader( + globals(ShaderType::Scan), + "#version 150\n" + "out vec4 fragColour;" + "void main(void) {" + "fragColour = vec4(1.0);" + "}" + )); + enable_vertex_attributes(ShaderType::Scan, *test_shader_); } ScanTarget::~ScanTarget() { @@ -104,6 +116,10 @@ void ScanTarget::set_modals(Modals modals) { write_pointers_.scan_buffer = 0; write_pointers_.write_area = 0; } + + // TODO: this, but not to the test shader. + test_shader_->set_uniform("scale", GLfloat(modals.output_scale.x), GLfloat(modals.output_scale.y)); + test_shader_->set_uniform("rowHeight", GLfloat(1.0f / modals.expected_vertical_lines)); } Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() { @@ -228,17 +244,19 @@ void ScanTarget::announce(Event event, uint16_t x, uint16_t y) { } void ScanTarget::draw() { + glClear(GL_COLOR_BUFFER_BIT); + // Grab the current read and submit pointers. const auto submit_pointers = submit_pointers_.load(); const auto read_pointers = read_pointers_.load(); // Submit scans. if(submit_pointers.scan_buffer != read_pointers.scan_buffer) { - const auto buffer_size = scan_buffer_.size() * sizeof(Scan); uint8_t *destination = static_cast( glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT) ); + assert(destination); if(submit_pointers.scan_buffer > read_pointers.scan_buffer) { // Submit the direct region from the submit pointer to the read pointer. @@ -323,7 +341,11 @@ void ScanTarget::draw() { // the submit pointer location. read_pointers_.store(submit_pointers); - glClear(GL_COLOR_BUFFER_BIT); -// ::OpenGL::Rectangle rect(-0.8f, -0.8f, 1.6f, 1.6f); + // TEST: draw all scans. + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindVertexArray(scan_vertex_array_); + test_shader_->bind(); + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(scan_buffer_.size())); +// Rectangle rect(-0.8f, -0.8f, 1.6f, 1.6f); // rect.draw(1, 1, 0); } diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 8b0b521cf..897f129b0 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -110,7 +110,8 @@ class ScanTarget: public Outputs::Display::ScanTarget { /*! @returns A string containing GLSL code describing the standard set of @c in and @c uniform variables to bind to the relevant struct - from [...]OpenGL::ScanTarget. + from [...]OpenGL::ScanTarget and a vertex function to provide + the standard varyings. */ std::string globals(ShaderType type); @@ -119,6 +120,8 @@ class ScanTarget: public Outputs::Display::ScanTarget { globals for shaders of @c type to @c target. */ void enable_vertex_attributes(ShaderType type, Shader &target); + + std::unique_ptr test_shader_; }; } diff --git a/Outputs/OpenGL/ScanTargetVertexArrayAttributs.cpp b/Outputs/OpenGL/ScanTargetVertexArrayAttributes.cpp similarity index 85% rename from Outputs/OpenGL/ScanTargetVertexArrayAttributs.cpp rename to Outputs/OpenGL/ScanTargetVertexArrayAttributes.cpp index 41aa86324..9c59f689c 100644 --- a/Outputs/OpenGL/ScanTargetVertexArrayAttributs.cpp +++ b/Outputs/OpenGL/ScanTargetVertexArrayAttributes.cpp @@ -14,6 +14,8 @@ std::string ScanTarget::globals(ShaderType type) { switch(type) { case ShaderType::Scan: return + "#version 150\n" + "uniform vec2 scale;" "uniform float rowHeight;" "uniform mat3 lumaChromaToRGB;" @@ -28,10 +30,23 @@ std::string ScanTarget::globals(ShaderType type) { "in float endCompositeAngle;" "in float dataY;" - "in float lineY;"; + "in float lineY;" + + "void main(void) {" + "float lateral = float(gl_VertexID & 1);" + "float longitudinal = float((gl_VertexID & 2) >> 1);" + + "vec2 vPosition = vec2(" + "mix(startPoint.x, endPoint.x, lateral)," + "mix(startPoint.y, endPoint.y, longitudinal)" + ") / scale;" + "gl_Position = vec4(vPosition, 0.0, 1.0);" + "}"; case ShaderType::Line: return + "#version 150\n" + "in vec2 startPoint;" "in vec2 endPoint;"; } From 938d09f34a564194f4e15cea5c9d7d159a76dac3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Nov 2018 18:23:45 -0500 Subject: [PATCH 025/208] Corrects scan outline generation. --- .../Mac/Clock Signal.xcodeproj/project.pbxproj | 12 ++++++------ ...rayAttributes.cpp => ScanTargetGLSLFragments.cpp} | 8 +++----- 2 files changed, 9 insertions(+), 11 deletions(-) rename Outputs/OpenGL/{ScanTargetVertexArrayAttributes.cpp => ScanTargetGLSLFragments.cpp} (91%) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index f29712c67..586b1c813 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -624,8 +624,8 @@ 4BD424E82193B5830097291A /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424E22193B5820097291A /* Rectangle.cpp */; }; 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD468F51D8DF41D0084958B /* 1770.cpp */; }; 4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */; }; - 4BD5D2682199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp */; }; - 4BD5D2692199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp */; }; + 4BD5D2682199148100DDF17D /* ScanTargetGLSLFragments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */; }; + 4BD5D2692199148100DDF17D /* ScanTargetGLSLFragments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */; }; 4BD61664206B2AC800236112 /* QuickLoadOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BD61662206B2AC700236112 /* QuickLoadOptions.xib */; }; 4BD67DCB209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */; }; @@ -1387,7 +1387,7 @@ 4BD468F51D8DF41D0084958B /* 1770.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 1770.cpp; path = 1770/1770.cpp; sourceTree = ""; }; 4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = ""; }; 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMTrackTests.mm; sourceTree = ""; }; - 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTargetVertexArrayAttributes.cpp; sourceTree = ""; }; + 4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTargetGLSLFragments.cpp; sourceTree = ""; }; 4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = ""; }; 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSBestEffortUpdater.mm; path = Updater/CSBestEffortUpdater.mm; sourceTree = ""; }; 4BD601A920D89F2A00CBCE57 /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Log.hpp; path = ../../Outputs/Log.hpp; sourceTree = ""; }; @@ -3027,7 +3027,7 @@ children = ( 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */, 4BD191F22191180E0042E144 /* ScanTarget.cpp */, - 4BD5D2672199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp */, + 4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */, 4BD191D8219113B80042E144 /* TextureBuilder.cpp */, 4BD191D9219113B80042E144 /* OpenGL.hpp */, 4BD191F32191180E0042E144 /* ScanTarget.hpp */, @@ -3672,7 +3672,7 @@ 4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B7F188F2154825E00388727 /* MasterSystem.cpp in Sources */, 4B055AA51FAE85EF0060FFFF /* Encoder.cpp in Sources */, - 4BD5D2692199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp in Sources */, + 4BD5D2692199148100DDF17D /* ScanTargetGLSLFragments.cpp in Sources */, 4B894529201967B4007DE474 /* Disk.cpp in Sources */, 4B055AEA1FAE9B990060FFFF /* 6502Storage.cpp in Sources */, 4B055AA71FAE85EF0060FFFF /* SegmentParser.cpp in Sources */, @@ -3862,7 +3862,7 @@ 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */, 4B894518201967B4007DE474 /* ConfidenceCounter.cpp in Sources */, 4B89452E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, - 4BD5D2682199148100DDF17D /* ScanTargetVertexArrayAttributes.cpp in Sources */, + 4BD5D2682199148100DDF17D /* ScanTargetGLSLFragments.cpp in Sources */, 4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */, 4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */, 4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */, diff --git a/Outputs/OpenGL/ScanTargetVertexArrayAttributes.cpp b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp similarity index 91% rename from Outputs/OpenGL/ScanTargetVertexArrayAttributes.cpp rename to Outputs/OpenGL/ScanTargetGLSLFragments.cpp index 9c59f689c..5a04e525c 100644 --- a/Outputs/OpenGL/ScanTargetVertexArrayAttributes.cpp +++ b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp @@ -36,11 +36,9 @@ std::string ScanTarget::globals(ShaderType type) { "float lateral = float(gl_VertexID & 1);" "float longitudinal = float((gl_VertexID & 2) >> 1);" - "vec2 vPosition = vec2(" - "mix(startPoint.x, endPoint.x, lateral)," - "mix(startPoint.y, endPoint.y, longitudinal)" - ") / scale;" - "gl_Position = vec4(vPosition, 0.0, 1.0);" + "vec2 centrePoint = mix(startPoint, endPoint, lateral) / scale;" + "vec2 height = normalize(endPoint - startPoint).yx * (longitudinal - 0.5) * rowHeight;" + "gl_Position = vec4(centrePoint + height, 0.0, 1.0);" "}"; case ShaderType::Line: From 0ac62e3805e59d4a3e1f8974c9488adc637c71b8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Nov 2018 18:28:09 -0500 Subject: [PATCH 026/208] Flips and properly sizes output scans. --- Outputs/OpenGL/ScanTargetGLSLFragments.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp index 5a04e525c..cc30f8c1d 100644 --- a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp +++ b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp @@ -38,7 +38,8 @@ std::string ScanTarget::globals(ShaderType type) { "vec2 centrePoint = mix(startPoint, endPoint, lateral) / scale;" "vec2 height = normalize(endPoint - startPoint).yx * (longitudinal - 0.5) * rowHeight;" - "gl_Position = vec4(centrePoint + height, 0.0, 1.0);" + "vec2 eyePosition = vec2(1.0) - 2.0 * (centrePoint + height);" + "gl_Position = vec4(eyePosition, 0.0, 1.0);" "}"; case ShaderType::Line: From 0af1d668a6269a64fc93683b0312d3140b1123dc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Nov 2018 18:47:55 -0500 Subject: [PATCH 027/208] Takes a first step towards generality, and thereby starts submitting lines. --- Outputs/OpenGL/ScanTarget.cpp | 85 ++++++++++++++++++++--------------- Outputs/OpenGL/ScanTarget.hpp | 10 +++-- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index b9e7d323f..88a907098 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -66,14 +66,19 @@ const GLenum formatForDepth(std::size_t depth) { } +template void ScanTarget::allocate_buffer(const T &array, GLuint &buffer_name) { + const auto buffer_size = array.size() * sizeof(array[0]); + glGenBuffers(1, &buffer_name); + glBindBuffer(GL_ARRAY_BUFFER, buffer_name); + glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(buffer_size), NULL, GL_STREAM_DRAW); +} + ScanTarget::ScanTarget() : unprocessed_line_texture_(LineBufferWidth, LineBufferHeight, UnprocessedLineBufferTextureUnit, GL_LINEAR) { - // Allocate space for the scans. - const auto buffer_size = scan_buffer_.size() * sizeof(Scan); - glGenBuffers(1, &scan_buffer_name_); - glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); - glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(buffer_size), NULL, GL_STREAM_DRAW); + // Allocate space for the scans and lines. + allocate_buffer(scan_buffer_, scan_buffer_name_); + allocate_buffer(line_buffer_, line_buffer_name_); // TODO: if this is OpenGL 4.4 or newer, use glBufferStorage rather than glBufferData // and specify GL_MAP_PERSISTENT_BIT. Then map the buffer now, and let the client @@ -243,6 +248,42 @@ void ScanTarget::announce(Event event, uint16_t x, uint16_t y) { } } +template void ScanTarget::submit_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer) { + if(submit_pointer != read_pointer) { + // Bind the buffer and map it into CPU space. + glBindBuffer(GL_ARRAY_BUFFER, target); + const auto buffer_size = array.size() * sizeof(array[0]); + uint8_t *destination = static_cast( + glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT) + ); + assert(destination); + + if(submit_pointer > read_pointer) { + // Submit the direct region from the submit pointer to the read pointer. + const size_t offset = read_pointer * sizeof(array[0]); + const size_t length = (submit_pointer - read_pointer) * sizeof(array[0]); + memcpy(&destination[offset], &array[read_pointer], length); + + glFlushMappedBufferRange(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(length)); + } else { + // The circular buffer wrapped around; submit the data from the read pointer to the end of + // the buffer and from the start of the buffer to the submit pointer. + const size_t offset = read_pointer * sizeof(array[0]); + const size_t end_length = (array.size() - read_pointer) * sizeof(array[0]); + const size_t start_length = submit_pointer * sizeof(array[0]); + + memcpy(&destination[offset], &array[read_pointer], end_length); + memcpy(&destination[0], &array[0], start_length); + + glFlushMappedBufferRange(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(end_length)); + glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(start_length)); + } + + // Unmap the buffer. + glUnmapBuffer(GL_ARRAY_BUFFER); + } +} + void ScanTarget::draw() { glClear(GL_COLOR_BUFFER_BIT); @@ -250,37 +291,9 @@ void ScanTarget::draw() { const auto submit_pointers = submit_pointers_.load(); const auto read_pointers = read_pointers_.load(); - // Submit scans. - if(submit_pointers.scan_buffer != read_pointers.scan_buffer) { - const auto buffer_size = scan_buffer_.size() * sizeof(Scan); - uint8_t *destination = static_cast( - glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT) - ); - assert(destination); - - if(submit_pointers.scan_buffer > read_pointers.scan_buffer) { - // Submit the direct region from the submit pointer to the read pointer. - const size_t offset = read_pointers.scan_buffer * sizeof(Scan); - const size_t length = (submit_pointers.scan_buffer - read_pointers.scan_buffer) * sizeof(Scan); - memcpy(&destination[offset], &scan_buffer_[read_pointers.scan_buffer], length); - - glFlushMappedBufferRange(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(length)); - } else { - // The circular buffer wrapped around; submit the data from the read pointer to the end of - // the buffer and from the start of the buffer to the submit pointer. - const size_t offset = read_pointers.scan_buffer * sizeof(Scan); - const size_t end_length = (scan_buffer_.size() - read_pointers.scan_buffer) * sizeof(Scan); - const size_t start_length = submit_pointers.scan_buffer * sizeof(Scan); - - memcpy(&destination[offset], &scan_buffer_[read_pointers.scan_buffer], end_length); - memcpy(&destination[0], &scan_buffer_[0], start_length); - - glFlushMappedBufferRange(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(end_length)); - glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(start_length)); - } - - glUnmapBuffer(GL_ARRAY_BUFFER); - } + // Submit scans and lines. + submit_buffer(scan_buffer_, scan_buffer_name_, submit_pointers.scan_buffer, read_pointers.scan_buffer); + submit_buffer(line_buffer_, line_buffer_name_, submit_pointers.line, read_pointers.line); // Submit texture. if(submit_pointers.write_area != read_pointers.write_area) { diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 897f129b0..8e108b25d 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -70,8 +70,6 @@ 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 scan_vertex_array_ = 0; // Maintains a list of composite scan buffer coordinates. struct Line { @@ -82,9 +80,15 @@ class ScanTarget: public Outputs::Display::ScanTarget { }; std::array line_buffer_; TextureTarget unprocessed_line_texture_; - GLuint line_vertex_array_ = 0; Line *active_line_ = nullptr; + // OpenGL storage handles for buffer data. + GLuint scan_buffer_name_ = 0, scan_vertex_array_ = 0; + GLuint line_buffer_name_ = 0, line_vertex_array_ = 0; + + template void allocate_buffer(const T &array, GLuint &buffer_name); + template void submit_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer); + // Uses a texture to vend write areas. std::vector write_area_texture_; size_t data_type_size_ = 0; From 833ab7945b47de47fc2ec7846a86799204bae283 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Nov 2018 18:56:54 -0500 Subject: [PATCH 028/208] Slow steps towards switching to line output. --- Outputs/OpenGL/ScanTarget.cpp | 5 ++--- Outputs/OpenGL/ScanTarget.hpp | 6 ++++- Outputs/OpenGL/ScanTargetGLSLFragments.cpp | 26 ++++++++++++++++------ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 88a907098..f7988d95e 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -90,7 +90,7 @@ ScanTarget::ScanTarget() : glBindVertexArray(scan_vertex_array_); glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); test_shader_.reset(new Shader( - globals(ShaderType::Scan), + glsl_globals(ShaderType::Scan) + glsl_default_vertex_shader(ShaderType::Scan), "#version 150\n" "out vec4 fragColour;" "void main(void) {" @@ -356,9 +356,8 @@ void ScanTarget::draw() { // TEST: draw all scans. glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindVertexArray(scan_vertex_array_); test_shader_->bind(); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(scan_buffer_.size())); -// Rectangle rect(-0.8f, -0.8f, 1.6f, 1.6f); -// rect.draw(1, 1, 0); } diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 8e108b25d..812db7dbb 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -117,7 +117,11 @@ class ScanTarget: public Outputs::Display::ScanTarget { from [...]OpenGL::ScanTarget and a vertex function to provide the standard varyings. */ - std::string globals(ShaderType type); + std::string glsl_globals(ShaderType type); + + /*! + */ + std::string glsl_default_vertex_shader(ShaderType type); /*! Calls @c taret.enable_vertex_attribute_with_pointer to attach all diff --git a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp index cc30f8c1d..5d3464089 100644 --- a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp +++ b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp @@ -10,7 +10,7 @@ using namespace Outputs::Display::OpenGL; -std::string ScanTarget::globals(ShaderType type) { +std::string ScanTarget::glsl_globals(ShaderType type) { switch(type) { case ShaderType::Scan: return @@ -30,8 +30,24 @@ std::string ScanTarget::globals(ShaderType type) { "in float endCompositeAngle;" "in float dataY;" - "in float lineY;" + "in float lineY;"; + case ShaderType::Line: + return + "#version 150\n" + + "uniform vec2 scale;" + "uniform float rowHeight;" + + "in vec2 startPoint;" + "in vec2 endPoint;"; + } +} + +std::string ScanTarget::glsl_default_vertex_shader(ShaderType type) { + switch(type) { + case ShaderType::Scan: + return "void main(void) {" "float lateral = float(gl_VertexID & 1);" "float longitudinal = float((gl_VertexID & 2) >> 1);" @@ -43,11 +59,7 @@ std::string ScanTarget::globals(ShaderType type) { "}"; case ShaderType::Line: - return - "#version 150\n" - - "in vec2 startPoint;" - "in vec2 endPoint;"; + return ""; } } From e9d1afd5155c12dce5f765217cc741657a231dc1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Nov 2018 19:10:48 -0500 Subject: [PATCH 029/208] Appears to demonstrates that the line buffer is approximately working. --- Outputs/OpenGL/ScanTarget.cpp | 22 ++++++++++++---------- Outputs/OpenGL/ScanTarget.hpp | 2 +- Outputs/OpenGL/ScanTargetGLSLFragments.cpp | 12 ++++++------ 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index f7988d95e..6a1755fa4 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -66,38 +66,40 @@ const GLenum formatForDepth(std::size_t depth) { } -template void ScanTarget::allocate_buffer(const T &array, GLuint &buffer_name) { +template void ScanTarget::allocate_buffer(const T &array, GLuint &buffer_name, GLuint &vertex_array_name) { const auto buffer_size = array.size() * sizeof(array[0]); glGenBuffers(1, &buffer_name); glBindBuffer(GL_ARRAY_BUFFER, buffer_name); glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(buffer_size), NULL, GL_STREAM_DRAW); + + glGenVertexArrays(1, &vertex_array_name); + glBindVertexArray(vertex_array_name); + glBindBuffer(GL_ARRAY_BUFFER, buffer_name); } ScanTarget::ScanTarget() : unprocessed_line_texture_(LineBufferWidth, LineBufferHeight, UnprocessedLineBufferTextureUnit, GL_LINEAR) { // Allocate space for the scans and lines. - allocate_buffer(scan_buffer_, scan_buffer_name_); - allocate_buffer(line_buffer_, line_buffer_name_); + allocate_buffer(scan_buffer_, scan_buffer_name_, scan_vertex_array_); + allocate_buffer(line_buffer_, line_buffer_name_, line_vertex_array_); // TODO: if this is OpenGL 4.4 or newer, use glBufferStorage rather than glBufferData // and specify GL_MAP_PERSISTENT_BIT. Then map the buffer now, and let the client // write straight into it. glGenTextures(1, &write_area_texture_name_); - glGenVertexArrays(1, &scan_vertex_array_); - glBindVertexArray(scan_vertex_array_); - glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); test_shader_.reset(new Shader( - glsl_globals(ShaderType::Scan) + glsl_default_vertex_shader(ShaderType::Scan), + glsl_globals(ShaderType::Line) + glsl_default_vertex_shader(ShaderType::Line), "#version 150\n" "out vec4 fragColour;" "void main(void) {" "fragColour = vec4(1.0);" "}" )); - enable_vertex_attributes(ShaderType::Scan, *test_shader_); + glBindVertexArray(line_vertex_array_); + enable_vertex_attributes(ShaderType::Line, *test_shader_); } ScanTarget::~ScanTarget() { @@ -354,10 +356,10 @@ void ScanTarget::draw() { // the submit pointer location. read_pointers_.store(submit_pointers); - // TEST: draw all scans. + // TEST: draw all lines. glBindFramebuffer(GL_FRAMEBUFFER, 0); - glBindVertexArray(scan_vertex_array_); + glBindVertexArray(line_vertex_array_); test_shader_->bind(); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(scan_buffer_.size())); } diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 812db7dbb..1e64fdea3 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -86,7 +86,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { GLuint scan_buffer_name_ = 0, scan_vertex_array_ = 0; GLuint line_buffer_name_ = 0, line_vertex_array_ = 0; - template void allocate_buffer(const T &array, GLuint &buffer_name); + template void allocate_buffer(const T &array, GLuint &buffer_name, GLuint &vertex_array_name); template void submit_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer); // Uses a texture to vend write areas. diff --git a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp index 5d3464089..d8fe3c505 100644 --- a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp +++ b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp @@ -45,8 +45,8 @@ std::string ScanTarget::glsl_globals(ShaderType type) { } std::string ScanTarget::glsl_default_vertex_shader(ShaderType type) { - switch(type) { - case ShaderType::Scan: +// switch(type) { +// case ShaderType::Scan: return "void main(void) {" "float lateral = float(gl_VertexID & 1);" @@ -54,13 +54,13 @@ std::string ScanTarget::glsl_default_vertex_shader(ShaderType type) { "vec2 centrePoint = mix(startPoint, endPoint, lateral) / scale;" "vec2 height = normalize(endPoint - startPoint).yx * (longitudinal - 0.5) * rowHeight;" - "vec2 eyePosition = vec2(1.0) - 2.0 * (centrePoint + height);" + "vec2 eyePosition = vec2(-0.9, 0.9) + vec2(1.8, -1.8) * (centrePoint + height);" "gl_Position = vec4(eyePosition, 0.0, 1.0);" "}"; - case ShaderType::Line: - return ""; - } +// case ShaderType::Line: +// return ""; +// } } void ScanTarget::enable_vertex_attributes(ShaderType type, Shader &target) { From 3045e850049354cd6be3d9f82ae824f023789234 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Nov 2018 20:15:38 -0500 Subject: [PATCH 030/208] Ensures redraws when resizing; declines to busy wait otherwise. --- .../xcshareddata/xcschemes/Clock Signal.xcscheme | 2 +- OSBindings/Mac/Clock Signal/Machine/CSMachine.mm | 2 +- Outputs/OpenGL/ScanTarget.cpp | 13 +++++++++++-- Outputs/OpenGL/ScanTarget.hpp | 3 ++- Outputs/OpenGL/ScanTargetGLSLFragments.cpp | 12 ++++++------ 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index f9049689f..782b17c09 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -68,7 +68,7 @@ draw(); + _scanTarget->draw(onlyIfDirty ? false : true); // _machine->crt_machine()->get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); } diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 6a1755fa4..b1a569d13 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -286,8 +286,14 @@ template void ScanTarget::submit_buffer(const T &array, GLuint targ } } -void ScanTarget::draw() { - glClear(GL_COLOR_BUFFER_BIT); +void ScanTarget::draw(bool synchronous) { + if(fence_ != nullptr) { + // if the GPU is still busy, don't wait; we'll catch it next time + if(glClientWaitSync(fence_, GL_SYNC_FLUSH_COMMANDS_BIT, synchronous ? GL_TIMEOUT_IGNORED : 0) == GL_TIMEOUT_EXPIRED) { + return; + } + fence_ = nullptr; + } // Grab the current read and submit pointers. const auto submit_pointers = submit_pointers_.load(); @@ -358,8 +364,11 @@ void ScanTarget::draw() { // TEST: draw all lines. glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(line_vertex_array_); test_shader_->bind(); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(scan_buffer_.size())); + + fence_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); } diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 1e64fdea3..2973bfa7b 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -27,7 +27,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { public: ScanTarget(); ~ScanTarget(); - void draw(); + void draw(bool synchronous); private: // Outputs::Display::ScanTarget overrides. @@ -130,6 +130,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { void enable_vertex_attributes(ShaderType type, Shader &target); std::unique_ptr test_shader_; + GLsync fence_ = nullptr; }; } diff --git a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp index d8fe3c505..76fc7be8d 100644 --- a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp +++ b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp @@ -45,8 +45,11 @@ std::string ScanTarget::glsl_globals(ShaderType type) { } std::string ScanTarget::glsl_default_vertex_shader(ShaderType type) { -// switch(type) { -// case ShaderType::Scan: + switch(type) { + case ShaderType::Scan: + return ""; + + case ShaderType::Line: return "void main(void) {" "float lateral = float(gl_VertexID & 1);" @@ -57,10 +60,7 @@ std::string ScanTarget::glsl_default_vertex_shader(ShaderType type) { "vec2 eyePosition = vec2(-0.9, 0.9) + vec2(1.8, -1.8) * (centrePoint + height);" "gl_Position = vec4(eyePosition, 0.0, 1.0);" "}"; - -// case ShaderType::Line: -// return ""; -// } + } } void ScanTarget::enable_vertex_attributes(ShaderType type, Shader &target) { From 2adf3d353ee29da3993e6ae7ba0a2b685af416b2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Nov 2018 20:20:09 -0500 Subject: [PATCH 031/208] Subtracts retrace periods from output scale. --- .../xcshareddata/xcschemes/Clock Signal.xcscheme | 2 +- Outputs/CRT/CRT.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 782b17c09..f9049689f 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -68,7 +68,7 @@ > 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; + const int real_clock_scan_period = vertical_flywheel_->get_scan_period(); vertical_flywheel_output_divider_ = (real_clock_scan_period + 65534) / 65535; // Communicate relevant fields to the scan target. - 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_.output_scale.x = uint16_t(horizontal_flywheel_->get_scan_period()); + scan_target_modals_.output_scale.y = uint16_t(real_clock_scan_period / 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_); From ecb5504bd106a4fb691c6eea7d96f8a156b8bb14 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Nov 2018 22:51:44 -0500 Subject: [PATCH 032/208] Switches `enable_vertex_attribute_with_pointer` to silent failure (versus glGetError). --- Outputs/OpenGL/Primitives/Shader.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Outputs/OpenGL/Primitives/Shader.cpp b/Outputs/OpenGL/Primitives/Shader.cpp index 879ca2167..23a10351d 100644 --- a/Outputs/OpenGL/Primitives/Shader.cpp +++ b/Outputs/OpenGL/Primitives/Shader.cpp @@ -104,9 +104,11 @@ GLint Shader::get_uniform_location(const std::string &name) { void Shader::enable_vertex_attribute_with_pointer(const std::string &name, GLint size, GLenum type, GLboolean normalised, GLsizei stride, const GLvoid *pointer, GLuint divisor) { GLint location = get_attrib_location(name); - glEnableVertexAttribArray((GLuint)location); - glVertexAttribPointer((GLuint)location, size, type, normalised, stride, pointer); - glVertexAttribDivisor((GLuint)location, divisor); + if(location >= 0) { + glEnableVertexAttribArray((GLuint)location); + glVertexAttribPointer((GLuint)location, size, type, normalised, stride, pointer); + glVertexAttribDivisor((GLuint)location, divisor); + } } // The various set_uniforms... From 654a19ea15148369fb0ff2b71480f06e39be6a98 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Nov 2018 22:52:26 -0500 Subject: [PATCH 033/208] Switches back to working on the scan shaders. Pixels from the emulated machine are now starting to appear. --- Outputs/OpenGL/ScanTarget.cpp | 32 ++++++++++++++++++---- Outputs/OpenGL/ScanTarget.hpp | 1 + Outputs/OpenGL/ScanTargetGLSLFragments.cpp | 14 +++++++++- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index b1a569d13..97a37e5f0 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -91,18 +91,25 @@ ScanTarget::ScanTarget() : glGenTextures(1, &write_area_texture_name_); test_shader_.reset(new Shader( - glsl_globals(ShaderType::Line) + glsl_default_vertex_shader(ShaderType::Line), + glsl_globals(ShaderType::Scan) + glsl_default_vertex_shader(ShaderType::Scan), "#version 150\n" + "out vec4 fragColour;" + "in vec2 textureCoordinate;" + + "uniform usampler2D textureName;" + "void main(void) {" - "fragColour = vec4(1.0);" + "fragColour = vec4(float(texture(textureName, textureCoordinate).r), 0.0, 0.0, 1.0);" "}" )); - glBindVertexArray(line_vertex_array_); - enable_vertex_attributes(ShaderType::Line, *test_shader_); + glBindVertexArray(scan_vertex_array_); + glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); + enable_vertex_attributes(ShaderType::Scan, *test_shader_); } ScanTarget::~ScanTarget() { + while(is_drawing_.test_and_set()) {} glDeleteBuffers(1, &scan_buffer_name_); glDeleteTextures(1, &write_area_texture_name_); glDeleteVertexArrays(1, &scan_vertex_array_); @@ -127,6 +134,7 @@ void ScanTarget::set_modals(Modals modals) { // TODO: this, but not to the test shader. test_shader_->set_uniform("scale", GLfloat(modals.output_scale.x), GLfloat(modals.output_scale.y)); test_shader_->set_uniform("rowHeight", GLfloat(1.0f / modals.expected_vertical_lines)); + test_shader_->set_uniform("textureName", GLint(SourceData1BppTextureUnit - GL_TEXTURE0)); } Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() { @@ -155,6 +163,7 @@ Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() { void ScanTarget::end_scan() { if(vended_scan_) { vended_scan_->data_y = TextureAddressGetY(vended_write_area_pointer_); + vended_scan_->line = write_pointers_.line; vended_scan_->scan.end_points[0].data_offset += TextureAddressGetX(vended_write_area_pointer_); vended_scan_->scan.end_points[1].data_offset += TextureAddressGetX(vended_write_area_pointer_); } @@ -203,6 +212,8 @@ uint8_t *ScanTarget::begin_data(size_t required_length, size_t required_alignmen void ScanTarget::end_data(size_t actual_length) { if(allocation_has_failed_) return; +// memset(&write_area_texture_[size_t(write_pointers_.write_area) * data_type_size_], 0xff, actual_length * data_type_size_); + // The write area was allocated in the knowledge that there's sufficient // distance left on the current line, so there's no need to worry about carry. write_pointers_.write_area += actual_length + 1; @@ -248,6 +259,9 @@ void ScanTarget::announce(Event event, uint16_t x, uint16_t y) { } } break; } + + // TODO: any lines that include any portion of vertical sync should be hidden. + // (maybe set a flag and zero out the line coordinates?) } template void ScanTarget::submit_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer) { @@ -295,6 +309,8 @@ void ScanTarget::draw(bool synchronous) { fence_ = nullptr; } + if(is_drawing_.test_and_set()) return; + // Grab the current read and submit pointers. const auto submit_pointers = submit_pointers_.load(); const auto read_pointers = read_pointers_.load(); @@ -305,11 +321,16 @@ void ScanTarget::draw(bool synchronous) { // Submit texture. if(submit_pointers.write_area != read_pointers.write_area) { + glActiveTexture(SourceData1BppTextureUnit); glBindTexture(GL_TEXTURE_2D, write_area_texture_name_); // Create storage for the texture if it doesn't yet exist; this was deferred until here // because the pixel format wasn't initially known. if(!texture_exists_) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D( GL_TEXTURE_2D, 0, @@ -366,9 +387,10 @@ void ScanTarget::draw(bool synchronous) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glClear(GL_COLOR_BUFFER_BIT); - glBindVertexArray(line_vertex_array_); + glBindVertexArray(scan_vertex_array_); test_shader_->bind(); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(scan_buffer_.size())); fence_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + is_drawing_.clear(); } diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 2973bfa7b..578c0e71f 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -131,6 +131,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { std::unique_ptr test_shader_; GLsync fence_ = nullptr; + std::atomic_flag is_drawing_; }; } diff --git a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp index 76fc7be8d..5c9fc9564 100644 --- a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp +++ b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp @@ -47,7 +47,19 @@ std::string ScanTarget::glsl_globals(ShaderType type) { std::string ScanTarget::glsl_default_vertex_shader(ShaderType type) { switch(type) { case ShaderType::Scan: - return ""; + return + "out vec2 textureCoordinate;" + "uniform usampler2D textureName;" + + "void main(void) {" + "float lateral = float(gl_VertexID & 1);" + "float longitudinal = float((gl_VertexID & 2) >> 1);" + + "textureCoordinate = vec2(mix(startDataX, endDataX, lateral), dataY) / textureSize(textureName, 0);" + + "vec2 eyePosition = vec2(mix(startPoint.x, endPoint.x, lateral), lineY + longitudinal) / vec2(scale.x, 2048.0);" + "gl_Position = vec4(eyePosition*2 - vec2(1.0), 0.0, 1.0);" + "}"; case ShaderType::Line: return From 02f9cada43ef6d38df1581e96bf77606202df5b9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Nov 2018 18:33:44 -0500 Subject: [PATCH 034/208] Communicates the colour subcarrier frequency, and uses it to pick a buffer width. --- Outputs/CRT/CRT.cpp | 2 ++ Outputs/OpenGL/ScanTarget.cpp | 9 +++++++++ Outputs/OpenGL/ScanTarget.hpp | 4 +++- Outputs/ScanTarget.hpp | 3 +++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 4db4d30e9..4bd136496 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -62,6 +62,8 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di scan_target_modals_.output_scale.y = uint16_t(real_clock_scan_period / vertical_flywheel_output_divider_); scan_target_modals_.expected_vertical_lines = height_of_display; scan_target_modals_.composite_colour_space = colour_space; + scan_target_modals_.colour_cycle_numerator = colour_cycle_numerator; + scan_target_modals_.colour_cycle_denominator = colour_cycle_denominator; scan_target_->set_modals(scan_target_modals_); } diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 97a37e5f0..49d0bb93b 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -131,6 +131,15 @@ void ScanTarget::set_modals(Modals modals) { write_pointers_.write_area = 0; } + // Pick a processing width; this will be at least four times the + // colour subcarrier, and an integer multiple of the pixel clock and + // at most 2048. + const int colour_cycle_width = (modals.colour_cycle_numerator * 4 + modals.colour_cycle_denominator - 1) / modals.colour_cycle_denominator; + const int dot_clock = modals.cycles_per_line / modals.clocks_per_pixel_greatest_common_divisor; + const int overflow = colour_cycle_width % dot_clock; + processing_width_ = colour_cycle_width + (overflow ? dot_clock - overflow : 0); + processing_width_ = std::min(processing_width_, 2048); + // TODO: this, but not to the test shader. test_shader_->set_uniform("scale", GLfloat(modals.output_scale.x), GLfloat(modals.output_scale.y)); test_shader_->set_uniform("rowHeight", GLfloat(1.0f / modals.expected_vertical_lines)); diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 578c0e71f..e4c6382a9 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -129,9 +129,11 @@ class ScanTarget: public Outputs::Display::ScanTarget { */ void enable_vertex_attributes(ShaderType type, Shader &target); - std::unique_ptr test_shader_; GLsync fence_ = nullptr; std::atomic_flag is_drawing_; + + int processing_width_ = 0; + std::unique_ptr test_shader_; }; } diff --git a/Outputs/ScanTarget.hpp b/Outputs/ScanTarget.hpp index 29fcf28a2..4f880adc5 100644 --- a/Outputs/ScanTarget.hpp +++ b/Outputs/ScanTarget.hpp @@ -129,6 +129,9 @@ struct ScanTarget { /// the @c cycles_per_line are offered for sizing of intermediary buffers. int clocks_per_pixel_greatest_common_divisor; + /// Provides the number of colour cycles in a line, as a quotient. + int colour_cycle_numerator, colour_cycle_denominator; + /// 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; From bf3ab4e260fbcaadc2b089edea11f1fd3dded4de Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Nov 2018 21:15:33 -0500 Subject: [PATCH 035/208] Proceeds as drawing to the unprocessed line buffer and drawing from it. Very, very slowly, and without yet clearing. --- .../Mac/Clock Signal/Machine/CSMachine.mm | 2 +- Outputs/OpenGL/ScanTarget.cpp | 102 ++++++++++++++---- Outputs/OpenGL/ScanTarget.hpp | 8 +- Outputs/OpenGL/ScanTargetGLSLFragments.cpp | 20 +++- 4 files changed, 104 insertions(+), 28 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 0f1a7d7cf..a574b5db4 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -244,7 +244,7 @@ struct ActivityObserver: public Activity::Observer { } - (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty { - _scanTarget->draw(onlyIfDirty ? false : true); + _scanTarget->draw(onlyIfDirty ? false : true, (int)pixelSize.width, (int)pixelSize.height); // _machine->crt_machine()->get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); } diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 49d0bb93b..327aeff36 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -90,7 +90,7 @@ ScanTarget::ScanTarget() : glGenTextures(1, &write_area_texture_name_); - test_shader_.reset(new Shader( + input_shader_.reset(new Shader( glsl_globals(ShaderType::Scan) + glsl_default_vertex_shader(ShaderType::Scan), "#version 150\n" @@ -103,9 +103,28 @@ ScanTarget::ScanTarget() : "fragColour = vec4(float(texture(textureName, textureCoordinate).r), 0.0, 0.0, 1.0);" "}" )); + glBindVertexArray(scan_vertex_array_); glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); - enable_vertex_attributes(ShaderType::Scan, *test_shader_); + enable_vertex_attributes(ShaderType::Scan, *input_shader_); + + output_shader_.reset(new Shader( + glsl_globals(ShaderType::Line) + glsl_default_vertex_shader(ShaderType::Line), + "#version 150\n" + + "out vec4 fragColour;" + "in vec2 textureCoordinate;" + + "uniform sampler2D textureName;" + + "void main(void) {" + "fragColour = texture(textureName, textureCoordinate);" + "}" + )); + + glBindVertexArray(line_vertex_array_); + glBindBuffer(GL_ARRAY_BUFFER, line_buffer_name_); + enable_vertex_attributes(ShaderType::Line, *output_shader_); } ScanTarget::~ScanTarget() { @@ -140,10 +159,17 @@ void ScanTarget::set_modals(Modals modals) { processing_width_ = colour_cycle_width + (overflow ? dot_clock - overflow : 0); processing_width_ = std::min(processing_width_, 2048); - // TODO: this, but not to the test shader. - test_shader_->set_uniform("scale", GLfloat(modals.output_scale.x), GLfloat(modals.output_scale.y)); - test_shader_->set_uniform("rowHeight", GLfloat(1.0f / modals.expected_vertical_lines)); - test_shader_->set_uniform("textureName", GLint(SourceData1BppTextureUnit - GL_TEXTURE0)); + set_uniforms(Outputs::Display::OpenGL::ScanTarget::ShaderType::Scan, *output_shader_); + set_uniforms(Outputs::Display::OpenGL::ScanTarget::ShaderType::Line, *input_shader_); + + input_shader_->set_uniform("textureName", GLint(SourceData1BppTextureUnit - GL_TEXTURE0)); + output_shader_->set_uniform("textureName", GLint(UnprocessedLineBufferTextureUnit - GL_TEXTURE0)); +} + +void Outputs::Display::OpenGL::ScanTarget::set_uniforms(ShaderType type, Shader &target) { + target.set_uniform("scale", GLfloat(modals_.output_scale.x), GLfloat(modals_.output_scale.y)); + target.set_uniform("rowHeight", GLfloat(1.0f / modals_.expected_vertical_lines)); + target.set_uniform("processingWidth", GLfloat(processing_width_) / 2048.0f); } Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() { @@ -273,7 +299,7 @@ void ScanTarget::announce(Event event, uint16_t x, uint16_t y) { // (maybe set a flag and zero out the line coordinates?) } -template void ScanTarget::submit_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer) { +template void ScanTarget::patch_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer) { if(submit_pointer != read_pointer) { // Bind the buffer and map it into CPU space. glBindBuffer(GL_ARRAY_BUFFER, target); @@ -283,7 +309,7 @@ template void ScanTarget::submit_buffer(const T &array, GLuint targ ); assert(destination); - if(submit_pointer > read_pointer) { + if(read_pointer < submit_pointer) { // Submit the direct region from the submit pointer to the read pointer. const size_t offset = read_pointer * sizeof(array[0]); const size_t length = (submit_pointer - read_pointer) * sizeof(array[0]); @@ -309,7 +335,7 @@ template void ScanTarget::submit_buffer(const T &array, GLuint targ } } -void ScanTarget::draw(bool synchronous) { +void ScanTarget::draw(bool synchronous, int output_width, int output_height) { if(fence_ != nullptr) { // if the GPU is still busy, don't wait; we'll catch it next time if(glClientWaitSync(fence_, GL_SYNC_FLUSH_COMMANDS_BIT, synchronous ? GL_TIMEOUT_IGNORED : 0) == GL_TIMEOUT_EXPIRED) { @@ -324,9 +350,31 @@ void ScanTarget::draw(bool synchronous) { const auto submit_pointers = submit_pointers_.load(); const auto read_pointers = read_pointers_.load(); - // Submit scans and lines. - submit_buffer(scan_buffer_, scan_buffer_name_, submit_pointers.scan_buffer, read_pointers.scan_buffer); - submit_buffer(line_buffer_, line_buffer_name_, submit_pointers.line, read_pointers.line); + // Submit scans and lines; TODO: for lines, rotate in. + patch_buffer(line_buffer_, line_buffer_name_, submit_pointers.line, read_pointers.line); + + size_t new_scans = (submit_pointers.scan_buffer + scan_buffer_.size() - read_pointers.scan_buffer) % scan_buffer_.size(); + if(new_scans) { + glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); + + // Map only the required portion of the buffer. + const size_t new_scans_size = new_scans * sizeof(Scan); + uint8_t *const destination = static_cast( + glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(new_scans_size), GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT) + ); + + if(read_pointers.scan_buffer < submit_pointers.scan_buffer) { + memcpy(destination, &scan_buffer_[read_pointers.scan_buffer], new_scans_size); + } else { + const size_t first_portion_length = (scan_buffer_.size() - read_pointers.scan_buffer) * sizeof(Scan); + memcpy(destination, &scan_buffer_[read_pointers.scan_buffer], first_portion_length); + memcpy(&destination[first_portion_length], &scan_buffer_[0], new_scans_size - first_portion_length); + } + + // Unmap the buffer. + glUnmapBuffer(GL_ARRAY_BUFFER); + } +// patch_buffer(scan_buffer_, scan_buffer_name_, submit_pointers.scan_buffer, read_pointers.scan_buffer); // Submit texture. if(submit_pointers.write_area != read_pointers.write_area) { @@ -384,22 +432,32 @@ void ScanTarget::draw(bool synchronous) { } } - // TODO: clear composite buffer (if needed). - // TODO: drawing (!) + // Push new input to the unprocessed line buffer. + if(new_scans) { + unprocessed_line_texture_.bind_framebuffer(); + glBindVertexArray(scan_vertex_array_); + input_shader_->bind(); + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(new_scans)); + } + // Clear the target framebuffer (TODO: don't assume 0). + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height); + glClear(GL_COLOR_BUFFER_BIT); + +// unprocessed_line_texture_.draw(1.0f); + + // Output all lines. + glBindVertexArray(line_vertex_array_); + output_shader_->bind(); + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(line_buffer_.size())); // All data now having been spooled to the GPU, update the read pointers to // the submit pointer location. read_pointers_.store(submit_pointers); - // TEST: draw all lines. - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glClear(GL_COLOR_BUFFER_BIT); - - glBindVertexArray(scan_vertex_array_); - test_shader_->bind(); - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(scan_buffer_.size())); - + // Grab a fence sync object to avoid busy waiting upon the next extry into this + // function, and reset the is_drawing_ flag. fence_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); is_drawing_.clear(); } diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index e4c6382a9..7f5f0a203 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -27,7 +27,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { public: ScanTarget(); ~ScanTarget(); - void draw(bool synchronous); + void draw(bool synchronous, int output_width, int output_height); private: // Outputs::Display::ScanTarget overrides. @@ -87,7 +87,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { GLuint line_buffer_name_ = 0, line_vertex_array_ = 0; template void allocate_buffer(const T &array, GLuint &buffer_name, GLuint &vertex_array_name); - template void submit_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer); + template void patch_buffer(const T &array, GLuint target, uint16_t submit_pointer, uint16_t read_pointer); // Uses a texture to vend write areas. std::vector write_area_texture_; @@ -128,12 +128,14 @@ class ScanTarget: public Outputs::Display::ScanTarget { globals for shaders of @c type to @c target. */ void enable_vertex_attributes(ShaderType type, Shader &target); + void set_uniforms(ShaderType type, Shader &target); GLsync fence_ = nullptr; std::atomic_flag is_drawing_; int processing_width_ = 0; - std::unique_ptr test_shader_; + std::unique_ptr input_shader_; + std::unique_ptr output_shader_; }; } diff --git a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp index 5c9fc9564..2d0d768c6 100644 --- a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp +++ b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp @@ -20,6 +20,7 @@ std::string ScanTarget::glsl_globals(ShaderType type) { "uniform float rowHeight;" "uniform mat3 lumaChromaToRGB;" "uniform mat3 rgbToLumaChroma;" + "uniform float processingWidth;" "in vec2 startPoint;" "in float startDataX;" @@ -38,9 +39,12 @@ std::string ScanTarget::glsl_globals(ShaderType type) { "uniform vec2 scale;" "uniform float rowHeight;" + "uniform float processingWidth;" "in vec2 startPoint;" - "in vec2 endPoint;"; + "in vec2 endPoint;" + + "in float lineY;"; } } @@ -57,16 +61,21 @@ std::string ScanTarget::glsl_default_vertex_shader(ShaderType type) { "textureCoordinate = vec2(mix(startDataX, endDataX, lateral), dataY) / textureSize(textureName, 0);" - "vec2 eyePosition = vec2(mix(startPoint.x, endPoint.x, lateral), lineY + longitudinal) / vec2(scale.x, 2048.0);" + "vec2 eyePosition = vec2(mix(startPoint.x, endPoint.x, lateral) * processingWidth, lineY + longitudinal) / vec2(scale.x, 2048.0);" "gl_Position = vec4(eyePosition*2 - vec2(1.0), 0.0, 1.0);" "}"; case ShaderType::Line: return + "out vec2 textureCoordinate;" + "uniform sampler2D textureName;" + "void main(void) {" "float lateral = float(gl_VertexID & 1);" "float longitudinal = float((gl_VertexID & 2) >> 1);" + "textureCoordinate = vec2(lateral * processingWidth, lineY) / vec2(1.0, textureSize(textureName, 0).y);" + "vec2 centrePoint = mix(startPoint, endPoint, lateral) / scale;" "vec2 height = normalize(endPoint - startPoint).yx * (longitudinal - 0.5) * rowHeight;" "vec2 eyePosition = vec2(-0.9, 0.9) + vec2(1.8, -1.8) * (centrePoint + height);" @@ -126,6 +135,13 @@ void ScanTarget::enable_vertex_attributes(ShaderType type, Shader &target) { reinterpret_cast(offsetof(Line, end_points[c].x)), 1); } + + target.enable_vertex_attribute_with_pointer( + "lineY", + 1, GL_UNSIGNED_SHORT, GL_FALSE, + sizeof(Line), + reinterpret_cast(offsetof(Line, line)), + 1); break; } } From 6ba02c44d02c27e65ca3997e5ce16465b218aa21 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Nov 2018 23:08:51 -0500 Subject: [PATCH 036/208] Better binds buffer sizes. --- Outputs/OpenGL/ScanTarget.cpp | 5 ----- Outputs/OpenGL/ScanTarget.hpp | 8 +++++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 327aeff36..671d5ab6b 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -13,11 +13,6 @@ using namespace Outputs::Display::OpenGL; namespace { -constexpr int WriteAreaWidth = 2048; -constexpr int WriteAreaHeight = 2048; - -constexpr int LineBufferWidth = 2048; -constexpr int LineBufferHeight = 2048; /// The texture unit from which to source 1bpp input data. constexpr GLenum SourceData1BppTextureUnit = GL_TEXTURE0; diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 7f5f0a203..c0377a50f 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -30,6 +30,12 @@ class ScanTarget: public Outputs::Display::ScanTarget { void draw(bool synchronous, int output_width, int output_height); private: + static constexpr int WriteAreaWidth = 2048; + static constexpr int WriteAreaHeight = 2048; + + static constexpr int LineBufferWidth = 2048; + static constexpr int LineBufferHeight = 2048; + // Outputs::Display::ScanTarget overrides. void set_modals(Modals) override; Scan *begin_scan() override; @@ -78,7 +84,7 @@ class ScanTarget: public Outputs::Display::ScanTarget { } end_points[2]; uint16_t line; }; - std::array line_buffer_; + std::array line_buffer_; TextureTarget unprocessed_line_texture_; Line *active_line_ = nullptr; From 3dca836571a4778ef4a4be1f963916446d82887a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Nov 2018 20:09:57 -0500 Subject: [PATCH 037/208] Ensures no overflow, and adds a couple of `const`s. --- Outputs/CRT/Internals/Flywheel.hpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Outputs/CRT/Internals/Flywheel.hpp b/Outputs/CRT/Internals/Flywheel.hpp index 5edc82947..dab8a5d61 100644 --- a/Outputs/CRT/Internals/Flywheel.hpp +++ b/Outputs/CRT/Internals/Flywheel.hpp @@ -10,6 +10,7 @@ #define Flywheel_hpp #include +#include namespace Outputs { namespace CRT { @@ -33,10 +34,8 @@ struct Flywheel { standard_period_(standard_period), retrace_time_(retrace_time), sync_error_window_(sync_error_window), - counter_(0), counter_before_retrace_(standard_period - retrace_time), - expected_next_sync_(standard_period), - number_of_surprises_(0) {} + expected_next_sync_(standard_period) {} enum SyncEvent { /// Indicates that no synchronisation events will occur in the queried window. @@ -64,10 +63,10 @@ struct Flywheel { // 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_) { - int time_now = (counter_ < sync_error_window_) ? expected_next_sync_ + counter_ : counter_; + const 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_++; + ++number_of_surprises_; if(counter_ < retrace_time_ + (expected_next_sync_ >> 1)) { expected_next_sync_ = (3*expected_next_sync_ + standard_period_ + sync_error_window_) >> 2; @@ -124,7 +123,7 @@ struct Flywheel { */ inline int get_current_output_position() { if(counter_ < retrace_time_) { - int retrace_distance = (counter_ * standard_period_) / retrace_time_; + const int retrace_distance = int((int64_t(counter_) * int64_t(standard_period_)) / int64_t(retrace_time_)); if(retrace_distance > counter_before_retrace_) return 0; return counter_before_retrace_ - retrace_distance; } @@ -182,11 +181,11 @@ struct Flywheel { 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 - int counter_; // time since the _start_ of the last sync + int counter_ = 0; // 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) - int number_of_surprises_; // a count of the surprising syncs + int number_of_surprises_ = 0; // a count of the surprising syncs /* Implementation notes: From 0487580a1a1d0453efd2046f5443dac3ae6b1c8f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Nov 2018 20:10:38 -0500 Subject: [PATCH 038/208] Corrects initial state of `is_drawing_` and expands lines to full display. --- Outputs/OpenGL/ScanTarget.cpp | 33 +++++++++++++++------- Outputs/OpenGL/ScanTargetGLSLFragments.cpp | 2 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 671d5ab6b..d29b7c0ee 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -120,6 +120,8 @@ ScanTarget::ScanTarget() : glBindVertexArray(line_vertex_array_); glBindBuffer(GL_ARRAY_BUFFER, line_buffer_name_); enable_vertex_attributes(ShaderType::Line, *output_shader_); + + is_drawing_.clear(); } ScanTarget::~ScanTarget() { @@ -298,19 +300,26 @@ template void ScanTarget::patch_buffer(const T &array, GLuint targe if(submit_pointer != read_pointer) { // Bind the buffer and map it into CPU space. glBindBuffer(GL_ARRAY_BUFFER, target); + const auto buffer_size = array.size() * sizeof(array[0]); uint8_t *destination = static_cast( glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT) ); assert(destination); - if(read_pointer < submit_pointer) { + // Populate it with the oldest scans first; the oldest are those from 1 beyond the submit pointer. + const size_t buffer_length = array.size() * sizeof(array[0]); + const size_t splice_point = (submit_pointer + 1) * sizeof(array[0]); + const size_t end_length = buffer_length - splice_point; + + memcpy(&destination[0], &array[submit_pointer+1], end_length); + memcpy(&destination[end_length], &array[0], buffer_length - end_length); + +/* if(read_pointer < submit_pointer) { // Submit the direct region from the submit pointer to the read pointer. const size_t offset = read_pointer * sizeof(array[0]); const size_t length = (submit_pointer - read_pointer) * sizeof(array[0]); - memcpy(&destination[offset], &array[read_pointer], length); - - glFlushMappedBufferRange(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(length)); + glBufferSubData(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(length), &array[read_pointer]); } else { // The circular buffer wrapped around; submit the data from the read pointer to the end of // the buffer and from the start of the buffer to the submit pointer. @@ -322,10 +331,13 @@ template void ScanTarget::patch_buffer(const T &array, GLuint targe memcpy(&destination[0], &array[0], start_length); glFlushMappedBufferRange(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(end_length)); - glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(start_length)); - } + glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(start_length));*/ + +// } // Unmap the buffer. +// glBufferSubData(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(length), &array[read_pointer]); + glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size)); glUnmapBuffer(GL_ARRAY_BUFFER); } } @@ -348,6 +360,7 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { // Submit scans and lines; TODO: for lines, rotate in. patch_buffer(line_buffer_, line_buffer_name_, submit_pointers.line, read_pointers.line); + // Submit scans; only the new ones need to be communicated. size_t new_scans = (submit_pointers.scan_buffer + scan_buffer_.size() - read_pointers.scan_buffer) % scan_buffer_.size(); if(new_scans) { glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_); @@ -366,10 +379,10 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { memcpy(&destination[first_portion_length], &scan_buffer_[0], new_scans_size - first_portion_length); } - // Unmap the buffer. + // Flush and unmap the buffer. + glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(new_scans_size)); glUnmapBuffer(GL_ARRAY_BUFFER); } -// patch_buffer(scan_buffer_, scan_buffer_name_, submit_pointers.scan_buffer, read_pointers.scan_buffer); // Submit texture. if(submit_pointers.write_area != read_pointers.write_area) { @@ -442,10 +455,10 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { // unprocessed_line_texture_.draw(1.0f); - // Output all lines. + // Output all lines except the one currently being worked on. glBindVertexArray(line_vertex_array_); output_shader_->bind(); - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(line_buffer_.size())); + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(line_buffer_.size() - 1)); // All data now having been spooled to the GPU, update the read pointers to // the submit pointer location. diff --git a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp index 2d0d768c6..3b10e6a0e 100644 --- a/Outputs/OpenGL/ScanTargetGLSLFragments.cpp +++ b/Outputs/OpenGL/ScanTargetGLSLFragments.cpp @@ -78,7 +78,7 @@ std::string ScanTarget::glsl_default_vertex_shader(ShaderType type) { "vec2 centrePoint = mix(startPoint, endPoint, lateral) / scale;" "vec2 height = normalize(endPoint - startPoint).yx * (longitudinal - 0.5) * rowHeight;" - "vec2 eyePosition = vec2(-0.9, 0.9) + vec2(1.8, -1.8) * (centrePoint + height);" + "vec2 eyePosition = vec2(-1.0, 1.0) + vec2(2.0, -2.0) * (centrePoint + height);" "gl_Position = vec4(eyePosition, 0.0, 1.0);" "}"; } From 91b19c5c7044ef64ec9d19785d0bbe3f1372ef19 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Nov 2018 20:49:06 -0500 Subject: [PATCH 039/208] Adds bookending, and finally kills the TextureBuilder. Farewell. --- .../Clock Signal.xcodeproj/project.pbxproj | 4 - Outputs/OpenGL/ScanTarget.cpp | 16 +- Outputs/OpenGL/TextureBuilder.cpp | 179 ------------------ Outputs/OpenGL/TextureBuilder.hpp | 132 ------------- 4 files changed, 15 insertions(+), 316 deletions(-) delete mode 100644 Outputs/OpenGL/TextureBuilder.cpp delete mode 100644 Outputs/OpenGL/TextureBuilder.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 586b1c813..48f765f78 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1365,10 +1365,8 @@ 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Oric.cpp; path = Oric/Oric.cpp; sourceTree = ""; }; 4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Oric/Oric.hpp; sourceTree = ""; }; 4BD060A51FE49D3C006E14BE /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = ""; }; - 4BD191D8219113B80042E144 /* TextureBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureBuilder.cpp; sourceTree = ""; }; 4BD191D9219113B80042E144 /* OpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OpenGL.hpp; sourceTree = ""; }; 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTOpenGL.cpp; sourceTree = ""; }; - 4BD191DD219113B80042E144 /* TextureBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureBuilder.hpp; sourceTree = ""; }; 4BD191E0219113B80042E144 /* IntermediateShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IntermediateShader.hpp; sourceTree = ""; }; 4BD191E1219113B80042E144 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; 4BD191E3219113B80042E144 /* IntermediateShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntermediateShader.cpp; sourceTree = ""; }; @@ -3028,10 +3026,8 @@ 4BD191DC219113B80042E144 /* CRTOpenGL.cpp */, 4BD191F22191180E0042E144 /* ScanTarget.cpp */, 4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */, - 4BD191D8219113B80042E144 /* TextureBuilder.cpp */, 4BD191D9219113B80042E144 /* OpenGL.hpp */, 4BD191F32191180E0042E144 /* ScanTarget.hpp */, - 4BD191DD219113B80042E144 /* TextureBuilder.hpp */, 4BD424DC2193B5340097291A /* Primitives */, 4BD191DF219113B80042E144 /* Shaders */, ); diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index d29b7c0ee..b7eebe49c 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -75,6 +75,10 @@ template void ScanTarget::allocate_buffer(const T &array, GLuint &b ScanTarget::ScanTarget() : unprocessed_line_texture_(LineBufferWidth, LineBufferHeight, UnprocessedLineBufferTextureUnit, GL_LINEAR) { + // Ensure proper initialisation of the two atomic pointer sets. + read_pointers_.store(write_pointers_); + submit_pointers_.store(write_pointers_); + // Allocate space for the scans and lines. allocate_buffer(scan_buffer_, scan_buffer_name_, scan_vertex_array_); allocate_buffer(line_buffer_, line_buffer_name_, line_vertex_array_); @@ -244,11 +248,21 @@ uint8_t *ScanTarget::begin_data(size_t required_length, size_t required_alignmen void ScanTarget::end_data(size_t actual_length) { if(allocation_has_failed_) return; -// memset(&write_area_texture_[size_t(write_pointers_.write_area) * data_type_size_], 0xff, actual_length * data_type_size_); + // Bookend the start of the new data, to safeguard for precision errors in sampling. + memcpy( + &write_area_texture_[size_t(write_pointers_.write_area - 1) * data_type_size_], + &write_area_texture_[size_t(write_pointers_.write_area) * data_type_size_], + data_type_size_); // The write area was allocated in the knowledge that there's sufficient // distance left on the current line, so there's no need to worry about carry. write_pointers_.write_area += actual_length + 1; + + // Also bookend the end. + memcpy( + &write_area_texture_[size_t(write_pointers_.write_area) * data_type_size_], + &write_area_texture_[size_t(write_pointers_.write_area - 1) * data_type_size_], + data_type_size_); } void ScanTarget::submit() { diff --git a/Outputs/OpenGL/TextureBuilder.cpp b/Outputs/OpenGL/TextureBuilder.cpp deleted file mode 100644 index 102653771..000000000 --- a/Outputs/OpenGL/TextureBuilder.cpp +++ /dev/null @@ -1,179 +0,0 @@ -// -// TextureBuilder.cpp -// Clock Signal -// -// Created by Thomas Harte on 08/03/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#include "TextureBuilder.hpp" -#include "CRTOpenGL.hpp" -#include "OpenGL.hpp" - -#include - -using namespace Outputs::CRT; - -namespace { - -const GLint internalFormatForDepth(std::size_t depth) { - switch(depth) { - default: return GL_FALSE; - case 1: return GL_R8UI; - case 2: return GL_RG8UI; - case 3: return GL_RGB8UI; - case 4: return GL_RGBA8UI; - } -} - -const GLenum formatForDepth(std::size_t depth) { - switch(depth) { - default: return GL_FALSE; - case 1: return GL_RED_INTEGER; - case 2: return GL_RG_INTEGER; - case 3: return GL_RGB_INTEGER; - case 4: return GL_RGBA_INTEGER; - } -} - -} - -TextureBuilder::TextureBuilder(std::size_t bytes_per_pixel, GLenum texture_unit) : - bytes_per_pixel_(bytes_per_pixel), texture_unit_(texture_unit) { - image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight); - glGenTextures(1, &texture_name_); - - bind(); - glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(bytes_per_pixel), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(bytes_per_pixel), GL_UNSIGNED_BYTE, nullptr); -} - -TextureBuilder::~TextureBuilder() { - glDeleteTextures(1, &texture_name_); -} - -void TextureBuilder::bind() { - glActiveTexture(texture_unit_); - glBindTexture(GL_TEXTURE_2D, texture_name_); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); -} - -inline uint8_t *TextureBuilder::pointer_to_location(uint16_t x, uint16_t y) { - return &image_[((y * InputBufferBuilderWidth) + x) * bytes_per_pixel_]; -} - -uint8_t *TextureBuilder::allocate_write_area(std::size_t required_length, std::size_t required_alignment) { - // Keep a flag to indicate whether the buffer was full at allocate_write_area; if it was then - // don't return anything now, and decline to act upon follow-up methods. is_full_ may be reset - // by asynchronous calls to submit. was_full_ will not be touched by it. - was_full_ = is_full_; - if(is_full_) return nullptr; - - // If there's not enough space on this line, move to the next. If the next is where the current - // submission group started, trigger is/was_full_ and return nothing. - std::size_t alignment_offset = (required_alignment - ((write_areas_start_x_ + 1) % required_alignment)) % required_alignment; - if(write_areas_start_x_ + required_length + 2 + alignment_offset > InputBufferBuilderWidth) { - write_areas_start_x_ = 0; - alignment_offset = required_alignment - 1; - write_areas_start_y_ = (write_areas_start_y_ + 1) % InputBufferBuilderHeight; - - if(write_areas_start_y_ == first_unsubmitted_y_) { - was_full_ = is_full_ = true; - return nullptr; - } - } - - // Queue up the latest write area. - write_areas_start_x_ += static_cast(alignment_offset); - write_area_.x = write_areas_start_x_ + 1; - write_area_.y = write_areas_start_y_; - write_area_.length = static_cast(required_length); - - // Return a video pointer. - return pointer_to_location(write_area_.x, write_area_.y); -} - -void TextureBuilder::reduce_previous_allocation_to(std::size_t actual_length) { - // If the previous allocate_write_area declined to act, decline also. - if(was_full_) return; - - // Update the length of the current write area. - write_area_.length = static_cast(actual_length); - - // Bookend the allocation with duplicates of the first and last pixel, to protect - // against rounding errors when this run is drawn. - uint8_t *start_pointer = pointer_to_location(write_area_.x, write_area_.y) - bytes_per_pixel_; - std::memcpy(start_pointer, &start_pointer[bytes_per_pixel_], bytes_per_pixel_); - std::memcpy(&start_pointer[(actual_length + 1) * bytes_per_pixel_], &start_pointer[actual_length * bytes_per_pixel_], bytes_per_pixel_); -} - -bool TextureBuilder::retain_latest() { - // If the previous allocate_write_area declined to act, decline also. - if(was_full_) return false; - - // Account for the most recently written area as taken. - write_areas_start_x_ += write_area_.length + 2; - - // Store into the vector directly if there's already room, otherwise grow the vector. - // Probably I don't need to mess about with this myself; it's unnecessary second-guessing. - // TODO: profile and prove. - if(number_of_write_areas_ < write_areas_.size()) - write_areas_[number_of_write_areas_] = write_area_; - else - write_areas_.push_back(write_area_); - number_of_write_areas_++; - - return true; -} - -void TextureBuilder::discard_latest() { - if(was_full_) return; - number_of_write_areas_--; -} - -bool TextureBuilder::is_full() { - return is_full_; -} - -void TextureBuilder::submit() { - if(write_areas_start_y_ < first_unsubmitted_y_) { - // A write area start y less than the first line on which submissions began implies it must have wrapped - // around. So the submission set is everything back to zero before the current write area plus everything - // from the first unsubmitted y downward. - uint16_t height = write_areas_start_y_ + (write_areas_start_x_ ? 1 : 0); - glTexSubImage2D( GL_TEXTURE_2D, 0, - 0, 0, - InputBufferBuilderWidth, height, - formatForDepth(bytes_per_pixel_), GL_UNSIGNED_BYTE, - image_.data()); - - glTexSubImage2D( GL_TEXTURE_2D, 0, - 0, first_unsubmitted_y_, - InputBufferBuilderWidth, InputBufferBuilderHeight - first_unsubmitted_y_, - formatForDepth(bytes_per_pixel_), GL_UNSIGNED_BYTE, - image_.data() + first_unsubmitted_y_ * bytes_per_pixel_ * InputBufferBuilderWidth); - } else { - // If the current write area start y is after the first unsubmitted line, just submit the region in between. - uint16_t height = write_areas_start_y_ + (write_areas_start_x_ ? 1 : 0) - first_unsubmitted_y_; - glTexSubImage2D( GL_TEXTURE_2D, 0, - 0, first_unsubmitted_y_, - InputBufferBuilderWidth, height, - formatForDepth(bytes_per_pixel_), GL_UNSIGNED_BYTE, - image_.data() + first_unsubmitted_y_ * bytes_per_pixel_ * InputBufferBuilderWidth); - } - - // Update the starting location for the next submission, and mark definitively that the buffer is once again not full. - first_unsubmitted_y_ = write_areas_start_y_; - is_full_ = false; -} - -void TextureBuilder::flush(const std::function &write_areas, std::size_t count)> &function) { - // Just throw everything currently in the flush queue to the provided function, and note that - // the queue is now empty. - if(number_of_write_areas_) { - function(write_areas_, number_of_write_areas_); - } - number_of_write_areas_ = 0; -} diff --git a/Outputs/OpenGL/TextureBuilder.hpp b/Outputs/OpenGL/TextureBuilder.hpp deleted file mode 100644 index 456a4e792..000000000 --- a/Outputs/OpenGL/TextureBuilder.hpp +++ /dev/null @@ -1,132 +0,0 @@ -// -// TextureBuilder.hpp -// Clock Signal -// -// Created by Thomas Harte on 08/03/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#ifndef Outputs_CRT_Internals_TextureBuilder_hpp -#define Outputs_CRT_Internals_TextureBuilder_hpp - -#include -#include -#include -#include - -#include "OpenGL.hpp" -#include "CRTConstants.hpp" - -namespace Outputs { -namespace CRT { - -/*! - Owns an OpenGL texture resource and provides mechanisms to fill it from bottom left to top right - with runs of data, ensuring each run is neighboured immediately to the left and right by copies of its - first and last pixels. - - Although this class is not itself inherently thread safe, it is built to permit one serialised stream - of calls to provide source data, with an interceding (but also serialised) submission to the GPU at any time. - - - Intended usage by the data generator: - - (i) allocate a write area with allocate_write_area, supplying a maximum size. - (ii) call reduce_previous_allocation_to to announce the actual size written. - - This will cause you to have added source data to the target texture. You can then either use that data - or allow it to expire. - - (iii) call retain_latest to add the most recently written write area to the flush queue. - - The flush queue contains provisional data, that can sit in the CPU's memory space indefinitely. This facility - is provided because it is expected that a texture will be built alontside some other collection of data; - that data in the flush queue is expected to become useful in coordination with something else but should - be retained at least until then. - - (iv) call flush to move data to the submit queue. - - When you flush, you'll receive a record of the bounds of all newly-flushed areas of source data. That gives - an opportunity to correlate the data with whatever else it is being tied to. It will continue to sit in - the CPU's memory space but has now passed beyond any further modification or reporting. - - - Intended usage by the GPU owner: - - (i) call submit to move data to the GPU and free up its CPU-side resources. - - The latest data is now on the GPU, regardless of where the data provider may be in its process; only data - that has entered the submission queue is uploaded. - -*/ -class TextureBuilder { - public: - /// Constructs an instance of InputTextureBuilder that contains a texture of colour depth @c bytes_per_pixel; - /// this creates a new texture and binds it to the current active texture unit. - TextureBuilder(std::size_t bytes_per_pixel, GLenum texture_unit); - virtual ~TextureBuilder(); - - /// 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. - /// Calls must be paired off with calls to @c reduce_previous_allocation_to. - /// @returns a pointer to the allocated space if any was available; @c nullptr otherwise. - uint8_t *allocate_write_area(std::size_t required_length, std::size_t required_alignment = 1); - - /// Announces that the owner is finished with the region created by the most recent @c allocate_write_area - /// and indicates that its actual final size was @c actual_length. - void reduce_previous_allocation_to(std::size_t actual_length); - - /// Allocated runs are provisional; they will not appear in the next flush queue unless retained. - /// @returns @c true if a retain succeeded; @c false otherwise. - bool retain_latest(); - - // Undoes the most recent retain_latest. Undefined behaviour if a submission has occurred in the interim. - void discard_latest(); - - /// @returns @c true if all future calls to @c allocate_write_area will fail on account of the input texture - /// being full; @c false if calls may succeed. - bool is_full(); - - /// Updates the currently-bound texture with all new data provided since the last @c submit. - void submit(); - - struct WriteArea { - uint16_t x = 0, y = 0, length = 0; - }; - /// Finalises all write areas allocated since the last call to @c flush. Only finalised areas will be - /// submitted upon the next @c submit. The supplied function will be called with a list of write areas - /// allocated, indicating their final resting locations and their lengths. - void flush(const std::function &write_areas, std::size_t count)> &); - - /// Binds this texture to the unit supplied at instantiation. - void bind(); - - private: - // the buffer size and target unit - std::size_t bytes_per_pixel_; - GLenum texture_unit_; - - // the buffer - std::vector image_; - GLuint texture_name_; - - // the current write area - WriteArea write_area_; - - // the list of write areas that have ascended to the flush queue - std::vector write_areas_; - std::size_t number_of_write_areas_ = 0; - bool is_full_ = false, was_full_ = false; - uint16_t first_unsubmitted_y_ = 0; - inline uint8_t *pointer_to_location(uint16_t x, uint16_t y); - - // Usually: the start position for the current batch of write areas. - // Caveat: reset to the origin upon a submit. So used in comparison by flush to - // determine whether the current batch of write areas needs to be relocated. - uint16_t write_areas_start_x_ = 0, write_areas_start_y_ = 0; -}; - -} -} - -#endif /* Outputs_CRT_Internals_TextureBuilder_hpp */ From 87df8b9e850b4abb9b54ac2c945db8182d2515fd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Nov 2018 21:19:14 -0500 Subject: [PATCH 040/208] Makes an attempt at pre-emptive line buffer clearing. --- Outputs/OpenGL/ScanTarget.cpp | 46 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index b7eebe49c..560a84328 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -329,28 +329,7 @@ template void ScanTarget::patch_buffer(const T &array, GLuint targe memcpy(&destination[0], &array[submit_pointer+1], end_length); memcpy(&destination[end_length], &array[0], buffer_length - end_length); -/* if(read_pointer < submit_pointer) { - // Submit the direct region from the submit pointer to the read pointer. - const size_t offset = read_pointer * sizeof(array[0]); - const size_t length = (submit_pointer - read_pointer) * sizeof(array[0]); - glBufferSubData(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(length), &array[read_pointer]); - } else { - // The circular buffer wrapped around; submit the data from the read pointer to the end of - // the buffer and from the start of the buffer to the submit pointer. - const size_t offset = read_pointer * sizeof(array[0]); - const size_t end_length = (array.size() - read_pointer) * sizeof(array[0]); - const size_t start_length = submit_pointer * sizeof(array[0]); - - memcpy(&destination[offset], &array[read_pointer], end_length); - memcpy(&destination[0], &array[0], start_length); - - glFlushMappedBufferRange(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(end_length)); - glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(start_length));*/ - -// } - - // Unmap the buffer. -// glBufferSubData(GL_ARRAY_BUFFER, GLintptr(offset), GLsizeiptr(length), &array[read_pointer]); + // Flush and unmap the buffer. glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size)); glUnmapBuffer(GL_ARRAY_BUFFER); } @@ -457,6 +436,27 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { // Push new input to the unprocessed line buffer. if(new_scans) { unprocessed_line_texture_.bind_framebuffer(); + + // Clear newly-touched lines; that is everything from (read+1) to submit. + const uint16_t first_line_to_clear = (read_pointers.line+1)%line_buffer_.size(); + const uint16_t final_line_to_clear = submit_pointers.line; + if(first_line_to_clear != final_line_to_clear) { + glEnable(GL_SCISSOR_TEST); + + if(first_line_to_clear < final_line_to_clear) { + glScissor(0, first_line_to_clear, unprocessed_line_texture_.get_width(), final_line_to_clear - first_line_to_clear); + glClear(GL_COLOR_BUFFER_BIT); + } else { + glScissor(0, 0, unprocessed_line_texture_.get_width(), final_line_to_clear); + glClear(GL_COLOR_BUFFER_BIT); + glScissor(0, first_line_to_clear, unprocessed_line_texture_.get_width(), 2048 - first_line_to_clear); + glClear(GL_COLOR_BUFFER_BIT); + } + + glDisable(GL_SCISSOR_TEST); + } + + // Apply new spans. glBindVertexArray(scan_vertex_array_); input_shader_->bind(); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(new_scans)); @@ -467,8 +467,6 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height); glClear(GL_COLOR_BUFFER_BIT); -// unprocessed_line_texture_.draw(1.0f); - // Output all lines except the one currently being worked on. glBindVertexArray(line_vertex_array_); output_shader_->bind(); From 8a699b607260e167231210bf1986287478f340cd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 14 Nov 2018 21:52:57 -0500 Subject: [PATCH 041/208] Kills `setup_output` definitively, saving some indirection. `set_scan_target` takes its place. --- .../Implementation/MultiCRTMachine.cpp | 2 +- .../Implementation/MultiCRTMachine.hpp | 2 +- Machines/AmstradCPC/AmstradCPC.cpp | 6 ++-- Machines/AppleII/AppleII.cpp | 2 +- Machines/Atari2600/Atari2600.cpp | 2 +- Machines/CRTMachine.hpp | 2 +- Machines/ColecoVision/ColecoVision.cpp | 2 +- Machines/Commodore/Vic-20/Vic20.cpp | 2 +- Machines/Electron/Electron.cpp | 17 ++++----- Machines/Electron/Video.cpp | 36 +++++++++---------- Machines/Electron/Video.hpp | 14 ++++---- Machines/MSX/MSX.cpp | 2 +- Machines/MasterSystem/MasterSystem.cpp | 2 +- Machines/Oric/Oric.cpp | 2 +- Machines/ZX8081/Video.cpp | 29 ++++++++------- Machines/ZX8081/Video.hpp | 11 +++--- Machines/ZX8081/ZX8081.cpp | 23 ++++++------ .../Clock Signal.xcodeproj/project.pbxproj | 8 ++++- .../Mac/Clock Signal/Machine/CSMachine.mm | 2 +- Outputs/CRT/CRT.cpp | 14 ++++---- Outputs/CRT/CRT.hpp | 11 +++--- Outputs/ScanTarget.cpp | 13 +++++++ Outputs/ScanTarget.hpp | 12 +++++++ 23 files changed, 122 insertions(+), 94 deletions(-) create mode 100644 Outputs/ScanTarget.cpp diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp index aed3e1721..73e086532 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp @@ -53,7 +53,7 @@ void MultiCRTMachine::perform_serial(const std::functionsetup_output(aspect_ratio); diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp index 17b13992c..6cd886669 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp @@ -53,7 +53,7 @@ class MultiCRTMachine: public CRTMachine::Machine { } // Below is the standard CRTMachine::Machine interface; see there for documentation. - void setup_output(Outputs::Display::ScanTarget *scan_target) override; + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override; Outputs::Speaker::Speaker *get_speaker() override; void run_for(Time::Seconds duration) override; diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index b93359bb0..d41e52de8 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -325,7 +325,7 @@ class CRTCBusHandler { } /// Constructs an appropriate CRT for video output. - void setup_output(Outputs::Display::ScanTarget *scan_target) { + void set_scan_target(Outputs::Display::ScanTarget *scan_target) { // crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::Display::Type::PAL50, 1)); // crt_->set_rgb_sampling_function( // "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" @@ -983,8 +983,8 @@ template class ConcreteMachine: } /// A CRTMachine function; indicates that outputs should be created now. - void setup_output(Outputs::Display::ScanTarget *scan_target) override final { - crtc_bus_handler_.setup_output(scan_target); + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final { + crtc_bus_handler_.set_scan_target(scan_target); } /// A CRTMachine function; indicates that outputs should be destroyed now. diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 56401a336..6ec7bf158 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -396,7 +396,7 @@ template class ConcreteMachine: audio_queue_.flush(); } - void setup_output(Outputs::Display::ScanTarget *scan_target) override { + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { video_.reset(new AppleII::Video::Video(video_bus_handler_)); video_->set_character_rom(character_rom_); } diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index f0f7baf46..874840dfd 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -157,7 +157,7 @@ class ConcreteMachine: } // to satisfy CRTMachine::Machine - void setup_output(Outputs::Display::ScanTarget *scan_target) override { + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { bus_->tia_.reset(new TIA); bus_->speaker_.set_input_rate(static_cast(get_clock_rate() / static_cast(CPUTicksPerAudioTick))); bus_->tia_->get_crt()->set_delegate(this); diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp index 42197f975..c1ad324c6 100644 --- a/Machines/CRTMachine.hpp +++ b/Machines/CRTMachine.hpp @@ -35,7 +35,7 @@ class Machine { The @c scan_target will receive all video output; the caller guarantees that it is non-null. */ - virtual void setup_output(Outputs::Display::ScanTarget *scan_target) = 0; + virtual void set_scan_target(Outputs::Display::ScanTarget *scan_target) = 0; /// @returns The speaker that receives this machine's output, or @c nullptr if this machine is mute. virtual Outputs::Speaker::Speaker *get_speaker() = 0; diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index b2840a76e..2b83e225d 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -169,7 +169,7 @@ class ConcreteMachine: return joysticks_; } - void setup_output(Outputs::Display::ScanTarget *scan_target) override { + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A)); // get_crt()->set_video_signal(Outputs::Display::VideoSignal::Composite); } diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index ec415437b..dd4a73732 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -620,7 +620,7 @@ class ConcreteMachine: m6502_.run_for(cycles); } - void setup_output(Outputs::Display::ScanTarget *scan_target) override final { + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final { mos6560_.reset(new MOS::MOS6560::MOS6560(mos6560_bus_handler_)); mos6560_->set_high_frequency_cutoff(1600); // There is a 1.6Khz low-pass filter in the Vic-20. mos6560_->set_output_mode(output_mode_); diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 010db7391..1301b5569 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -51,6 +51,7 @@ class ConcreteMachine: public: ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : m6502_(*this), + video_output_(ram_), sound_generator_(audio_queue_), speaker_(sound_generator_) { memset(key_states_, 0, sizeof(key_states_)); @@ -160,7 +161,7 @@ class ConcreteMachine: // for the entire frame, RAM is accessible only on odd cycles; in modes below 4 // it's also accessible only outside of the pixel regions - cycles += video_output_->get_cycles_until_next_ram_availability(cycles_since_display_update_.as_int() + 1); + cycles += video_output_.get_cycles_until_next_ram_availability(cycles_since_display_update_.as_int() + 1); } else { switch(address & 0xff0f) { case 0xfe00: @@ -198,8 +199,8 @@ class ConcreteMachine: case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f: if(!isReadOperation(operation)) { update_display(); - video_output_->set_register(address, *value); - video_access_range_ = video_output_->get_memory_access_range(); + video_output_.set_register(address, *value); + video_access_range_ = video_output_.get_memory_access_range(); queue_next_display_interrupt(); } break; @@ -373,8 +374,8 @@ class ConcreteMachine: audio_queue_.perform(); } - void setup_output(Outputs::Display::ScanTarget *scan_target) override final { - video_output_.reset(new VideoOutput(ram_, scan_target)); + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final { + video_output_.set_scan_target(scan_target); } Outputs::Speaker::Speaker *get_speaker() override final { @@ -500,12 +501,12 @@ class ConcreteMachine: // MARK: - Work deferral updates. inline void update_display() { if(cycles_since_display_update_ > 0) { - video_output_->run_for(cycles_since_display_update_.flush()); + video_output_.run_for(cycles_since_display_update_.flush()); } } inline void queue_next_display_interrupt() { - VideoOutput::Interrupt next_interrupt = video_output_->get_next_interrupt(); + VideoOutput::Interrupt next_interrupt = video_output_.get_next_interrupt(); cycles_until_display_interrupt_ = next_interrupt.cycles; next_display_interrupt_ = next_interrupt.interrupt; } @@ -575,7 +576,7 @@ class ConcreteMachine: int shift_restart_counter_ = 0; // Outputs - std::unique_ptr video_output_; + VideoOutput video_output_; Concurrency::DeferringAsyncTaskQueue audio_queue_; SoundGenerator sound_generator_; diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index 54fed963e..9b07e1b89 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -38,18 +38,16 @@ namespace { // MARK: - Lifecycle -VideoOutput::VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_target) : ram_(memory) { +VideoOutput::VideoOutput(uint8_t *memory) : + ram_(memory), + crt_(crt_cycles_per_line, + 1, + Outputs::Display::Type::PAL50, + Outputs::Display::InputDataType::Red1Green1Blue1) { memset(palette_, 0xf, sizeof(palette_)); setup_screen_map(); setup_base_address(); - crt_.reset(new Outputs::CRT::CRT( - crt_cycles_per_line, - 1, - Outputs::Display::Type::PAL50, - Outputs::Display::InputDataType::Red1Green1Blue1, - scan_target)); - // crt_->set_rgb_sampling_function( // "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" // "{" @@ -57,13 +55,11 @@ VideoOutput::VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_tar // "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)); + 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)); } -// MARK: - CRT getter - -Outputs::CRT::CRT *VideoOutput::get_crt() { - return crt_.get(); +void VideoOutput::set_scan_target(Outputs::Display::ScanTarget *scan_target) { + crt_.set_scan_target(scan_target); } // MARK: - Display update methods @@ -95,7 +91,7 @@ void VideoOutput::start_pixel_line() { void VideoOutput::end_pixel_line() { if(current_output_target_) { const int data_length = int(current_output_target_ - initial_output_target_); - crt_->output_data(data_length * current_output_divider_, size_t(data_length)); + crt_.output_data(data_length * current_output_divider_, size_t(data_length)); } current_character_row_++; } @@ -104,7 +100,7 @@ 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); + crt_.output_blank(number_of_cycles * crt_cycles_multiplier); } else { int divider = 1; switch(screen_mode_) { @@ -116,10 +112,10 @@ void VideoOutput::output_pixels(int number_of_cycles) { if(!initial_output_target_ || divider != current_output_divider_) { if(current_output_target_) { const int data_length = int(current_output_target_ - initial_output_target_); - crt_->output_data(data_length * current_output_divider_, size_t(data_length)); + crt_.output_data(data_length * current_output_divider_, size_t(data_length)); } current_output_divider_ = divider; - initial_output_target_ = current_output_target_ = crt_->begin_data(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() \ @@ -242,9 +238,9 @@ void VideoOutput::run_for(const Cycles cycles) { 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(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::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(); diff --git a/Machines/Electron/Video.hpp b/Machines/Electron/Video.hpp index 716c6d808..b839a7f55 100644 --- a/Machines/Electron/Video.hpp +++ b/Machines/Electron/Video.hpp @@ -27,19 +27,18 @@ namespace Electron { class VideoOutput { public: /*! - Instantiates a VideoOutput that will read its pixels from @c memory and output video - to @c scan_target. + Instantiates a VideoOutput that will read its pixels from @c memory. The pointer supplied should be to address 0 in the unexpanded Electron's memory map. */ - VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_target); - - /// @returns the CRT to which output is being painted. - Outputs::CRT::CRT *get_crt(); + VideoOutput(uint8_t *memory); /// Produces the next @c cycles of video output. void run_for(const Cycles cycles); + /// Sets the destination for output. + void set_scan_target(Outputs::Display::ScanTarget *scan_target); + /*! Writes @c value to the register at @c address. May mutate the results of @c get_next_interrupt, @c get_cycles_until_next_ram_availability and @c get_memory_access_range. @@ -114,8 +113,7 @@ class VideoOutput { uint8_t *current_output_target_ = nullptr; uint8_t *initial_output_target_ = nullptr; int current_output_divider_ = 1; - - std::unique_ptr crt_; + Outputs::CRT::CRT crt_; struct DrawAction { enum Type { diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index dcc06db5e..940d92019 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -218,7 +218,7 @@ class ConcreteMachine: audio_queue_.flush(); } - void setup_output(Outputs::Display::ScanTarget *scan_target) override { + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A)); } diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 8d2c587f1..1da0b8003 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -161,7 +161,7 @@ class ConcreteMachine: audio_queue_.flush(); } - void setup_output(Outputs::Display::ScanTarget *scan_target) override { + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { TI::TMS::Personality personality = TI::TMS::TMS9918A; switch(model_) { case Target::Model::SG1000: personality = TI::TMS::TMS9918A; break; diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index aca16adc9..0a8af14bc 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -456,7 +456,7 @@ template class Co } // to satisfy CRTMachine::Machine - void setup_output(Outputs::Display::ScanTarget *scan_target) override final { + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final { speaker_.set_input_rate(1000000.0f); video_output_.reset(new VideoOutput(ram_)); diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index 1afde2ebb..7e9416cd3 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -22,21 +22,20 @@ const std::size_t StandardAllocationSize = 320; } -Video::Video(Outputs::Display::ScanTarget *scan_target) : - crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Luminance1, scan_target)) - { +Video::Video() : + crt_(207 * 2, 1, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Luminance1) { // 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( +// 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::Display::VideoSignal::Composite); - crt_->set_visible_area(Outputs::Display::Rect(0.1f, 0.1f, 0.8f, 0.8f)); +// crt_.set_video_signal(Outputs::Display::VideoSignal::Composite); + crt_.set_visible_area(Outputs::Display::Rect(0.1f, 0.1f, 0.8f, 0.8f)); } void Video::run_for(const HalfCycles half_cycles) { @@ -51,7 +50,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(time_since_update_.as_int()); + crt_.output_sync(time_since_update_.as_int()); } else { // If not presently in sync, then... @@ -61,16 +60,16 @@ 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(output_length); + crt_.output_data(output_length); line_data_pointer_ = line_data_ = nullptr; time_since_update_ -= HalfCycles(output_length); } else return; } // Any pending pixels being dealt with, pad with the white level. - uint8_t *colour_pointer = static_cast(crt_->begin_data(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()); + crt_.output_level(time_since_update_.as_int()); } time_since_update_ = 0; @@ -92,16 +91,16 @@ 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_->begin_data(StandardAllocationSize); + line_data_pointer_ = line_data_ = crt_.begin_data(StandardAllocationSize); } // If a buffer was obtained, serialise the new pixels. if(line_data_) { // If the buffer is full, output it now and obtain a new one if(line_data_pointer_ - line_data_ == StandardAllocationSize) { - crt_->output_data(StandardAllocationSize, StandardAllocationSize); + crt_.output_data(StandardAllocationSize, StandardAllocationSize); time_since_update_ -= StandardAllocationSize; - line_data_pointer_ = line_data_ = crt_->begin_data(StandardAllocationSize); + line_data_pointer_ = line_data_ = crt_.begin_data(StandardAllocationSize); if(!line_data_) return; } @@ -115,6 +114,6 @@ void Video::output_byte(uint8_t byte) { } } -Outputs::CRT::CRT *Video::get_crt() { - return crt_.get(); +void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) { + crt_.set_scan_target(scan_target); } diff --git a/Machines/ZX8081/Video.hpp b/Machines/ZX8081/Video.hpp index bc5813582..b58c6c149 100644 --- a/Machines/ZX8081/Video.hpp +++ b/Machines/ZX8081/Video.hpp @@ -26,10 +26,8 @@ namespace ZX8081 { */ class Video { public: - /// Constructs an instance of the video feed; a CRT is also created. - Video(Outputs::Display::ScanTarget *scan_target); - /// @returns The CRT this video feed is feeding. - Outputs::CRT::CRT *get_crt(); + /// Constructs an instance of the video feed. + Video(); /// Advances time by @c half-cycles. void run_for(const HalfCycles); @@ -41,12 +39,15 @@ class Video { /// Causes @c byte to be serialised into pixels and output over the next four cycles. void output_byte(uint8_t byte); + /// Sets the scan target. + void set_scan_target(Outputs::Display::ScanTarget *scan_target); + private: bool sync_ = false; uint8_t *line_data_ = nullptr; uint8_t *line_data_pointer_ = nullptr; HalfCycles time_since_update_ = 0; - std::unique_ptr crt_; + Outputs::CRT::CRT crt_; void flush(bool next_sync); }; diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index cfaa28bc0..ab9818006 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -135,23 +135,23 @@ template class ConcreteMachine: time_since_ay_update_ += cycle.length; if(previous_counter < vsync_start_ && horizontal_counter_ >= vsync_start_) { - video_->run_for(vsync_start_ - previous_counter); + video_.run_for(vsync_start_ - previous_counter); set_hsync(true); line_counter_ = (line_counter_ + 1) & 7; if(nmi_is_enabled_) { z80_.set_non_maskable_interrupt_line(true); } - video_->run_for(horizontal_counter_ - vsync_start_); + video_.run_for(horizontal_counter_ - vsync_start_); } else if(previous_counter < vsync_end_ && horizontal_counter_ >= vsync_end_) { - video_->run_for(vsync_end_ - previous_counter); + video_.run_for(vsync_end_ - previous_counter); set_hsync(false); if(nmi_is_enabled_) { z80_.set_non_maskable_interrupt_line(false); z80_.set_wait_line(false); } - video_->run_for(horizontal_counter_ - vsync_end_); + video_.run_for(horizontal_counter_ - vsync_end_); } else { - video_->run_for(cycle.length); + video_.run_for(cycle.length); } if(is_zx81) horizontal_counter_ %= HalfCycles(Cycles(207)); @@ -240,7 +240,7 @@ template class ConcreteMachine: latched_video_byte_ = ram_[address & ram_mask_] ^ mask; } - video_->output_byte(latched_video_byte_); + video_.output_byte(latched_video_byte_); has_latched_video_byte_ = false; } break; @@ -303,15 +303,15 @@ template class ConcreteMachine: } forceinline void flush() { - video_->flush(); + video_.flush(); if(is_zx81) { update_audio(); audio_queue_.perform(); } } - void setup_output(Outputs::Display::ScanTarget *scan_target) override final { - video_.reset(new Video(scan_target)); + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final { + video_.set_scan_target(scan_target); } Outputs::Speaker::Speaker *get_speaker() override final { @@ -407,8 +407,7 @@ template class ConcreteMachine: private: CPU::Z80::Processor z80_; - - std::unique_ptr