From 4ac1f959e995b900e7d9a4cb67e54a6346cbbdcb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 19 Mar 2016 17:07:05 -0400 Subject: [PATCH] A shortcut here and a shortcut there; this allows me at least to determine whether use of a PBO gains anything. --- .../Clock Signal.xcodeproj/project.pbxproj | 2 + Outputs/CRT/CRT.hpp | 2 +- Outputs/CRT/Internals/CRTConstants.hpp | 57 ++++++++++++ .../CRT/Internals/CRTInputBufferBuilder.cpp | 6 +- .../CRT/Internals/CRTInputBufferBuilder.hpp | 7 ++ Outputs/CRT/Internals/CRTOpenGL.cpp | 88 +++++++------------ Outputs/CRT/Internals/CRTOpenGL.hpp | 42 ++------- 7 files changed, 108 insertions(+), 96 deletions(-) create mode 100644 Outputs/CRT/Internals/CRTConstants.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 141821bc2..ca2fdabaf 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -333,6 +333,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 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 = ""; }; 4B0EBFB61C487F2F00A11F35 /* AudioQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioQueue.h; sourceTree = ""; }; @@ -1217,6 +1218,7 @@ 4BBF99111C8FBA6F0075DAFB /* Shader.hpp */, 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */, 4BBF99131C8FBA6F0075DAFB /* TextureTarget.hpp */, + 4B0B6E121C9DBD5D00FFB60D /* CRTConstants.hpp */, ); path = Internals; sourceTree = ""; diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 596ccce10..92742d59c 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -107,7 +107,7 @@ 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, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int number_of_buffers, ...); + CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned 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 diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp new file mode 100644 index 000000000..f8480d437 --- /dev/null +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -0,0 +1,57 @@ +// +// CRTContants.hpp +// Clock Signal +// +// Created by Thomas Harte on 19/03/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef CRTConstants_h +#define CRTConstants_h + +#include "OpenGL.hpp" +#include + +namespace Outputs { +namespace CRT { + +// Output vertices are those used to copy from an input buffer — whether it describes data that maps directly to RGB +// or is one of the intermediate buffers that we've used to convert from composite towards RGB. +const size_t OutputVertexOffsetOfPosition = 0; +const size_t OutputVertexOffsetOfTexCoord = 4; +const size_t OutputVertexOffsetOfTimestamp = 8; +const size_t OutputVertexOffsetOfLateral = 12; + +const size_t OutputVertexSize = 16; + +// Input vertices, used only in composite mode, map from the input buffer to temporary buffer locations; such +// remapping occurs to ensure a continous stream of data for each scan, giving correct out-of-bounds behaviour +const size_t InputVertexOffsetOfInputPosition = 0; +const size_t InputVertexOffsetOfOutputPosition = 4; +const size_t InputVertexOffsetOfPhaseAndAmplitude = 8; +const size_t InputVertexOffsetOfPhaseTime = 12; + +const size_t InputVertexSize = 16; + +// These constants hold the size of the rolling buffer to which the CPU writes +const int InputBufferBuilderWidth = 2048; +const int InputBufferBuilderHeight = 1024; + +// This is the size of the intermediate buffers used during composite to RGB conversion +const int IntermediateBufferWidth = 2048; +const int IntermediateBufferHeight = 2048; + +// Some internal +const GLsizeiptr InputVertexBufferDataSize = 262080; // a multiple of 6 * OutputVertexSize +const GLsizeiptr InputTextureBufferDataSize = InputBufferBuilderWidth*InputBufferBuilderHeight; + + +// Runs are divided discretely by vertical syncs in order to put a usable bounds on the uniform used to track +// run age; that therefore creates a discrete number of fields that are stored. This number should be the +// number of historic fields that are required fully to +const int NumberOfFields = 3; + +} +} + +#endif /* CRTContants_h */ diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 75e54ebc4..fd3edc5d5 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -41,8 +41,7 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) { - _next_write_x_position = 0; - _next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight; + move_to_new_line(); } _write_x_position = _next_write_x_position + 1; @@ -53,6 +52,8 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) void CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length) { + // book end the allocation with duplicates of the first and last pixel, to protect + // against rounding errors when this run is drawn for(int c = 0; c < number_of_buffers; c++) { memcpy( &buffers[c].data[(_write_target_pointer - 1) * buffers[c].bytes_per_pixel], @@ -64,6 +65,7 @@ void CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length) buffers[c].bytes_per_pixel); } + // return any allocated length that wasn't actually used to the available pool _next_write_x_position -= (_last_allocation_amount - actual_length); } diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index 738c02a9b..6b8653963 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -12,6 +12,7 @@ #include #include #include +#include "CRTConstants.hpp" namespace Outputs { namespace CRT { @@ -40,6 +41,12 @@ struct CRTInputBufferBuilder { // Storage for the amount of buffer uploaded so far; initialised correctly by the buffer // builder but otherwise entrusted to the CRT to update. unsigned int last_uploaded_line; + + inline void move_to_new_line() + { + _next_write_x_position = 0; + _next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight; + } }; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 0ea951a22..fcd96e028 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -25,7 +25,8 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int number_of_buffers, va_list _composite_shader(nullptr), _rgb_shader(nullptr), _output_buffer_data(nullptr), - _output_buffer_sync(nullptr) + _output_buffer_sync(nullptr), + _input_texture_data(nullptr) { _run_builders = new CRTRunBuilder *[NumberOfFields]; for(int builder = 0; builder < NumberOfFields; builder++) @@ -47,7 +48,9 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder() delete _run_builders[builder]; } delete[] _run_builders; - delete[] _output_buffer_data; +// delete[] _input_texture_data; + + glUnmapBuffer(GL_ARRAY_BUFFER); free(_composite_shader); free(_rgb_shader); @@ -82,10 +85,12 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GLenum format = formatForDepth(_buffer_builder->buffers[buffer].bytes_per_pixel); - glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, InputBufferBuilderWidth, InputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, _buffer_builder->buffers[buffer].data); - } + glGenBuffers(1, &_input_texture_array); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); + glBufferData(GL_PIXEL_UNPACK_BUFFER, InputTextureBufferDataSize, NULL, GL_STREAM_DRAW); - printf("%s\n", glGetString(GL_VERSION)); + glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, InputBufferBuilderWidth, InputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, nullptr); + } prepare_composite_input_shader(); prepare_rgb_output_shader(); @@ -97,7 +102,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glBufferData(GL_ARRAY_BUFFER, InputVertexBufferDataSize, NULL, GL_STREAM_DRAW); - _output_buffer_data = new uint8_t[InputVertexBufferDataSize]; _output_buffer_data_pointer = 0; glBindVertexArray(output_vertex_array); @@ -109,22 +113,30 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&defaultFramebuffer); // Create intermediate textures and bind to slots 0, 1 and 2 - glActiveTexture(GL_TEXTURE0); - compositeTexture = std::unique_ptr(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight)); - glActiveTexture(GL_TEXTURE1); - filteredYTexture = std::unique_ptr(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight)); - glActiveTexture(GL_TEXTURE2); - filteredTexture = std::unique_ptr(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight)); +// glActiveTexture(GL_TEXTURE0); +// compositeTexture = std::unique_ptr(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight)); +// glActiveTexture(GL_TEXTURE1); +// filteredYTexture = std::unique_ptr(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight)); +// glActiveTexture(GL_TEXTURE2); +// filteredTexture = std::unique_ptr(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight)); } // lock down any further work on the current frame _output_mutex->lock(); + // release the mapping, giving up on trying to draw if data has been lost + if(glUnmapBuffer(GL_ARRAY_BUFFER) == GL_FALSE) + { + for(int c = 0; c < NumberOfFields; c++) + _run_builders[c]->reset(); + } + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + // upload more source pixel data if any; we'll always resubmit the last line submitted last // time as it may have had extra data appended to it for(unsigned int buffer = 0; buffer < _buffer_builder->number_of_buffers; buffer++) { - glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit + buffer); +// glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit + buffer); GLenum format = formatForDepth(_buffer_builder->buffers[0].bytes_per_pixel); if(_buffer_builder->_next_write_y_position < _buffer_builder->last_uploaded_line) { @@ -132,7 +144,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out 0, (GLint)_buffer_builder->last_uploaded_line, InputBufferBuilderWidth, (GLint)(InputBufferBuilderHeight - _buffer_builder->last_uploaded_line), format, GL_UNSIGNED_BYTE, - &_buffer_builder->buffers[0].data[_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel]); + (void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel)); _buffer_builder->last_uploaded_line = 0; } @@ -142,7 +154,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out 0, (GLint)_buffer_builder->last_uploaded_line, InputBufferBuilderWidth, (GLint)(1 + _buffer_builder->_next_write_y_position - _buffer_builder->last_uploaded_line), format, GL_UNSIGNED_BYTE, - &_buffer_builder->buffers[0].data[_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel]); + (void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel)); _buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position; } } @@ -177,7 +189,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out // draw all sitting frames unsigned int run = (unsigned int)_run_write_pointer; - // printf("%d: %zu v %zu\n", run, _run_builders[run]->uploaded_vertices, _run_builders[run]->number_of_vertices); GLint total_age = 0; for(int c = 0; c < NumberOfFields; c++) { @@ -186,47 +197,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out if(_run_builders[run]->amount_of_data > 0) { - // upload if required - if(_run_builders[run]->amount_of_data != _run_builders[run]->amount_of_uploaded_data) - { - size_t start = (_run_builders[run]->start + _run_builders[run]->amount_of_uploaded_data) % InputVertexBufferDataSize; - size_t length = _run_builders[run]->amount_of_data + _run_builders[run]->amount_of_uploaded_data; - - if(start + length > InputVertexBufferDataSize) - { - if(_output_buffer_sync) - { - glClientWaitSync(_output_buffer_sync, GL_SYNC_FLUSH_COMMANDS_BIT, ~(GLuint64)0); - glDeleteSync(_output_buffer_sync); - } - _output_buffer_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - uint8_t *target = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, InputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); - if(target) - { - size_t first_size = InputVertexBufferDataSize - start; - memcpy(&target[start], &_output_buffer_data[start], first_size); - memcpy(target, _output_buffer_data, length - first_size); - } - } - else - { - uint8_t *target = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, (GLintptr)start, (GLsizeiptr)length, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); - if(target) - { - memcpy(target, &_output_buffer_data[start], length); - } - } - - while(glUnmapBuffer(GL_ARRAY_BUFFER) == GL_FALSE) - { - // "the data store contents are undefined. An application must detect this rare condition and reinitialize the data store." - uint8_t *target = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, InputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); - memcpy(target, _output_buffer_data, InputVertexBufferDataSize); - } - - _run_builders[run]->amount_of_uploaded_data = _run_builders[run]->amount_of_data; - } - // draw glUniform1f(timestampBaseUniform, (GLfloat)total_age); GLsizei count = (GLsizei)(_run_builders[run]->amount_of_data / InputVertexSize); @@ -247,6 +217,10 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out } } + // drawing commands having been issued, reclaim the array buffer pointer + _buffer_builder->move_to_new_line(); + _output_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, InputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + _input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, InputTextureBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); _output_mutex->unlock(); } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index ab67efdcf..c9dcf45ef 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -10,6 +10,7 @@ #define CRTOpenGL_h #include "../CRTTypes.hpp" +#include "CRTConstants.hpp" #include "OpenGL.hpp" #include "TextureTarget.hpp" #include "Shader.hpp" @@ -21,41 +22,6 @@ namespace Outputs { namespace CRT { -// Output vertices are those used to copy from an input buffer — whether it describes data that maps directly to RGB -// or is one of the intermediate buffers that we've used to convert from composite towards RGB. -const size_t OutputVertexOffsetOfPosition = 0; -const size_t OutputVertexOffsetOfTexCoord = 4; -const size_t OutputVertexOffsetOfTimestamp = 8; -const size_t OutputVertexOffsetOfLateral = 12; - -const size_t OutputVertexSize = 16; - -// Input vertices, used only in composite mode, map from the input buffer to temporary buffer locations; such -// remapping occurs to ensure a continous stream of data for each scan, giving correct out-of-bounds behaviour -const size_t InputVertexOffsetOfInputPosition = 0; -const size_t InputVertexOffsetOfOutputPosition = 4; -const size_t InputVertexOffsetOfPhaseAndAmplitude = 8; -const size_t InputVertexOffsetOfPhaseTime = 12; - -const size_t InputVertexSize = 16; - -// These constants hold the size of the rolling buffer to which the CPU writes -const int InputBufferBuilderWidth = 2048; -const int InputBufferBuilderHeight = 1024; - -// This is the size of the intermediate buffers used during composite to RGB conversion -const int IntermediateBufferWidth = 2048; -const int IntermediateBufferHeight = 2048; - -// Some internal -const GLsizeiptr InputVertexBufferDataSize = 262080; // a multiple of 6 * OutputVertexSize - - -// Runs are divided discretely by vertical syncs in order to put a usable bounds on the uniform used to track -// run age; that therefore creates a discrete number of fields that are stored. This number should be the -// number of historic fields that are required fully to -const int NumberOfFields = 3; - class OpenGLOutputBuilder { private: // colour information @@ -212,7 +178,8 @@ class OpenGLOutputBuilder { inline uint8_t *get_write_target_for_buffer(int buffer) { - return _buffer_builder->get_write_target_for_buffer(buffer); + return &_input_texture_data[_buffer_builder->_write_target_pointer]; // * _buffer_builder->bytes_per_pixel +// return _buffer_builder->get_write_target_for_buffer(buffer); } inline uint16_t get_last_write_x_posiiton() @@ -241,6 +208,9 @@ class OpenGLOutputBuilder { // TODO: update related uniforms } + uint8_t *_input_texture_data; + GLuint _input_texture_array; + uint8_t *_output_buffer_data; size_t _output_buffer_data_pointer; GLsync _output_buffer_sync;