diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 3a30d9811..f33048864 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -336,7 +336,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::patch_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); @@ -361,7 +361,7 @@ template void ScanTarget::patch_buffer(const T &array, GLuint targe glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size)); glUnmapBuffer(GL_ARRAY_BUFFER); } -} +}*/ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { if(fence_ != nullptr) { @@ -375,12 +375,9 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { if(is_drawing_.test_and_set()) return; // Grab the current read and submit pointers. - const auto submit_pointers = submit_pointers_.load(); + auto submit_pointers = submit_pointers_.load(); const auto read_pointers = read_pointers_.load(); - // 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) { @@ -514,30 +511,84 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) { accumulation_texture_ = std::move(new_framebuffer); } - // Bind the accumulation texture. - accumulation_texture_->bind_framebuffer(); - glClear(GL_STENCIL_BUFFER_BIT); + // Figure out how many new spans are ostensible ready; use two less than that. + uint16_t new_spans = (submit_pointers.line + LineBufferHeight - read_pointers.line) % LineBufferHeight; + if(new_spans > 2) { + new_spans -= 2; - // Enable stenciling and ensure spans increment the stencil buffer. - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_EQUAL, 0, GLuint(-1)); - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + // Bind the accumulation framebuffer. + accumulation_texture_->bind_framebuffer(); - // Output all lines except the one currently being worked on. - glBindVertexArray(line_vertex_array_); - output_shader_->bind(); - glEnable(GL_BLEND); - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(line_buffer_.size() - 2)); + // Enable blending and stenciling, and ensure spans increment the stencil buffer. + glEnable(GL_BLEND); + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_EQUAL, 0, GLuint(-1)); + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); - // Clear untouched parts of the display. (TODO: at vertical sync, probably) - full_display_rectangle_.draw(0.0, 0.0, 0.0); - glDisable(GL_STENCIL_TEST); + // Prepare to output lines. + glBindVertexArray(line_vertex_array_); + output_shader_->bind(); + + // Prepare to upload data that will consitute lines. + glBindBuffer(GL_ARRAY_BUFFER, line_buffer_name_); + + // Divide spans by which frame they're in. + uint16_t start_line = read_pointers.line; + submit_pointers.line = (read_pointers.line + new_spans) % LineBufferHeight; + while(new_spans) { + uint16_t end_line = start_line+1; + + // Find the limit of spans to draw in this cycle. + size_t spans = 1; + while(end_line != submit_pointers.line && !line_metadata_buffer_[end_line].is_first_in_frame) { + end_line = (end_line + 1) % LineBufferHeight; + ++spans; + } + + // Upload and draw. + const auto buffer_size = spans * sizeof(Line); + if(!end_line || end_line > start_line) { + glBufferSubData(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), &line_buffer_[start_line]); + } else { + uint8_t *destination = static_cast( + glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT) + ); + assert(destination); + + const size_t buffer_length = line_buffer_.size() * sizeof(Line); + const size_t start_position = start_line * sizeof(Line); + memcpy(&destination[0], &line_buffer_[start_line], buffer_length - start_position); + memcpy(&destination[buffer_length - start_position], &line_buffer_[0], end_line * sizeof(Line)); + + glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size)); + glUnmapBuffer(GL_ARRAY_BUFFER); + } + + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(spans)); + + // If this is end-of-frame, clear any untouched pixels and flush the stencil buffer + if(line_metadata_buffer_[end_line].is_first_in_frame) { + full_display_rectangle_.draw(0.0, 0.0, 0.0); + glClear(GL_STENCIL_BUFFER_BIT); + + // Rebind the program for span output. + glBindVertexArray(line_vertex_array_); + output_shader_->bind(); + } + + start_line = end_line; + new_spans -= spans; + } + + // Clear untouched parts of the display. (TODO: at vertical sync, probably) + glDisable(GL_STENCIL_TEST); + glDisable(GL_BLEND); + } // 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); diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 01b7c249f..9cc5de606 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -24,6 +24,11 @@ namespace Outputs { namespace Display { namespace OpenGL { +/*! + Provides a ScanTarget that uses OpenGL to render its output; + this uses various internal buffers so that the only geometry + drawn to the target framebuffer is a quad. +*/ class ScanTarget: public Outputs::Display::ScanTarget { public: ScanTarget();