From bf3ab4e260fbcaadc2b089edea11f1fd3dded4de Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Nov 2018 21:15:33 -0500 Subject: [PATCH] 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; } }