From 75bc0e451d3cfffbe089daf196ca8f899cc3fa92 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Nov 2018 21:39:11 -0500 Subject: [PATCH] Reintroduces the accumulation texture. Disables automatic clearing of the texture target, as the profiler indicates the vector instantiation to be a huge time sink. --- .../xcschemes/Clock Signal.xcscheme | 1 - Outputs/OpenGL/Primitives/TextureTarget.cpp | 5 +- Outputs/OpenGL/Primitives/TextureTarget.hpp | 2 +- Outputs/OpenGL/ScanTarget.cpp | 49 +++++++++++++++++-- Outputs/OpenGL/ScanTarget.hpp | 21 +++++++- 5 files changed, 67 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..f46055641 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -78,7 +78,6 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" stopOnEveryThreadSanitizerIssue = "YES" - stopOnEveryUBSanitizerIssue = "YES" debugServiceExtension = "internal" allowLocationSimulation = "NO"> blank_buffer(static_cast(expanded_width_ * expanded_height_ * 4), 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast(expanded_width_), static_cast(expanded_height_), 0, GL_RGBA, GL_UNSIGNED_BYTE, blank_buffer.data()); + // Set dimensions and set the user-supplied magnification filter. + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast(expanded_width_), static_cast(expanded_height_), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); diff --git a/Outputs/OpenGL/Primitives/TextureTarget.hpp b/Outputs/OpenGL/Primitives/TextureTarget.hpp index 16773fc85..73801ee83 100644 --- a/Outputs/OpenGL/Primitives/TextureTarget.hpp +++ b/Outputs/OpenGL/Primitives/TextureTarget.hpp @@ -24,7 +24,7 @@ namespace OpenGL { class TextureTarget { public: /*! - Creates a new texture target. + Creates a new texture target. Contents are initially undefined. Throws ErrorFramebufferIncomplete if creation fails. Leaves both the generated texture and framebuffer bound. diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index f3bc461e5..152f9fe4f 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -73,7 +73,8 @@ template void ScanTarget::allocate_buffer(const T &array, GLuint &b } ScanTarget::ScanTarget() : - unprocessed_line_texture_(LineBufferWidth, LineBufferHeight, UnprocessedLineBufferTextureUnit, GL_LINEAR, false) { + unprocessed_line_texture_(LineBufferWidth, LineBufferHeight, UnprocessedLineBufferTextureUnit, GL_LINEAR, false), + full_display_rectangle_(-1.0f, -1.0f, 2.0f, 2.0f) { // Ensure proper initialisation of the two atomic pointer sets. read_pointers_.store(write_pointers_); @@ -296,6 +297,12 @@ void ScanTarget::announce(Event event, uint16_t x, uint16_t y) { // Commit the most recent line only if any scans fell on it. // Otherwise there's no point outputting it, it'll contribute nothing. if(provided_scans_) { + // Store metadata if concluding a previous line. + if(active_line_) { + line_metadata_buffer_[size_t(write_pointers_.line)].is_first_in_frame = is_first_in_frame_; + is_first_in_frame_ = false; + } + const auto read_pointers = read_pointers_.load(); // Attempt to allocate a new line; note allocation failure if necessary. @@ -316,6 +323,9 @@ void ScanTarget::announce(Event event, uint16_t x, uint16_t y) { active_line_->line = write_pointers_.line; } } break; + case ScanTarget::Event::EndVerticalRetrace: + is_first_in_frame_ = true; + break; } // TODO: any lines that include any portion of vertical sync should be hidden. @@ -477,10 +487,30 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { 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); + // Ensure the accumulation buffer is properly sized. + if(!accumulation_texture_ || (!synchronous && (accumulation_texture_->get_width() != output_width || accumulation_texture_->get_height() != output_height))) { + std::unique_ptr new_framebuffer( + new TextureTarget( + GLsizei(output_width), + GLsizei(output_height), + AccumulationTextureUnit, + GL_LINEAR, + true)); + if(accumulation_texture_) { + new_framebuffer->bind_framebuffer(); + glClear(GL_COLOR_BUFFER_BIT); + + glActiveTexture(AccumulationTextureUnit); + accumulation_texture_->bind_texture(); + accumulation_texture_->draw(float(output_width) / float(output_height)); + + new_framebuffer->bind_texture(); + } + accumulation_texture_ = std::move(new_framebuffer); + } + + // Bind the accumulation texture. + accumulation_texture_->bind_framebuffer(); // Output all lines except the one currently being worked on. glBindVertexArray(line_vertex_array_); @@ -488,6 +518,15 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { glEnable(GL_BLEND); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(line_buffer_.size() - 2)); + // Copy the accumulatiion texture to the target (TODO: don't assume framebuffer 0). + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height); + + glDisable(GL_BLEND); + glClear(GL_COLOR_BUFFER_BIT); + accumulation_texture_->bind_texture(); + accumulation_texture_->draw(float(output_width) / float(output_height), 4.0f / 255.0f); + // All data now having been spooled to the GPU, update the read pointers to // the submit pointer location. read_pointers_.store(submit_pointers); diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index eabf96f79..01b7c249f 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -12,6 +12,7 @@ #include "../ScanTarget.hpp" #include "OpenGL.hpp" #include "Primitives/TextureTarget.hpp" +#include "Primitives/Rectangle.hpp" #include #include @@ -77,17 +78,35 @@ class ScanTarget: public Outputs::Display::ScanTarget { // Maintains a buffer of the most recent 3072 scans. std::array scan_buffer_; - // Maintains a list of composite scan buffer coordinates. + // Maintains a list of composite scan buffer coordinates; the Line struct + // is transported to the GPU in its entirety; the LineMetadatas live in CPU + // space only. struct Line { struct EndPoint { uint16_t x, y; } end_points[2]; uint16_t line; }; + struct LineMetadata { + bool is_first_in_frame; + }; std::array line_buffer_; + std::array line_metadata_buffer_; + + // Contains the first composition of scans into lines; + // they're accumulated prior to output to allow for continuous + // application of any necessary conversions — e.g. composite processing. TextureTarget unprocessed_line_texture_; + + // Scans are accumulated to the accumulation texture; the full-display + // rectangle is used to ensure untouched pixels properly decay. + std::unique_ptr accumulation_texture_; + Rectangle full_display_rectangle_; + + // Ephemeral state that helps in line composition. Line *active_line_ = nullptr; int provided_scans_ = 0; + bool is_first_in_frame_ = true; // OpenGL storage handles for buffer data. GLuint scan_buffer_name_ = 0, scan_vertex_array_ = 0;