From 00cb4d26b37e11c3fd465a9e3d2e6f40303d0dc3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 11 Jul 2018 19:52:55 -0400 Subject: [PATCH 1/7] Corrects typo. --- Outputs/CRT/Internals/TextureTarget.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Outputs/CRT/Internals/TextureTarget.hpp b/Outputs/CRT/Internals/TextureTarget.hpp index 9a69efa47..8d05dd1bc 100644 --- a/Outputs/CRT/Internals/TextureTarget.hpp +++ b/Outputs/CRT/Internals/TextureTarget.hpp @@ -60,7 +60,7 @@ class TextureTarget { /*! Draws this texture to the currently-bound framebuffer, which has the aspect ratio @c aspect_ratio. This texture will fill the height of the frame buffer, and pick - an appropriate width based o the aspect ratio. + an appropriate width based on the aspect ratio. @c colour_threshold sets a threshold test that each colour must satisfy to be output. A threshold of 0.0f means that all colours will pass through. A threshold From c9451a5382836cbcc3f152de0a95970d226fb560 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 14 Jul 2018 17:42:23 -0400 Subject: [PATCH 2/7] Introduces an object for drawing OpenGL rectangles. --- .../Clock Signal.xcodeproj/project.pbxproj | 8 ++ Outputs/CRT/Internals/Rectangle.cpp | 74 +++++++++++++++++++ Outputs/CRT/Internals/Rectangle.hpp | 41 ++++++++++ 3 files changed, 123 insertions(+) create mode 100644 Outputs/CRT/Internals/Rectangle.cpp create mode 100644 Outputs/CRT/Internals/Rectangle.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 44f629ee4..1d2903758 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -614,6 +614,8 @@ 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 */; }; @@ -1356,6 +1358,8 @@ 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 = ""; }; @@ -2896,6 +2900,7 @@ children = ( 4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */, 4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */, + 4BC891AB20F6EAB300EDE5B3 /* Rectangle.cpp */, 4BBF99081C8FBA6F0075DAFB /* TextureBuilder.cpp */, 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */, 4B5073061DDD3B9400C48FBD /* ArrayBuilder.hpp */, @@ -2903,6 +2908,7 @@ 4BBF990B1C8FBA6F0075DAFB /* CRTOpenGL.hpp */, 4BBF990E1C8FBA6F0075DAFB /* Flywheel.hpp */, 4BBF990F1C8FBA6F0075DAFB /* OpenGL.hpp */, + 4BC891AC20F6EAB300EDE5B3 /* Rectangle.hpp */, 4BBF99091C8FBA6F0075DAFB /* TextureBuilder.hpp */, 4BBF99131C8FBA6F0075DAFB /* TextureTarget.hpp */, 4BC3B74C1CD194CC00F86E85 /* Shaders */, @@ -3614,6 +3620,7 @@ 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 */, 4B055AE51FAE9B6F0060FFFF /* IntermediateShader.cpp in Sources */, 4B15A9FD208249BB005E6C8D /* StaticAnalyser.cpp in Sources */, @@ -3840,6 +3847,7 @@ 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 */, diff --git a/Outputs/CRT/Internals/Rectangle.cpp b/Outputs/CRT/Internals/Rectangle.cpp new file mode 100644 index 000000000..6600ef5bd --- /dev/null +++ b/Outputs/CRT/Internals/Rectangle.cpp @@ -0,0 +1,74 @@ +// +// Rectangle.cpp +// Clock Signal +// +// Created by Thomas Harte on 11/07/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "Rectangle.hpp" + +using namespace OpenGL; + +Rectangle::Rectangle(float x, float y, float width, float height): + pixel_shader_( + "#version 150\n" + + "in vec2 position;" + + "void main(void)" + "{" + "gl_Position = vec4(position, 0.0, 1.0);" + "}", + + "#version 150\n" + + "uniform vec4 colour;" + "out vec4 fragColour;" + + "void main(void)" + "{" + "fragColour = colour;" + "}" + ){ + pixel_shader_.bind(); + + glGenVertexArrays(1, &drawing_vertex_array_); + glGenBuffers(1, &drawing_array_buffer_); + + glBindVertexArray(drawing_vertex_array_); + glBindBuffer(GL_ARRAY_BUFFER, drawing_array_buffer_); + + GLint position_attribute = pixel_shader_.get_attrib_location("position"); + glEnableVertexAttribArray(static_cast(position_attribute)); + + glVertexAttribPointer( + (GLuint)position_attribute, + 2, + GL_FLOAT, + GL_FALSE, + 2 * sizeof(GLfloat), + (void *)0); + + colour_uniform_ = pixel_shader_.get_uniform_location("colour"); + + float buffer[4*2]; + + // Store positions. + buffer[0] = x; buffer[1] = y; + buffer[2] = x; buffer[3] = y + height; + buffer[4] = x + width; buffer[5] = y; + buffer[6] = x + width; buffer[7] = y + height; + + // Upload buffer. + glBindBuffer(GL_ARRAY_BUFFER, drawing_array_buffer_); + glBufferData(GL_ARRAY_BUFFER, sizeof(buffer), buffer, GL_STATIC_DRAW); +} + +void Rectangle::draw(float red, float green, float blue) { + pixel_shader_.bind(); + glUniform4f(colour_uniform_, red, green, blue, 1.0); + + glBindVertexArray(drawing_vertex_array_); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} diff --git a/Outputs/CRT/Internals/Rectangle.hpp b/Outputs/CRT/Internals/Rectangle.hpp new file mode 100644 index 000000000..b98e9a4df --- /dev/null +++ b/Outputs/CRT/Internals/Rectangle.hpp @@ -0,0 +1,41 @@ +// +// Rectangle.hpp +// Clock Signal +// +// Created by Thomas Harte on 11/07/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef Rectangle_hpp +#define Rectangle_hpp + +#include "OpenGL.hpp" +#include "Shaders/Shader.hpp" +#include + +namespace OpenGL { + +/*! + Provides a wrapper for drawing a solid, single-colour rectangle. +*/ +class Rectangle { + public: + /*! + Instantiates an instance of Rectange with the coordinates given. + */ + Rectangle(float x, float y, float width, float height); + + /*! + Draws this rectangle in the colour supplied. + */ + void draw(float red, float green, float blue); + + private: + Shader pixel_shader_; + GLuint drawing_vertex_array_ = 0, drawing_array_buffer_ = 0; + GLint colour_uniform_; +}; + +} + +#endif /* Rectangle_hpp */ From 3e9ef6b8cb36d7b0000f54375139aca2d7148c21 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 15 Jul 2018 20:19:06 -0400 Subject: [PATCH 3/7] Adds indicator lights for the SDL port. To complete #426 --- Activity/Observer.hpp | 11 +-- .../Mac/Clock Signal/Machine/CSMachine.mm | 6 -- OSBindings/SDL/main.cpp | 99 ++++++++++++++++++- 3 files changed, 103 insertions(+), 13 deletions(-) diff --git a/Activity/Observer.hpp b/Activity/Observer.hpp index 44875e4f7..349511123 100644 --- a/Activity/Observer.hpp +++ b/Activity/Observer.hpp @@ -24,13 +24,13 @@ namespace Activity { class Observer { public: /// Announces to the receiver that there is an LED of name @c name. - virtual void register_led(const std::string &name) = 0; + virtual void register_led(const std::string &name) {} /// Announces to the receiver that there is a drive of name @c name. - virtual void register_drive(const std::string &name) = 0; + virtual void register_drive(const std::string &name) {} /// Informs the receiver of the new state of the LED with name @c name. - virtual void set_led_status(const std::string &name, bool lit) = 0; + virtual void set_led_status(const std::string &name, bool lit) {} enum class DriveEvent { StepNormal, @@ -39,11 +39,10 @@ class Observer { }; /// Informs the receiver that the named event just occurred for the drive with name @c name. - virtual void announce_drive_event(const std::string &name, DriveEvent event) = 0; + virtual void announce_drive_event(const std::string &name, DriveEvent event) {} /// Informs the receiver of the motor-on status of the drive with name @c name. - virtual void set_drive_motor_status(const std::string &name, bool is_on) = 0; - + virtual void set_drive_motor_status(const std::string &name, bool is_on) {} }; } diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 52271e74f..8c34ea90d 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -57,9 +57,6 @@ struct ActivityObserver: public Activity::Observer { [machine addLED:[NSString stringWithUTF8String:name.c_str()]]; } - void register_drive(const std::string &name) override { - } - void set_led_status(const std::string &name, bool lit) override { [machine.delegate machine:machine led:[NSString stringWithUTF8String:name.c_str()] didChangeToLit:lit]; } @@ -68,9 +65,6 @@ struct ActivityObserver: public Activity::Observer { [machine.delegate machine:machine ledShouldBlink:[NSString stringWithUTF8String:name.c_str()]]; } - void set_drive_motor_status(const std::string &name, bool is_on) override { - } - __unsafe_unretained CSMachine *machine; }; diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 01c0045cf..3bfc80787 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -21,6 +21,9 @@ #include "../../Concurrency/BestEffortUpdater.hpp" +#include "../../Activity/Observer.hpp" +#include "../../Outputs/CRT/Internals/Rectangle.hpp" + namespace { struct BestEffortUpdaterDelegate: public Concurrency::BestEffortUpdater::Delegate { @@ -31,8 +34,8 @@ struct BestEffortUpdaterDelegate: public Concurrency::BestEffortUpdater::Delegat Machine::DynamicMachine *machine; }; -// This is set to a relatively large number for now. struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate { + // This is set to a relatively large number for now. static const int buffer_size = 1024; void speaker_did_complete_samples(Outputs::Speaker::Speaker *speaker, const std::vector &buffer) override { @@ -69,6 +72,88 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate { std::vector audio_buffer_; }; +class ActivityObserver: public Activity::Observer { + public: + ActivityObserver(Activity::Source *source, float aspect_ratio) { + // Get the suorce to supply all LEDs and drives. + source->set_activity_observer(this); + + // The objective is to display drives on one side of the screen, other LEDs on the other. Drives + // may or may not have LEDs and this code intends to display only those which do; so a quick + // comparative processing of the two lists is called for. + + // Strip the list of drives to only those which have LEDs. Thwy're the ones that'll be displayed. + drives_.resize(std::remove_if(drives_.begin(), drives_.end(), [this](const std::string &string) { + return std::find(leds_.begin(), leds_.end(), string) == leds_.end(); + }) - drives_.begin()); + + // Remove from the list of LEDs any which are drives. Those will be represented separately. + leds_.resize(std::remove_if(leds_.begin(), leds_.end(), [this](const std::string &string) { + return std::find(drives_.begin(), drives_.end(), string) != drives_.end(); + }) - leds_.begin()); + + set_aspect_ratio(aspect_ratio); + } + + void set_aspect_ratio(float aspect_ratio) { + lights_.clear(); + + // Generate a bunch of LEDs for connected drives. + const float height = 0.05f; + const float width = height / aspect_ratio; + const float right_x = 1.0f - 2.0f * width; + float y = 1.0f - 2.0f * height; + for(const auto &drive: drives_) { + lights_.emplace(std::make_pair(drive, std::make_unique(right_x, y, width, height))); + y -= height * 2.0f; + } + + /* + This would generate LEDs for things other than drives; I'm declining for now + due to the inexpressiveness of just painting a rectangle. + + const float left_x = -1.0f + 2.0f * width; + y = 1.0f - 2.0f * height; + for(const auto &led: leds_) { + lights_.emplace(std::make_pair(led, std::make_unique(left_x, y, width, height))); + y -= height * 2.0f; + } + */ + } + + void draw() { + for(const auto &lit_led: lit_leds_) { + if(blinking_leds_.find(lit_led) == blinking_leds_.end() && lights_.find(lit_led) != lights_.end()) + lights_[lit_led]->draw(0.0, 0.8, 0.0); + } + blinking_leds_.clear(); + } + + private: + std::vector leds_; + void register_led(const std::string &name) override { + leds_.push_back(name); + } + + std::vector drives_; + void register_drive(const std::string &name) override { + drives_.push_back(name); + } + + void set_led_status(const std::string &name, bool lit) override { + if(lit) lit_leds_.insert(name); + else lit_leds_.erase(name); + } + + void announce_drive_event(const std::string &name, DriveEvent event) override { + blinking_leds_.insert(name); + } + + std::map> lights_; + std::set lit_leds_; + std::set blinking_leds_; +}; + bool KeyboardKeyForSDLScancode(SDL_Keycode scancode, Inputs::Keyboard::Key &key) { #define BIND(x, y) case SDL_SCANCODE_##x: key = Inputs::Keyboard::Key::y; break; switch(scancode) { @@ -462,6 +547,16 @@ int main(int argc, char *argv[]) { } } + /* + If the machine offers anything for activity observation, + create and register an activity observer. + */ + std::unique_ptr activity_observer; + Activity::Source *const activity_source = machine->activity_source(); + if(activity_source) { + activity_observer.reset(new ActivityObserver(activity_source, 4.0f / 3.0f)); + } + // Run the main event loop until the OS tells us to quit. bool should_quit = false; Uint32 fullscreen_mode = 0; @@ -479,6 +574,7 @@ int main(int argc, char *argv[]) { glGetIntegerv(GL_FRAMEBUFFER_BINDING, &target_framebuffer); machine->crt_machine()->get_crt()->set_target_framebuffer(target_framebuffer); SDL_GetWindowSize(window, &window_width, &window_height); + if(activity_observer) activity_observer->set_aspect_ratio(static_cast(window_width) / static_cast(window_height)); } break; default: break; @@ -610,6 +706,7 @@ int main(int argc, char *argv[]) { // Display a new frame and wait for vsync. updater.update(); machine->crt_machine()->get_crt()->draw_frame(static_cast(window_width), static_cast(window_height), false); + if(activity_observer) activity_observer->draw(); SDL_GL_SwapWindow(window); } From 29f7f4d432d8c94e72e3ef23a0e8a9eddb9b72a9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 15 Jul 2018 22:47:50 -0400 Subject: [PATCH 4/7] Adds missing #include. --- OSBindings/SDL/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 3bfc80787..e0bba7fb0 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -6,6 +6,7 @@ // Copyright 2017 Thomas Harte. All rights reserved. // +#include #include #include #include From 24b861f056a189c218ce7aa97e589cb51e0d280b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 15 Jul 2018 22:52:36 -0400 Subject: [PATCH 5/7] Eliminates `make_unique` as this is presently a C++11 project. --- OSBindings/SDL/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index e0bba7fb0..dff709c4e 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -105,7 +105,8 @@ class ActivityObserver: public Activity::Observer { const float right_x = 1.0f - 2.0f * width; float y = 1.0f - 2.0f * height; for(const auto &drive: drives_) { - lights_.emplace(std::make_pair(drive, std::make_unique(right_x, y, width, height))); + // TODO: use std::make_unique as below, if/when formally embracing C++14. + lights_.emplace(std::make_pair(drive, std::unique_ptr(new OpenGL::Rectangle(right_x, y, width, height)))); y -= height * 2.0f; } From efa45b9504f963e6889cc4accefadb48ffd6f957 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 16 Jul 2018 21:52:31 -0400 Subject: [PATCH 6/7] Adds a right gutter to clip persistence errors. Also uncovers and corrects a long-standing centring error. --- Outputs/CRT/Internals/CRTOpenGL.cpp | 7 +++++++ Outputs/CRT/Internals/CRTOpenGL.hpp | 13 +++++++++++++ Outputs/CRT/Internals/Shaders/OutputShader.cpp | 10 ++++++++-- Outputs/CRT/Internals/Shaders/OutputShader.hpp | 8 ++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 19f24884e..6e630d04f 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -227,11 +227,18 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out output_shader_program_->set_output_size(output_width, output_height, visible_area_); last_output_width_ = output_width; last_output_height_ = output_height; + + // Configure a right gutter to crop the right-hand 2% of the display. + right_overlay_.reset(new OpenGL::Rectangle(output_shader_program_->get_right_extent() * 0.98, -1.0, 1.0, 2.0)); } output_shader_program_->bind(); // draw glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, (GLsizei)array_submission.output_size / OutputVertexSize); + + // mask off the gutter + glDisable(GL_BLEND); + right_overlay_->draw(0.0, 0.0, 0.0); } #ifdef GL_NV_texture_barrier diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 23462831d..adbc35e71 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -20,6 +20,7 @@ #include "Shaders/OutputShader.hpp" #include "Shaders/IntermediateShader.hpp" +#include "Rectangle.hpp" #include #include @@ -106,6 +107,18 @@ class OpenGLOutputBuilder { float integer_coordinate_multiplier_ = 1.0f; + // 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_; + public: // These two are protected by output_mutex_. TextureBuilder texture_builder; diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.cpp b/Outputs/CRT/Internals/Shaders/OutputShader.cpp index 0ab9c2ff3..8f3687fab 100644 --- a/Outputs/CRT/Internals/Shaders/OutputShader.cpp +++ b/Outputs/CRT/Internals/Shaders/OutputShader.cpp @@ -94,15 +94,21 @@ 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) { GLfloat outputAspectRatioMultiplier = (static_cast(output_width) / static_cast(output_height)) / (4.0f / 3.0f); - GLfloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * visible_area.size.width; - visible_area.origin.x -= bonusWidth * 0.5f * visible_area.size.width; + + right_extent_ = (1.0f / outputAspectRatioMultiplier) / visible_area.size.width; + + visible_area.origin.x -= bonusWidth * 0.5f; visible_area.size.width *= outputAspectRatioMultiplier; set_uniform("boundsOrigin", (GLfloat)visible_area.origin.x, (GLfloat)visible_area.origin.y); set_uniform("boundsSize", (GLfloat)visible_area.size.width, (GLfloat)visible_area.size.height); } +float OutputShader::get_right_extent() { + return right_extent_; +} + void OutputShader::set_source_texture_unit(GLenum unit) { set_uniform("texID", (GLint)(unit - GL_TEXTURE0)); } diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.hpp b/Outputs/CRT/Internals/Shaders/OutputShader.hpp index cbb7f7aa5..2ca65b572 100644 --- a/Outputs/CRT/Internals/Shaders/OutputShader.hpp +++ b/Outputs/CRT/Internals/Shaders/OutputShader.hpp @@ -87,6 +87,14 @@ public: space, 0.5 means use half, etc. */ void set_input_width_scaler(float input_scaler); + + /*! + @returns The location, in eye coordinates, of the right edge of the output area. + */ + float get_right_extent(); + +private: + float right_extent_ = 0.0f; }; } From 2832792fedc428d080167278a22f4a6ff955a52b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 16 Jul 2018 21:55:19 -0400 Subject: [PATCH 7/7] Corrects improper use of doubles. --- Outputs/CRT/Internals/CRTOpenGL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 6e630d04f..6510dc471 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -229,7 +229,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out last_output_height_ = output_height; // Configure a right gutter to crop the right-hand 2% of the display. - right_overlay_.reset(new OpenGL::Rectangle(output_shader_program_->get_right_extent() * 0.98, -1.0, 1.0, 2.0)); + right_overlay_.reset(new OpenGL::Rectangle(output_shader_program_->get_right_extent() * 0.98f, -1.0f, 1.0f, 2.0f)); } output_shader_program_->bind();