From 87df57195d20bccf84ec9e2c2e8e0bb83f5b529a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 4 May 2016 07:39:45 -0400 Subject: [PATCH] Started on an attempt not to treat the various buffers as free to loop within, starting with the input texture. --- Machines/Atari2600/Atari2600.cpp | 7 +- Machines/Electron/Electron.cpp | 155 ++++++++++-------- Outputs/CRT/CRT.cpp | 24 ++- Outputs/CRT/CRT.hpp | 6 +- .../CRT/Internals/CRTInputBufferBuilder.cpp | 53 +++--- .../CRT/Internals/CRTInputBufferBuilder.hpp | 20 ++- Outputs/CRT/Internals/CRTOpenGL.hpp | 4 +- 7 files changed, 151 insertions(+), 118 deletions(-) diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 4d12af989..0022cdc03 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -156,8 +156,8 @@ void Machine::get_output_pixel(uint8_t *pixel, int offset) if(playerPixels[0] || missilePixels[0]) outputColour = _playerColour[0]; } - // store colour - pixel[0] = outputColour; + // store colour, if possible + if(pixel) *pixel = outputColour; } // in imputing the knowledge that all we're dealing with is the rollover from 159 to 0, @@ -237,8 +237,7 @@ void Machine::output_pixels(unsigned int count) } if(_horizontalTimer < (_vBlankExtend ? 152 : 160)) { - if(_outputBuffer) - get_output_pixel(&_outputBuffer[_lastOutputStateDuration], 159 - _horizontalTimer); + get_output_pixel(_outputBuffer ? &_outputBuffer[_lastOutputStateDuration] : nullptr, 159 - _horizontalTimer); // increment all graphics counters increment_object_counter(0); diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index e2453dfd2..5ddb69435 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -539,7 +539,7 @@ inline void Machine::start_pixel_line() } _currentScreenAddress = _startLineAddress; _current_pixel_column = 0; - _current_output_target = nullptr; + _initial_output_target = _current_output_target = nullptr; } inline void Machine::end_pixel_line() @@ -566,7 +566,7 @@ inline void Machine::output_pixels(unsigned int number_of_cycles) case 2: case 5: divider = 8; break; } - if(!_current_output_target || divider != _current_output_divider) + if(!_initial_output_target || divider != _current_output_divider) { if(_current_output_target) _crt->output_data((unsigned int)((_current_output_target - _initial_output_target) * _current_output_divider), _current_output_divider); _current_output_divider = divider; @@ -584,97 +584,112 @@ inline void Machine::output_pixels(unsigned int number_of_cycles) switch(_screen_mode) { case 0: case 3: - while(number_of_cycles--) + if(_initial_output_target) { - get_pixel(); - *(uint32_t *)_current_output_target = _paletteTables.eighty1bpp[_last_pixel_byte]; - _current_output_target += 4; - _current_pixel_column++; - } + while(number_of_cycles--) + { + get_pixel(); + *(uint32_t *)_current_output_target = _paletteTables.eighty1bpp[_last_pixel_byte]; + _current_output_target += 4; + _current_pixel_column++; + } + } else _current_output_target += 4*number_of_cycles; break; case 1: - while(number_of_cycles--) + if(_initial_output_target) { - get_pixel(); - *(uint16_t *)_current_output_target = _paletteTables.eighty2bpp[_last_pixel_byte]; - _current_output_target += 2; - _current_pixel_column++; - } + while(number_of_cycles--) + { + get_pixel(); + *(uint16_t *)_current_output_target = _paletteTables.eighty2bpp[_last_pixel_byte]; + _current_output_target += 2; + _current_pixel_column++; + } + } else _current_output_target += 2*number_of_cycles; break; case 2: - while(number_of_cycles--) + if(_initial_output_target) { - get_pixel(); - *_current_output_target = _paletteTables.eighty4bpp[_last_pixel_byte]; - _current_output_target += 1; - _current_pixel_column++; - } + while(number_of_cycles--) + { + get_pixel(); + *_current_output_target = _paletteTables.eighty4bpp[_last_pixel_byte]; + _current_output_target += 1; + _current_pixel_column++; + } + } else _current_output_target += number_of_cycles; break; case 4: case 6: - if(_current_pixel_column&1) + if(_initial_output_target) { - _last_pixel_byte <<= 4; - *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; - _current_output_target += 2; + if(_current_pixel_column&1) + { + _last_pixel_byte <<= 4; + *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; + _current_output_target += 2; - number_of_cycles--; - _current_pixel_column++; - } - while(number_of_cycles > 1) - { - get_pixel(); - *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; - _current_output_target += 2; + number_of_cycles--; + _current_pixel_column++; + } + while(number_of_cycles > 1) + { + get_pixel(); + *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; + _current_output_target += 2; - _last_pixel_byte <<= 4; - *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; - _current_output_target += 2; + _last_pixel_byte <<= 4; + *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; + _current_output_target += 2; - number_of_cycles -= 2; - _current_pixel_column+=2; - } - if(number_of_cycles) - { - get_pixel(); - *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; - _current_output_target += 2; - _current_pixel_column++; - } + number_of_cycles -= 2; + _current_pixel_column+=2; + } + if(number_of_cycles) + { + get_pixel(); + *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; + _current_output_target += 2; + _current_pixel_column++; + } + } else _current_output_target += 2 * number_of_cycles; break; case 5: - if(_current_pixel_column&1) + if(_initial_output_target) { - _last_pixel_byte <<= 2; - *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; - _current_output_target += 1; + if(_current_pixel_column&1) + { + _last_pixel_byte <<= 2; + *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; + _current_output_target += 1; - number_of_cycles--; - _current_pixel_column++; - } - while(number_of_cycles > 1) - { - get_pixel(); - *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; - _current_output_target += 1; + number_of_cycles--; + _current_pixel_column++; + } + while(number_of_cycles > 1) + { + get_pixel(); + *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; + _current_output_target += 1; - _last_pixel_byte <<= 2; - *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; - _current_output_target += 1; + _last_pixel_byte <<= 2; + *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; + _current_output_target += 1; - number_of_cycles -= 2; - _current_pixel_column+=2; - } - if(number_of_cycles) - { - get_pixel(); - *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; - _current_output_target += 1; - _current_pixel_column++; - } + number_of_cycles -= 2; + _current_pixel_column+=2; + } + if(number_of_cycles) + { + get_pixel(); + *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; + _current_output_target += 1; + _current_pixel_column++; + } + } else _current_output_target += number_of_cycles; break; } diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 5166e9717..9a1ef52a5 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -312,15 +312,21 @@ void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider) { - _openGL_output_builder->reduce_previous_allocation_to(number_of_cycles / source_divider); - Scan scan { - .type = Scan::Type::Data, - .number_of_cycles = number_of_cycles, - .tex_x = _openGL_output_builder->get_last_write_x_posititon(), - .tex_y = _openGL_output_builder->get_last_write_y_posititon(), - .source_divider = source_divider - }; - output_scan(&scan); + if(_openGL_output_builder->reduce_previous_allocation_to(number_of_cycles / source_divider)) + { + Scan scan { + .type = Scan::Type::Data, + .number_of_cycles = number_of_cycles, + .tex_x = _openGL_output_builder->get_last_write_x_posititon(), + .tex_y = _openGL_output_builder->get_last_write_y_posititon(), + .source_divider = source_divider + }; + output_scan(&scan); + } + else + { + output_blank(number_of_cycles); + } } Outputs::CRT::Rect CRT::get_rect_for_area(int first_line_after_sync, int number_of_lines, int first_cycle_after_sync, int number_of_cycles, float aspect_ratio) diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index dd48512f2..0794956f5 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -176,14 +176,16 @@ class CRT { */ void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude); - /*! Ensures that the given number of output samples are allocated for writing. + /*! Attempts to allocate the given number of output samples for writing. 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. + Allocation should fail only if emulation is running significantly below real speed. + @param required_length The number of samples to allocate. - @returns A pointer to the allocated area. + @returns A pointer to the allocated area if room is available; @c nullptr otherwise. */ inline uint8_t *allocate_write_area(size_t required_length) { diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index a277860f3..1aa8f3d35 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -13,44 +13,51 @@ using namespace Outputs::CRT; CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : - bytes_per_pixel(bytes_per_pixel), + _bytes_per_pixel(bytes_per_pixel), _next_write_x_position(0), _next_write_y_position(0), - last_uploaded_line(0) + _last_uploaded_line(0), + _is_full(false) {} -CRTInputBufferBuilder::~CRTInputBufferBuilder() -{ -} - void CRTInputBufferBuilder::allocate_write_area(size_t required_length) { - _last_allocation_amount = required_length; - - if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) + if (!_is_full) { - _next_write_x_position = 0; - _next_write_y_position++; - } + _last_allocation_amount = required_length; - _write_x_position = _next_write_x_position + 1; - _write_y_position = _next_write_y_position; - _write_target_pointer = ((_write_y_position % InputBufferBuilderHeight) * InputBufferBuilderWidth) + _write_x_position; - _next_write_x_position += required_length + 2; + if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) + { + _next_write_x_position = 0; + _next_write_y_position++; + + _is_full = (_next_write_y_position == _last_uploaded_line + InputBufferBuilderHeight); + if(_is_full) return; + } + + _write_x_position = _next_write_x_position + 1; + _write_y_position = _next_write_y_position; + _write_target_pointer = ((_write_y_position % InputBufferBuilderHeight) * InputBufferBuilderWidth) + _write_x_position; + _next_write_x_position += required_length + 2; + } } -void CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer) +bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer) { + if(_is_full) return false; + // book end the allocation with duplicates of the first and last pixel, to protect // against rounding errors when this run is drawn - memcpy( &buffer[(_write_target_pointer - 1) * bytes_per_pixel], - &buffer[_write_target_pointer * bytes_per_pixel], - bytes_per_pixel); + memcpy( &buffer[(_write_target_pointer - 1) * _bytes_per_pixel], + &buffer[_write_target_pointer * _bytes_per_pixel], + _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); + 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); + + return true; } diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index a65182887..98bfd3190 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -20,23 +20,26 @@ namespace CRT { struct CRTInputBufferBuilder { CRTInputBufferBuilder(size_t bytes_per_pixel); - ~CRTInputBufferBuilder(); void allocate_write_area(size_t required_length); - void reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer); + bool reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer); inline uint16_t get_and_finalise_current_line() { uint16_t result = _write_y_position; - _next_write_x_position = 0; - _next_write_y_position++; + if(!_is_full) + { + _next_write_x_position = 0; + _next_write_y_position++; + } _next_write_y_position %= InputBufferBuilderHeight; + _is_full = false; return result; } inline uint8_t *get_write_target(uint8_t *buffer) { - return &buffer[_write_target_pointer * bytes_per_pixel]; + return _is_full ? nullptr : &buffer[_write_target_pointer * _bytes_per_pixel]; } inline uint16_t get_last_write_x_position() @@ -51,7 +54,7 @@ struct CRTInputBufferBuilder { inline size_t get_bytes_per_pixel() { - return bytes_per_pixel; + return _bytes_per_pixel; } private: @@ -66,11 +69,12 @@ struct CRTInputBufferBuilder { size_t _last_allocation_amount; // the buffer size - size_t bytes_per_pixel; + 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. - unsigned int last_uploaded_line; + unsigned int _last_uploaded_line; + bool _is_full; }; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index dd2343e46..e1f3daf08 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -152,9 +152,9 @@ class OpenGLOutputBuilder { return output; } - inline void reduce_previous_allocation_to(size_t actual_length) + inline bool reduce_previous_allocation_to(size_t actual_length) { - _buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data); + return _buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data); } inline uint16_t get_last_write_x_posititon()