diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 2e2e16755..8b92ad490 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -24,7 +24,7 @@ Machine::Machine() : _piaDataValue{0xff, 0xff}, _tiaInputValue{0xff, 0xff} { - _crt = new Outputs::CRT::CRT(228, 1, Outputs::CRT::DisplayType::NTSC60, 1, 2); + _crt = new Outputs::CRT::CRT(228, 1, Outputs::CRT::DisplayType::NTSC60, 2); _crt->set_composite_sampling_function( "float composite_sample(vec2 coordinate, float phase)\n" "{\n" @@ -226,8 +226,7 @@ void Machine::output_pixels(unsigned int count) if(state == OutputState::Pixel) { - _crt->allocate_write_area(160); - _outputBuffer = _crt->get_write_target_for_buffer(0); + _outputBuffer = _crt->allocate_write_area(160); } else { _outputBuffer = nullptr; } diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 24353dfd9..0e6ce8b88 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -45,7 +45,7 @@ Machine::Machine() : _audioOutputPositionError(0), _current_pixel_line(-1), _use_fast_tape_hack(false), - _crt(std::unique_ptr(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::CRT::DisplayType::PAL50, 1, 1))) + _crt(std::unique_ptr(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::CRT::DisplayType::PAL50, 1))) { _crt->set_rgb_sampling_function( "vec4 rgb_sample(vec2 coordinate)" @@ -502,8 +502,7 @@ inline void Machine::start_pixel_line() if(!_isBlankLine) { - _crt->allocate_write_area(640); - _currentLine = _crt->get_write_target_for_buffer(0); + _currentLine = _crt->allocate_write_area(640); } } diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 62d4c39d4..c1776df9d 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -68,22 +68,15 @@ CRT::CRT(unsigned int common_output_divisor) : _common_output_divisor(common_output_divisor), _is_writing_composite_run(false) {} -CRT::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(common_output_divisor) +CRT::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) : CRT(common_output_divisor) { - va_list buffer_sizes; - va_start(buffer_sizes, number_of_buffers); - _openGL_output_builder = std::unique_ptr(new OpenGLOutputBuilder(number_of_buffers, buffer_sizes)); - va_end(buffer_sizes); - + _openGL_output_builder = std::unique_ptr(new OpenGLOutputBuilder(buffer_depth)); set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator); } -CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int number_of_buffers, ...) : CRT(common_output_divisor) +CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int buffer_depth) : CRT(common_output_divisor) { - va_list buffer_sizes; - va_start(buffer_sizes, number_of_buffers); - _openGL_output_builder = std::unique_ptr(new OpenGLOutputBuilder(number_of_buffers, buffer_sizes)); - va_end(buffer_sizes); + _openGL_output_builder = std::unique_ptr(new OpenGLOutputBuilder(buffer_depth)); set_new_display_type(cycles_per_line, displayType); } diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 92742d59c..5303fbe6c 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -95,19 +95,15 @@ class CRT { @param colour_cycle_denominator Specifies the denominator for the per-line frequency of the colour subcarrier. The colour subcarrier is taken to have colour_cycle_numerator/colour_cycle_denominator cycles per line. - @param number_of_buffers The number of source data buffers to create for this machine. Machines - may provide per-clock-cycle data in any form that they consider convenient, supplying a sampling + @param buffer_depth The depth per pixel of source data buffers to create for this machine. Machines + may provide per-clock-cycle data in the depth that they consider convenient, supplying a sampling function to convert between their data format and either a composite or RGB signal, allowing that work to be offloaded onto the GPU and allowing the output signal to be sampled at a rate appropriate to the display size. - @param ... A list of sizes for source data buffers, provided as the number of bytes per sample. - For compatibility with OpenGL ES, samples should be 1–4 bytes in size. If a machine requires more - than 4 bytes/sample then it should use multiple buffers. - @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 buffer_depth, ...); + 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 @@ -116,7 +112,7 @@ class CRT { Exactly identical to calling the designated constructor with colour subcarrier information looked up by display type. */ - CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int number_of_buffers, ...); + CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int buffer_depth); /*! Resets the CRT with new timing information. The CRT then continues as though the new timing had been provided at construction. */ @@ -171,30 +167,18 @@ class CRT { /*! Ensures that the given number of output samples are allocated for writing. - Following this call, the caller should call @c get_write_target_for_buffer for each - buffer they requested to get the location of the allocated memory. - The beginning of the most recently allocated area is used as the start of data written by a call to @c output_data; it is acceptable to write and to output less data than the amount requested but that may be less efficient. @param required_length The number of samples to allocate. + @returns A pointer to the allocated area. */ - inline void allocate_write_area(size_t required_length) + inline uint8_t *allocate_write_area(size_t required_length) { return _openGL_output_builder->allocate_write_area(required_length); } - /*! Gets a pointer for writing to the area created by the most recent call to @c allocate_write_area - for the nominated buffer. - - @param buffer The buffer to get a write target for. - */ - inline uint8_t *get_write_target_for_buffer(int buffer) - { - return _openGL_output_builder->get_write_target_for_buffer(buffer); - } - /*! Causes appropriate OpenGL or OpenGL ES calls to be issued in order to draw the current CRT state. The caller is responsible for ensuring that a valid OpenGL context exists for the duration of this call. */ diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index fd3edc5d5..6fdaf5ba8 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -12,29 +12,12 @@ using namespace Outputs::CRT; -CRTInputBufferBuilder::CRTInputBufferBuilder(unsigned int number_of_buffers, va_list buffer_sizes) +CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : bytes_per_pixel(bytes_per_pixel) { - this->number_of_buffers = number_of_buffers; - buffers = new CRTInputBufferBuilder::Buffer[number_of_buffers]; - - for(int buffer = 0; buffer < number_of_buffers; buffer++) - { - buffers[buffer].bytes_per_pixel = va_arg(buffer_sizes, unsigned int); - buffers[buffer].data = new uint8_t[InputBufferBuilderWidth * InputBufferBuilderHeight * buffers[buffer].bytes_per_pixel]; - } - _next_write_x_position = _next_write_y_position = 0; last_uploaded_line = 0; } -CRTInputBufferBuilder::~CRTInputBufferBuilder() -{ - for(int buffer = 0; buffer < number_of_buffers; buffer++) - delete[] buffers[buffer].data; - delete buffers; -} - - void CRTInputBufferBuilder::allocate_write_area(size_t required_length) { _last_allocation_amount = required_length; @@ -50,26 +33,18 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) _next_write_x_position += required_length + 2; } -void CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length) +void CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer) { // 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], - &buffers[c].data[_write_target_pointer * buffers[c].bytes_per_pixel], - buffers[c].bytes_per_pixel); + memcpy( &buffer[(_write_target_pointer - 1) * bytes_per_pixel], + &buffer[_write_target_pointer * bytes_per_pixel], + bytes_per_pixel); - memcpy( &buffers[c].data[(_write_target_pointer + actual_length) * buffers[c].bytes_per_pixel], - &buffers[c].data[(_write_target_pointer + actual_length - 1) * buffers[c].bytes_per_pixel], - buffers[c].bytes_per_pixel); - } + memcpy( &buffer[(_write_target_pointer + actual_length) * bytes_per_pixel], + &buffer[(_write_target_pointer + actual_length - 1) * bytes_per_pixel], + 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); } - -uint8_t *CRTInputBufferBuilder::get_write_target_for_buffer(int buffer) -{ - return &buffers[buffer].data[_write_target_pointer * buffers[buffer].bytes_per_pixel]; -} diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index 6b8653963..78e665a6b 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -18,12 +18,10 @@ namespace Outputs { namespace CRT { struct CRTInputBufferBuilder { - CRTInputBufferBuilder(unsigned int number_of_buffers, va_list buffer_sizes); - ~CRTInputBufferBuilder(); + CRTInputBufferBuilder(size_t bytes_per_pixel); void allocate_write_area(size_t required_length); - void reduce_previous_allocation_to(size_t actual_length); - uint8_t *get_write_target_for_buffer(int buffer); + void reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer); // a pointer to the section of content buffer currently being // returned and to where the next section will begin @@ -31,12 +29,7 @@ struct CRTInputBufferBuilder { uint16_t _write_x_position, _write_y_position; size_t _write_target_pointer; size_t _last_allocation_amount; - - struct Buffer { - uint8_t *data; - size_t bytes_per_pixel; - } *buffers; - unsigned int number_of_buffers; + size_t bytes_per_pixel; // Storage for the amount of buffer uploaded so far; initialised correctly by the buffer // builder but otherwise entrusted to the CRT to update. @@ -47,6 +40,11 @@ struct CRTInputBufferBuilder { _next_write_x_position = 0; _next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight; } + + inline uint8_t *get_write_target(uint8_t *buffer) + { + return &buffer[_write_target_pointer * bytes_per_pixel]; + } }; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index fcd96e028..8c7104a3c 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -17,7 +17,7 @@ namespace { static const GLenum first_supplied_buffer_texture_unit = 3; } -OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int number_of_buffers, va_list sizes) : +OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : _run_write_pointer(0), _output_mutex(new std::mutex), _visible_area(Rect(0, 0, 1, 1)), @@ -35,10 +35,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int number_of_buffers, va_list } // _composite_src_runs = std::unique_ptr(new CRTRunBuilder(InputVertexSize)); - va_list va; - va_copy(va, sizes); - _buffer_builder = std::unique_ptr(new CRTInputBufferBuilder(number_of_buffers, sizes)); - va_end(va); + _buffer_builder = std::unique_ptr(new CRTInputBufferBuilder(buffer_depth)); } OpenGLOutputBuilder::~OpenGLOutputBuilder() @@ -48,9 +45,13 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder() delete _run_builders[builder]; } delete[] _run_builders; -// delete[] _input_texture_data; glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + glDeleteTextures(1, &textureName); + glDeleteBuffers(1, &_input_texture_array); + glDeleteBuffers(1, &output_array_buffer); + glDeleteVertexArrays(1, &output_vertex_array); free(_composite_shader); free(_rgb_shader); @@ -73,24 +74,21 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out // establish essentials if(!composite_input_shader_program && !rgb_shader_program) { - // generate and bind textures for every one of the requested buffers - for(unsigned int buffer = 0; buffer < _buffer_builder->number_of_buffers; buffer++) - { - glGenTextures(1, &textureName); - glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit + buffer); - glBindTexture(GL_TEXTURE_2D, textureName); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + // generate and bind texture for input data + glGenTextures(1, &textureName); + glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit); + glBindTexture(GL_TEXTURE_2D, textureName); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - GLenum format = formatForDepth(_buffer_builder->buffers[buffer].bytes_per_pixel); - glGenBuffers(1, &_input_texture_array); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); - glBufferData(GL_PIXEL_UNPACK_BUFFER, InputTextureBufferDataSize, NULL, GL_STREAM_DRAW); + GLenum format = formatForDepth(_buffer_builder->bytes_per_pixel); + glGenBuffers(1, &_input_texture_array); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); + glBufferData(GL_PIXEL_UNPACK_BUFFER, InputTextureBufferDataSize, NULL, GL_STREAM_DRAW); - glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, InputBufferBuilderWidth, InputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, nullptr); - } + glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, InputBufferBuilderWidth, InputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, nullptr); prepare_composite_input_shader(); prepare_rgb_output_shader(); @@ -134,17 +132,17 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out // 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++) - { +// for(unsigned int buffer = 0; buffer < _buffer_builder->number_of_buffers; buffer++) +// { // glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit + buffer); - GLenum format = formatForDepth(_buffer_builder->buffers[0].bytes_per_pixel); + GLenum format = formatForDepth(_buffer_builder->bytes_per_pixel); if(_buffer_builder->_next_write_y_position < _buffer_builder->last_uploaded_line) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, (GLint)_buffer_builder->last_uploaded_line, InputBufferBuilderWidth, (GLint)(InputBufferBuilderHeight - _buffer_builder->last_uploaded_line), format, GL_UNSIGNED_BYTE, - (void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel)); + (void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel)); _buffer_builder->last_uploaded_line = 0; } @@ -154,10 +152,10 @@ 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, - (void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel)); + (void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel)); _buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position; } - } +// } // check for anything to decode from composite // if(_composite_src_runs->number_of_vertices) diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index c9dcf45ef..0d48a9ed3 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -89,7 +89,7 @@ class OpenGLOutputBuilder { std::unique_ptr filteredTexture; // receives filtered YIQ or YUV public: - OpenGLOutputBuilder(unsigned int number_of_buffers, va_list sizes); + OpenGLOutputBuilder(unsigned int buffer_depth); ~OpenGLOutputBuilder(); inline void set_colour_format(ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator) @@ -164,22 +164,18 @@ class OpenGLOutputBuilder { _output_mutex->unlock(); } - inline void allocate_write_area(size_t required_length) + inline uint8_t *allocate_write_area(size_t required_length) { _output_mutex->lock(); _buffer_builder->allocate_write_area(required_length); + uint8_t *output = _input_texture_data ? _buffer_builder->get_write_target(_input_texture_data) : nullptr; _output_mutex->unlock(); + return output; } inline void reduce_previous_allocation_to(size_t actual_length) { - _buffer_builder->reduce_previous_allocation_to(actual_length); - } - - inline uint8_t *get_write_target_for_buffer(int buffer) - { - return &_input_texture_data[_buffer_builder->_write_target_pointer]; // * _buffer_builder->bytes_per_pixel -// return _buffer_builder->get_write_target_for_buffer(buffer); + _buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data); } inline uint16_t get_last_write_x_posiiton()