1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-07 12:54:34 +00:00

Merge pull request #5 from TomHarte/Glitches

Started tackling the sources of graphical glitching
This commit is contained in:
Thomas Harte 2016-05-08 21:16:06 -04:00
commit 95ee2fffdb
12 changed files with 380 additions and 346 deletions

View File

@ -157,7 +157,7 @@ void Machine::get_output_pixel(uint8_t *pixel, int offset)
} }
// store colour // store colour
pixel[0] = outputColour; *pixel = outputColour;
} }
// in imputing the knowledge that all we're dealing with is the rollover from 159 to 0, // in imputing the knowledge that all we're dealing with is the rollover from 159 to 0,
@ -237,8 +237,8 @@ void Machine::output_pixels(unsigned int count)
} }
if(_horizontalTimer < (_vBlankExtend ? 152 : 160)) { if(_horizontalTimer < (_vBlankExtend ? 152 : 160)) {
if(_outputBuffer) uint8_t throwaway_pixel;
get_output_pixel(&_outputBuffer[_lastOutputStateDuration], 159 - _horizontalTimer); get_output_pixel(_outputBuffer ? &_outputBuffer[_lastOutputStateDuration] : &throwaway_pixel, 159 - _horizontalTimer);
// increment all graphics counters // increment all graphics counters
increment_object_counter(0); increment_object_counter(0);

View File

@ -537,7 +537,7 @@ inline void Machine::start_pixel_line()
} }
_currentScreenAddress = _startLineAddress; _currentScreenAddress = _startLineAddress;
_current_pixel_column = 0; _current_pixel_column = 0;
_current_output_target = nullptr; _initial_output_target = _current_output_target = nullptr;
} }
inline void Machine::end_pixel_line() inline void Machine::end_pixel_line()
@ -564,7 +564,7 @@ inline void Machine::output_pixels(unsigned int number_of_cycles)
case 2: case 5: divider = 8; break; 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); 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; _current_output_divider = divider;
@ -582,97 +582,112 @@ inline void Machine::output_pixels(unsigned int number_of_cycles)
switch(_screen_mode) switch(_screen_mode)
{ {
case 0: case 3: case 0: case 3:
while(number_of_cycles--) if(_initial_output_target)
{ {
get_pixel(); while(number_of_cycles--)
*(uint32_t *)_current_output_target = _paletteTables.eighty1bpp[_last_pixel_byte]; {
_current_output_target += 4; get_pixel();
_current_pixel_column++; *(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; break;
case 1: case 1:
while(number_of_cycles--) if(_initial_output_target)
{ {
get_pixel(); while(number_of_cycles--)
*(uint16_t *)_current_output_target = _paletteTables.eighty2bpp[_last_pixel_byte]; {
_current_output_target += 2; get_pixel();
_current_pixel_column++; *(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; break;
case 2: case 2:
while(number_of_cycles--) if(_initial_output_target)
{ {
get_pixel(); while(number_of_cycles--)
*_current_output_target = _paletteTables.eighty4bpp[_last_pixel_byte]; {
_current_output_target += 1; get_pixel();
_current_pixel_column++; *_current_output_target = _paletteTables.eighty4bpp[_last_pixel_byte];
} _current_output_target += 1;
_current_pixel_column++;
}
} else _current_output_target += number_of_cycles;
break; break;
case 4: case 6: case 4: case 6:
if(_current_pixel_column&1) if(_initial_output_target)
{ {
_last_pixel_byte <<= 4; if(_current_pixel_column&1)
*(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--; number_of_cycles--;
_current_pixel_column++; _current_pixel_column++;
} }
while(number_of_cycles > 1) while(number_of_cycles > 1)
{ {
get_pixel(); get_pixel();
*(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte];
_current_output_target += 2; _current_output_target += 2;
_last_pixel_byte <<= 4; _last_pixel_byte <<= 4;
*(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte];
_current_output_target += 2; _current_output_target += 2;
number_of_cycles -= 2; number_of_cycles -= 2;
_current_pixel_column+=2; _current_pixel_column+=2;
} }
if(number_of_cycles) if(number_of_cycles)
{ {
get_pixel(); get_pixel();
*(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte];
_current_output_target += 2; _current_output_target += 2;
_current_pixel_column++; _current_pixel_column++;
} }
} else _current_output_target += 2 * number_of_cycles;
break; break;
case 5: case 5:
if(_current_pixel_column&1) if(_initial_output_target)
{ {
_last_pixel_byte <<= 2; if(_current_pixel_column&1)
*_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--; number_of_cycles--;
_current_pixel_column++; _current_pixel_column++;
} }
while(number_of_cycles > 1) while(number_of_cycles > 1)
{ {
get_pixel(); get_pixel();
*_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte];
_current_output_target += 1; _current_output_target += 1;
_last_pixel_byte <<= 2; _last_pixel_byte <<= 2;
*_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte];
_current_output_target += 1; _current_output_target += 1;
number_of_cycles -= 2; number_of_cycles -= 2;
_current_pixel_column+=2; _current_pixel_column+=2;
} }
if(number_of_cycles) if(number_of_cycles)
{ {
get_pixel(); get_pixel();
*_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte];
_current_output_target += 1; _current_output_target += 1;
_current_pixel_column++; _current_pixel_column++;
} }
} else _current_output_target += number_of_cycles;
break; break;
} }

View File

@ -69,7 +69,8 @@ CRT::CRT(unsigned int common_output_divisor) :
_common_output_divisor(common_output_divisor), _common_output_divisor(common_output_divisor),
_is_writing_composite_run(false), _is_writing_composite_run(false),
_delegate(nullptr), _delegate(nullptr),
_frames_since_last_delegate_call(0) {} _frames_since_last_delegate_call(0),
_did_start_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 buffer_depth) : 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)
{ {
@ -99,15 +100,13 @@ Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested,
#define output_position_y(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfPosition + 2]) #define output_position_y(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfPosition + 2])
#define output_tex_x(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfTexCoord + 0]) #define output_tex_x(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfTexCoord + 0])
#define output_tex_y(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfTexCoord + 2]) #define output_tex_y(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfTexCoord + 2])
#define output_lateral(v) next_run[OutputVertexSize*v + OutputVertexOffsetOfLateral]
#define source_input_position_x(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfInputPosition + 0]) #define source_input_position_x(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfInputPosition + 0])
#define source_input_position_y(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfInputPosition + 2]) #define source_input_position_y(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfInputPosition + 2])
#define source_output_position_x(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfOutputPosition + 0]) #define source_output_position_x(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfOutputPosition + 0])
#define source_output_position_y(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfOutputPosition + 2]) #define source_output_position_y(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfOutputPosition + 2])
#define source_phase(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAmplitudeAndOffset + 0] #define source_phase(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAndAmplitude + 0]
#define source_amplitude(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAmplitudeAndOffset + 1] #define source_amplitude(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAndAmplitude + 1]
#define source_offset(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAmplitudeAndOffset + 2]
#define source_phase_time(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseTime]) #define source_phase_time(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseTime])
void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Scan::Type type, uint16_t tex_x, uint16_t tex_y) void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Scan::Type type, uint16_t tex_x, uint16_t tex_y)
@ -131,7 +130,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
bool is_output_segment = ((is_output_run && next_run_length) && !_horizontal_flywheel->is_in_retrace() && !_vertical_flywheel->is_in_retrace()); bool is_output_segment = ((is_output_run && next_run_length) && !_horizontal_flywheel->is_in_retrace() && !_vertical_flywheel->is_in_retrace());
uint8_t *next_run = nullptr; uint8_t *next_run = nullptr;
if(is_output_segment) if(is_output_segment && !_openGL_output_builder->composite_output_buffer_is_full())
{ {
next_run = _openGL_output_builder->get_next_source_run(); next_run = _openGL_output_builder->get_next_source_run();
} }
@ -150,8 +149,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
source_phase(0) = source_phase(1) = _colour_burst_phase; source_phase(0) = source_phase(1) = _colour_burst_phase;
source_amplitude(0) = source_amplitude(1) = _colour_burst_amplitude; source_amplitude(0) = source_amplitude(1) = _colour_burst_amplitude;
source_phase_time(0) = source_phase_time(1) = _colour_burst_time; source_phase_time(0) = source_phase_time(1) = _colour_burst_time;
source_offset(0) = 0;
source_offset(1) = 255;
} }
// decrement the number of cycles left to run for and increment the // decrement the number of cycles left to run for and increment the
@ -189,17 +186,23 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
if(needs_endpoint) if(needs_endpoint)
{ {
uint8_t *next_run = _openGL_output_builder->get_next_output_run(); if(
_is_writing_composite_run == _did_start_run &&
_openGL_output_builder->composite_output_run_has_room_for_vertices(_did_start_run ? 3 : 6) &&
!_openGL_output_builder->composite_output_buffer_is_full())
{
uint8_t *next_run = _openGL_output_builder->get_next_output_run();
if(next_run)
{
output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position();
output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider);
output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position();
output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = _openGL_output_builder->get_composite_output_y();
output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); _openGL_output_builder->complete_output_run(3);
output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider); _did_start_run ^= true;
output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); }
output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = _openGL_output_builder->get_composite_output_y(); }
output_lateral(0) = 0;
output_lateral(1) = _is_writing_composite_run ? 1 : 0;
output_lateral(2) = 1;
_openGL_output_builder->complete_output_run(3);
_is_writing_composite_run ^= true; _is_writing_composite_run ^= true;
} }
@ -297,8 +300,8 @@ void CRT::output_level(unsigned int number_of_cycles)
Scan scan { Scan scan {
.type = Scan::Type::Level, .type = Scan::Type::Level,
.number_of_cycles = number_of_cycles, .number_of_cycles = number_of_cycles,
.tex_x = _openGL_output_builder->get_last_write_x_posiiton(), .tex_x = _openGL_output_builder->get_last_write_x_posititon(),
.tex_y = _openGL_output_builder->get_last_write_y_posiiton() .tex_y = _openGL_output_builder->get_last_write_y_posititon()
}; };
output_scan(&scan); output_scan(&scan);
} }
@ -316,15 +319,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) 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); if(_openGL_output_builder->reduce_previous_allocation_to(number_of_cycles / source_divider))
Scan scan { {
.type = Scan::Type::Data, Scan scan {
.number_of_cycles = number_of_cycles, .type = Scan::Type::Data,
.tex_x = _openGL_output_builder->get_last_write_x_posiiton(), .number_of_cycles = number_of_cycles,
.tex_y = _openGL_output_builder->get_last_write_y_posiiton(), .tex_x = _openGL_output_builder->get_last_write_x_posititon(),
.source_divider = source_divider .tex_y = _openGL_output_builder->get_last_write_y_posititon(),
}; .source_divider = source_divider
output_scan(&scan); };
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) 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)

View File

@ -68,7 +68,7 @@ class CRT {
uint8_t _colour_burst_phase, _colour_burst_amplitude; uint8_t _colour_burst_phase, _colour_burst_amplitude;
uint16_t _colour_burst_time; uint16_t _colour_burst_time;
bool _is_writing_composite_run; bool _is_writing_composite_run, _did_start_run;
// the outer entry point for dispatching output_sync, output_blank, output_level and output_data // the outer entry point for dispatching output_sync, output_blank, output_level and output_data
void advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Scan::Type type, uint16_t tex_x, uint16_t tex_y); void advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Scan::Type type, uint16_t tex_x, uint16_t tex_y);
@ -176,14 +176,16 @@ class CRT {
*/ */
void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude); 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 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 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. 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. @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) inline uint8_t *allocate_write_area(size_t required_length)
{ {
@ -240,18 +242,6 @@ class CRT {
_openGL_output_builder->set_rgb_sampling_function(shader); _openGL_output_builder->set_rgb_sampling_function(shader);
} }
/*! Optionally sets a function that will map from an input cycle count to a colour carrier phase.
If this function is not supplied then the colour phase is determined from
the input clock rate and the the colour cycle clock rate. Machines whose per-line clock rate
is not intended exactly to match the normal line time may prefer to supply a custom function.
@param A GLSL fragent including a function with the signature
`float phase_for_clock_cycle(int cycle)` that returns the colour phase at the beginning of
the supplied cycle.
*/
// void set_phase_function(const char *shader);
inline void set_output_device(OutputDevice output_device) inline void set_output_device(OutputDevice output_device)
{ {
_openGL_output_builder->set_output_device(output_device); _openGL_output_builder->set_output_device(output_device);

View File

@ -19,30 +19,29 @@ namespace CRT {
// or is one of the intermediate buffers that we've used to convert from composite towards RGB. // or is one of the intermediate buffers that we've used to convert from composite towards RGB.
const GLsizei OutputVertexOffsetOfPosition = 0; const GLsizei OutputVertexOffsetOfPosition = 0;
const GLsizei OutputVertexOffsetOfTexCoord = 4; const GLsizei OutputVertexOffsetOfTexCoord = 4;
const GLsizei OutputVertexOffsetOfLateral = 8;
const GLsizei OutputVertexSize = 12; const GLsizei OutputVertexSize = 8;
// Input vertices, used only in composite mode, map from the input buffer to temporary buffer locations; such // 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 // remapping occurs to ensure a continous stream of data for each scan, giving correct out-of-bounds behaviour
const GLsizei SourceVertexOffsetOfInputPosition = 0; const GLsizei SourceVertexOffsetOfInputPosition = 0;
const GLsizei SourceVertexOffsetOfOutputPosition = 4; const GLsizei SourceVertexOffsetOfOutputPosition = 4;
const GLsizei SourceVertexOffsetOfPhaseAmplitudeAndOffset = 8; const GLsizei SourceVertexOffsetOfPhaseAndAmplitude = 8;
const GLsizei SourceVertexOffsetOfPhaseTime = 12; const GLsizei SourceVertexOffsetOfPhaseTime = 12;
const GLsizei SourceVertexSize = 16; const GLsizei SourceVertexSize = 16;
// These constants hold the size of the rolling buffer to which the CPU writes // These constants hold the size of the rolling buffer to which the CPU writes
const GLsizei InputBufferBuilderWidth = 2048; const GLsizei InputBufferBuilderWidth = 2048;
const GLsizei InputBufferBuilderHeight = 1024; const GLsizei InputBufferBuilderHeight = 512;
// This is the size of the intermediate buffers used during composite to RGB conversion // This is the size of the intermediate buffers used during composite to RGB conversion
const GLsizei IntermediateBufferWidth = 2048; const GLsizei IntermediateBufferWidth = 2048;
const GLsizei IntermediateBufferHeight = 2048; const GLsizei IntermediateBufferHeight = 512;
// Some internal buffer sizes // Some internal buffer sizes
const GLsizeiptr OutputVertexBufferDataSize = 89856; // a multiple of 6 * OutputVertexSize const GLsizeiptr OutputVertexBufferDataSize = 6 * 8 * 312; // a multiple of 6 * OutputVertexSize
const GLsizeiptr SourceVertexBufferDataSize = 87360; // a multiple of 2 * SourceVertexSize const GLsizeiptr SourceVertexBufferDataSize = 2 * 16 * 312; // a multiple of 2 * SourceVertexSize
} }
} }

View File

@ -13,45 +13,93 @@
using namespace Outputs::CRT; using namespace Outputs::CRT;
CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : 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_x_position(0),
_next_write_y_position(0), _next_write_y_position(0),
last_uploaded_line(0), _image(new uint8_t[bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight])
_wraparound_sync(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
{} {}
CRTInputBufferBuilder::~CRTInputBufferBuilder()
{
glDeleteSync(_wraparound_sync);
}
void CRTInputBufferBuilder::allocate_write_area(size_t required_length) void CRTInputBufferBuilder::allocate_write_area(size_t required_length)
{ {
_last_allocation_amount = required_length; if(_next_write_y_position != InputBufferBuilderHeight)
if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth)
{ {
move_to_new_line(); _last_allocation_amount = required_length;
}
_write_x_position = _next_write_x_position + 1; if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth)
_write_y_position = _next_write_y_position; {
_write_target_pointer = (_write_y_position * InputBufferBuilderWidth) + _write_x_position; _next_write_x_position = 0;
_next_write_x_position += required_length + 2; _next_write_y_position++;
if(_next_write_y_position == InputBufferBuilderHeight)
return;
}
_write_x_position = _next_write_x_position + 1;
_write_y_position = _next_write_y_position;
_write_target_pointer = (_write_y_position * 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)
{ {
if(_next_write_y_position == InputBufferBuilderHeight) return false;
uint8_t *const image_pointer = _image.get();
// correct if the writing cursor was reset while a client was writing
if(_next_write_x_position == 0 && _next_write_y_position == 0 && _write_target_pointer != 1)
{
memmove(&image_pointer[1], &image_pointer[_write_target_pointer], actual_length);
_write_target_pointer = 1;
_last_allocation_amount = actual_length;
_next_write_x_position = (uint16_t)(actual_length + 2);
}
// book end the allocation with duplicates of the first and last pixel, to protect // book end the allocation with duplicates of the first and last pixel, to protect
// against rounding errors when this run is drawn // against rounding errors when this run is drawn
memcpy( &buffer[(_write_target_pointer - 1) * bytes_per_pixel], memcpy( &image_pointer[(_write_target_pointer - 1) * _bytes_per_pixel],
&buffer[_write_target_pointer * bytes_per_pixel], &image_pointer[_write_target_pointer * _bytes_per_pixel],
bytes_per_pixel); _bytes_per_pixel);
memcpy( &buffer[(_write_target_pointer + actual_length) * bytes_per_pixel], memcpy( &image_pointer[(_write_target_pointer + actual_length) * _bytes_per_pixel],
&buffer[(_write_target_pointer + actual_length - 1) * bytes_per_pixel], &image_pointer[(_write_target_pointer + actual_length - 1) * _bytes_per_pixel],
bytes_per_pixel); _bytes_per_pixel);
// return any allocated length that wasn't actually used to the available pool // return any allocated length that wasn't actually used to the available pool
_next_write_x_position -= (_last_allocation_amount - actual_length); _next_write_x_position -= (_last_allocation_amount - actual_length);
return true;
}
uint8_t *CRTInputBufferBuilder::get_image_pointer()
{
return _image.get();
}
uint16_t CRTInputBufferBuilder::get_and_finalise_current_line()
{
uint16_t result = _write_y_position + (_next_write_x_position ? 1 : 0);
_next_write_x_position = _next_write_y_position = 0;
return result;
}
uint8_t *CRTInputBufferBuilder::get_write_target()
{
return (_next_write_y_position == InputBufferBuilderHeight) ? nullptr : &_image.get()[_write_target_pointer * _bytes_per_pixel];
}
uint16_t CRTInputBufferBuilder::get_last_write_x_position()
{
return _write_x_position;
}
uint16_t CRTInputBufferBuilder::get_last_write_y_position()
{
return _write_y_position;
}
size_t CRTInputBufferBuilder::get_bytes_per_pixel()
{
return _bytes_per_pixel;
} }

View File

@ -14,41 +14,44 @@
#include <stddef.h> #include <stddef.h>
#include "CRTConstants.hpp" #include "CRTConstants.hpp"
#include "OpenGL.hpp" #include "OpenGL.hpp"
#include <memory>
namespace Outputs { namespace Outputs {
namespace CRT { namespace CRT {
struct CRTInputBufferBuilder { struct CRTInputBufferBuilder {
CRTInputBufferBuilder(size_t bytes_per_pixel); CRTInputBufferBuilder(size_t bytes_per_pixel);
~CRTInputBufferBuilder();
void allocate_write_area(size_t required_length); 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);
// a pointer to the section of content buffer currently being uint16_t get_and_finalise_current_line();
// returned and to where the next section will begin uint8_t *get_image_pointer();
uint16_t _next_write_x_position, _next_write_y_position;
uint16_t _write_x_position, _write_y_position;
size_t _write_target_pointer;
size_t _last_allocation_amount;
size_t bytes_per_pixel;
// Storage for the amount of buffer uploaded so far; initialised correctly by the buffer uint8_t *get_write_target();
// builder but otherwise entrusted to the CRT to update.
unsigned int last_uploaded_line;
GLsync _wraparound_sync; uint16_t get_last_write_x_position();
inline void move_to_new_line() uint16_t get_last_write_y_position();
{
_next_write_x_position = 0;
_next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight;
}
inline uint8_t *get_write_target(uint8_t *buffer) size_t get_bytes_per_pixel();
{
return &buffer[_write_target_pointer * bytes_per_pixel]; private:
} // where pixel data will be put to the next time a write is requested
uint16_t _next_write_x_position, _next_write_y_position;
// the most recent position returned for pixel data writing
uint16_t _write_x_position, _write_y_position;
// details of the most recent allocation
size_t _write_target_pointer;
size_t _last_allocation_amount;
// the buffer size
size_t _bytes_per_pixel;
// the buffer
std::unique_ptr<uint8_t> _image;
}; };
} }

View File

@ -37,40 +37,61 @@ static const GLenum formatForDepth(size_t depth)
} }
} }
static int getCircularRanges(GLsizei start, GLsizei end, GLsizei buffer_length, GLsizei granularity, GLsizei *ranges) struct Range {
GLsizei location, length;
};
static int getCircularRanges(GLsizei start, GLsizei end, GLsizei buffer_length, GLsizei granularity, Range *ranges)
{ {
GLsizei startOffset = start%granularity; start -= start%granularity;
if(startOffset) end -= end%granularity;
{
start -= startOffset;
}
GLsizei length = end - start; GLsizei length = end - start;
if(!length) return 0; if(!length) return 0;
if(length > buffer_length) if(length >= buffer_length)
{ {
ranges[0] = 0; ranges[0].location = 0;
ranges[1] = buffer_length; ranges[0].length = buffer_length;
return 1; return 1;
} }
else else
{ {
ranges[0] = start % buffer_length; ranges[0].location = start % buffer_length;
if(ranges[0]+length < buffer_length) if(ranges[0].location + length <= buffer_length)
{ {
ranges[1] = length; ranges[0].length = length;
return 1; return 1;
} }
else else
{ {
ranges[1] = buffer_length - ranges[0]; ranges[0].length = buffer_length - ranges[0].location;
ranges[2] = 0; ranges[1].location = 0;
ranges[3] = length - ranges[1]; ranges[1].length = length - ranges[0].length;
return 2; return 2;
} }
} }
} }
static inline void drawArrayRanges(GLenum mode, GLsizei vertex_size, int number_of_ranges, Range *ranges)
{
for(int c = 0; c < number_of_ranges; c++)
{
glDrawArrays(mode, ranges[c].location / vertex_size, ranges[c].length / vertex_size);
}
}
static void submitArrayData(GLuint buffer, uint8_t *source, int number_of_ranges, Range *ranges)
{
glBindBuffer(GL_ARRAY_BUFFER, buffer);
for(int c = 0; c < number_of_ranges; c++)
{
uint8_t *data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, ranges[c].location, ranges[c].length, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
memcpy(data, &source[ranges[c].location], (size_t)ranges[c].length);
glFlushMappedBufferRange(GL_ARRAY_BUFFER, ranges[c].location, ranges[c].length);
glUnmapBuffer(GL_ARRAY_BUFFER);
}
}
using namespace Outputs::CRT; using namespace Outputs::CRT;
namespace { namespace {
@ -89,15 +110,15 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
_cleared_composite_output_y(0), _cleared_composite_output_y(0),
_composite_shader(nullptr), _composite_shader(nullptr),
_rgb_shader(nullptr), _rgb_shader(nullptr),
_output_buffer_data(nullptr), _output_buffer_data(new uint8_t[OutputVertexBufferDataSize]),
_source_buffer_data(nullptr), _source_buffer_data(new uint8_t[SourceVertexBufferDataSize]),
_input_texture_data(nullptr),
_output_buffer_data_pointer(0), _output_buffer_data_pointer(0),
_drawn_output_buffer_data_pointer(0), _drawn_output_buffer_data_pointer(0),
_source_buffer_data_pointer(0), _source_buffer_data_pointer(0),
_drawn_source_buffer_data_pointer(0), _drawn_source_buffer_data_pointer(0),
_last_output_width(0), _last_output_width(0),
_last_output_height(0) _last_output_height(0),
_fence(nullptr)
{ {
_buffer_builder = std::unique_ptr<CRTInputBufferBuilder>(new CRTInputBufferBuilder(buffer_depth)); _buffer_builder = std::unique_ptr<CRTInputBufferBuilder>(new CRTInputBufferBuilder(buffer_depth));
@ -118,16 +139,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(_buffer_builder->bytes_per_pixel), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_buffer_builder->bytes_per_pixel), GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(_buffer_builder->get_bytes_per_pixel()), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, nullptr);
// create a pixel unpack buffer
glGenBuffers(1, &_input_texture_array);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array);
_input_texture_array_size = (GLsizeiptr)(InputBufferBuilderWidth * InputBufferBuilderHeight * _buffer_builder->bytes_per_pixel);
glBufferData(GL_PIXEL_UNPACK_BUFFER, _input_texture_array_size, NULL, GL_STREAM_DRAW);
// map the buffer for clients
_input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
// create the output vertex array // create the output vertex array
glGenVertexArrays(1, &output_vertex_array); glGenVertexArrays(1, &output_vertex_array);
@ -137,9 +149,6 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
glBufferData(GL_ARRAY_BUFFER, OutputVertexBufferDataSize, NULL, GL_STREAM_DRAW); glBufferData(GL_ARRAY_BUFFER, OutputVertexBufferDataSize, NULL, GL_STREAM_DRAW);
// map that buffer too, for any CRT activity that may occur before the first draw
_output_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, OutputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
// create the source vertex array // create the source vertex array
glGenVertexArrays(1, &source_vertex_array); glGenVertexArrays(1, &source_vertex_array);
@ -147,17 +156,13 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
glGenBuffers(1, &source_array_buffer); glGenBuffers(1, &source_array_buffer);
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
glBufferData(GL_ARRAY_BUFFER, SourceVertexBufferDataSize, NULL, GL_STREAM_DRAW); glBufferData(GL_ARRAY_BUFFER, SourceVertexBufferDataSize, NULL, GL_STREAM_DRAW);
// map that buffer too, for any CRT activity that may occur before the first draw
_source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
} }
OpenGLOutputBuilder::~OpenGLOutputBuilder() OpenGLOutputBuilder::~OpenGLOutputBuilder()
{ {
glUnmapBuffer(GL_ARRAY_BUFFER); // glUnmapBuffer(GL_ARRAY_BUFFER);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glDeleteTextures(1, &textureName); glDeleteTextures(1, &textureName);
glDeleteBuffers(1, &_input_texture_array);
glDeleteBuffers(1, &output_array_buffer); glDeleteBuffers(1, &output_array_buffer);
glDeleteBuffers(1, &source_array_buffer); glDeleteBuffers(1, &source_array_buffer);
glDeleteVertexArrays(1, &output_vertex_array); glDeleteVertexArrays(1, &output_vertex_array);
@ -168,6 +173,9 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder()
void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty) void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty)
{ {
// lock down any further work on the current frame
_output_mutex->lock();
// establish essentials // establish essentials
if(!output_shader_program) if(!output_shader_program)
{ {
@ -180,30 +188,39 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
set_timing_uniforms(); set_timing_uniforms();
set_colour_space_uniforms(); set_colour_space_uniforms();
// This should return either an actual framebuffer number, if this is a target with a framebuffer intended for output,
// or 0 if no framebuffer is bound, in which case 0 is also what we want to supply to bind the implied framebuffer. So
// it works either way.
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&defaultFramebuffer);
// TODO: is this sustainable, cross-platform? If so, why store it at all?
defaultFramebuffer = 0;
} }
// lock down any further work on the current frame // determine how many lines are newly reclaimed; they'll need to be cleared
_output_mutex->lock(); Range clearing_zones[2], source_drawing_zones[2];
Range output_drawing_zones[2];
int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y, _composite_src_output_y, IntermediateBufferHeight, 1, clearing_zones);
int number_of_source_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, source_drawing_zones);
int number_of_output_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, output_drawing_zones);
uint16_t completed_texture_y = _buffer_builder->get_and_finalise_current_line();
_composite_src_output_y %= IntermediateBufferHeight;
_source_buffer_data_pointer %= SourceVertexBufferDataSize;
_output_buffer_data_pointer %= OutputVertexBufferDataSize;
_cleared_composite_output_y = _composite_src_output_y;
_drawn_source_buffer_data_pointer = _source_buffer_data_pointer;
_drawn_output_buffer_data_pointer = _output_buffer_data_pointer;
if(_fence != nullptr)
{
glClientWaitSync(_fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(_fence);
}
// release the mapping, giving up on trying to draw if data has been lost // release the mapping, giving up on trying to draw if data has been lost
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); submitArrayData(output_array_buffer, _output_buffer_data.get(), number_of_output_drawing_zones, output_drawing_zones);
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); // bind and flush the source array buffer
glUnmapBuffer(GL_ARRAY_BUFFER); submitArrayData(source_array_buffer, _source_buffer_data.get(), number_of_source_drawing_zones, source_drawing_zones);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
// make sure there's a target to draw to // make sure there's a target to draw to
if(!framebuffer || framebuffer->get_height() != output_height || framebuffer->get_width() != output_width) if(!framebuffer || framebuffer->get_height() != output_height || framebuffer->get_width() != output_width)
{ {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
std::unique_ptr<OpenGL::TextureTarget> new_framebuffer = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget((GLsizei)output_width, (GLsizei)output_height, pixel_accumulation_texture_unit)); std::unique_ptr<OpenGL::TextureTarget> new_framebuffer = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget((GLsizei)output_width, (GLsizei)output_height, pixel_accumulation_texture_unit));
if(framebuffer) if(framebuffer)
{ {
@ -217,30 +234,17 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
new_framebuffer->bind_texture(); new_framebuffer->bind_texture();
} }
framebuffer = std::move(new_framebuffer); framebuffer = std::move(new_framebuffer);
}
// upload new source pixels
if(completed_texture_y)
{
glActiveTexture(source_data_texture_unit); glActiveTexture(source_data_texture_unit);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array);
}
// 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
if(_buffer_builder->_write_y_position < _buffer_builder->last_uploaded_line)
{
glTexSubImage2D( GL_TEXTURE_2D, 0, glTexSubImage2D( GL_TEXTURE_2D, 0,
0, (GLint)_buffer_builder->last_uploaded_line, 0, 0,
InputBufferBuilderWidth, (GLint)(InputBufferBuilderHeight - _buffer_builder->last_uploaded_line), InputBufferBuilderWidth, completed_texture_y,
formatForDepth(_buffer_builder->bytes_per_pixel), GL_UNSIGNED_BYTE, formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE,
(void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel)); _buffer_builder->get_image_pointer());
_buffer_builder->last_uploaded_line = 0;
}
if(_buffer_builder->_write_y_position > _buffer_builder->last_uploaded_line)
{
glTexSubImage2D( GL_TEXTURE_2D, 0,
0, (GLint)_buffer_builder->last_uploaded_line,
InputBufferBuilderWidth, (GLint)(1 + _buffer_builder->_next_write_y_position - _buffer_builder->last_uploaded_line),
formatForDepth(_buffer_builder->bytes_per_pixel), GL_UNSIGNED_BYTE,
(void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel));
_buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position;
} }
struct RenderStage { struct RenderStage {
@ -268,29 +272,12 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
RenderStage *active_pipeline = (_output_device == Television || !rgb_input_shader_program) ? composite_render_stages : rgb_render_stages; RenderStage *active_pipeline = (_output_device == Television || !rgb_input_shader_program) ? composite_render_stages : rgb_render_stages;
// for television, update intermediate buffers and then draw; for a monitor, just draw // for television, update intermediate buffers and then draw; for a monitor, just draw
if(_drawn_source_buffer_data_pointer != _source_buffer_data_pointer) if(number_of_source_drawing_zones)
{ {
// determine how many lines are newly reclaimed; they'll need to be cleared
GLsizei clearing_zones[4], drawing_zones[4];
int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y+1, _composite_src_output_y+1, IntermediateBufferHeight, 1, clearing_zones);
int number_of_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, drawing_zones);
_composite_src_output_y %= IntermediateBufferHeight;
_cleared_composite_output_y = _composite_src_output_y;
_source_buffer_data_pointer %= SourceVertexBufferDataSize;
_drawn_source_buffer_data_pointer = _source_buffer_data_pointer;
// all drawing will be from the source vertex array and without blending // all drawing will be from the source vertex array and without blending
glBindVertexArray(source_vertex_array); glBindVertexArray(source_vertex_array);
glDisable(GL_BLEND); glDisable(GL_BLEND);
// flush the source data
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
for(int c = 0; c < number_of_drawing_zones; c++)
{
glFlushMappedBufferRange(GL_ARRAY_BUFFER, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize);
}
while(active_pipeline->target) while(active_pipeline->target)
{ {
// switch to the initial texture // switch to the initial texture
@ -304,17 +291,14 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0); glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0);
for(int c = 0; c < number_of_clearing_zones; c++) for(int c = 0; c < number_of_clearing_zones; c++)
{ {
glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]); glScissor(0, clearing_zones[c].location, IntermediateBufferWidth, clearing_zones[c].length);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
} }
// draw as desired // draw as desired
for(int c = 0; c < number_of_drawing_zones; c++) drawArrayRanges(GL_LINES, SourceVertexSize, number_of_source_drawing_zones, source_drawing_zones);
{
glDrawArrays(GL_LINES, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize);
}
active_pipeline++; active_pipeline++;
} }
@ -323,22 +307,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
// transfer to framebuffer // transfer to framebuffer
framebuffer->bind_framebuffer(); framebuffer->bind_framebuffer();
// draw all pending lines if(number_of_output_drawing_zones)
GLsizei drawing_zones[4];
int number_of_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, drawing_zones);
// flush the buffer data
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
for(int c = 0; c < number_of_drawing_zones; c++)
{
glFlushMappedBufferRange(GL_ARRAY_BUFFER, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize);
}
_output_buffer_data_pointer %= SourceVertexBufferDataSize;
_output_buffer_data_pointer -= (_output_buffer_data_pointer%(6*OutputVertexSize));
_drawn_output_buffer_data_pointer = _output_buffer_data_pointer;
if(number_of_drawing_zones > 0)
{ {
glEnable(GL_BLEND); glEnable(GL_BLEND);
@ -355,10 +324,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
output_shader_program->bind(); output_shader_program->bind();
// draw // draw
for(int c = 0; c < number_of_drawing_zones; c++) drawArrayRanges(GL_TRIANGLE_STRIP, OutputVertexSize, number_of_output_drawing_zones, output_drawing_zones);
{
glDrawArrays(GL_TRIANGLE_STRIP, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize);
}
} }
// copy framebuffer to the intended place // copy framebuffer to the intended place
@ -366,17 +332,10 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height); glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
framebuffer->draw((float)output_width / (float)output_height); framebuffer->draw((float)output_width / (float)output_height);
// drawing commands having been issued, reclaim the array buffer pointer _fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
_output_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, OutputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
_source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
_input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
_output_mutex->unlock(); _output_mutex->unlock();
} }
@ -433,24 +392,24 @@ void OpenGLOutputBuilder::prepare_source_vertex_array()
{ {
if(composite_input_shader_program) if(composite_input_shader_program)
{ {
GLint inputPositionAttribute = composite_input_shader_program->get_attrib_location("inputPosition"); GLint inputPositionAttribute = composite_input_shader_program->get_attrib_location("inputPosition");
GLint outputPositionAttribute = composite_input_shader_program->get_attrib_location("outputPosition"); GLint outputPositionAttribute = composite_input_shader_program->get_attrib_location("outputPosition");
GLint phaseAmplitudeAndOffsetAttribute = composite_input_shader_program->get_attrib_location("phaseAmplitudeAndOffset"); GLint phaseAndAmplitudeAttribute = composite_input_shader_program->get_attrib_location("phaseAndAmplitude");
GLint phaseTimeAttribute = composite_input_shader_program->get_attrib_location("phaseTime"); GLint phaseTimeAttribute = composite_input_shader_program->get_attrib_location("phaseTime");
glBindVertexArray(source_vertex_array); glBindVertexArray(source_vertex_array);
glEnableVertexAttribArray((GLuint)inputPositionAttribute); glEnableVertexAttribArray((GLuint)inputPositionAttribute);
glEnableVertexAttribArray((GLuint)outputPositionAttribute); glEnableVertexAttribArray((GLuint)outputPositionAttribute);
glEnableVertexAttribArray((GLuint)phaseAmplitudeAndOffsetAttribute); glEnableVertexAttribArray((GLuint)phaseAndAmplitudeAttribute);
glEnableVertexAttribArray((GLuint)phaseTimeAttribute); glEnableVertexAttribArray((GLuint)phaseTimeAttribute);
const GLsizei vertexStride = SourceVertexSize; const GLsizei vertexStride = SourceVertexSize;
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
glVertexAttribPointer((GLuint)inputPositionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfInputPosition); glVertexAttribPointer((GLuint)inputPositionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfInputPosition);
glVertexAttribPointer((GLuint)outputPositionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfOutputPosition); glVertexAttribPointer((GLuint)outputPositionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfOutputPosition);
glVertexAttribPointer((GLuint)phaseAmplitudeAndOffsetAttribute, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexStride, (void *)SourceVertexOffsetOfPhaseAmplitudeAndOffset); glVertexAttribPointer((GLuint)phaseAndAmplitudeAttribute, 2, GL_UNSIGNED_BYTE, GL_TRUE, vertexStride, (void *)SourceVertexOffsetOfPhaseAndAmplitude);
glVertexAttribPointer((GLuint)phaseTimeAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfPhaseTime); glVertexAttribPointer((GLuint)phaseTimeAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfPhaseTime);
} }
} }
@ -466,19 +425,16 @@ void OpenGLOutputBuilder::prepare_output_vertex_array()
{ {
GLint positionAttribute = output_shader_program->get_attrib_location("position"); GLint positionAttribute = output_shader_program->get_attrib_location("position");
GLint textureCoordinatesAttribute = output_shader_program->get_attrib_location("srcCoordinates"); GLint textureCoordinatesAttribute = output_shader_program->get_attrib_location("srcCoordinates");
GLint lateralAttribute = output_shader_program->get_attrib_location("lateral");
glBindVertexArray(output_vertex_array); glBindVertexArray(output_vertex_array);
glEnableVertexAttribArray((GLuint)positionAttribute); glEnableVertexAttribArray((GLuint)positionAttribute);
glEnableVertexAttribArray((GLuint)textureCoordinatesAttribute); glEnableVertexAttribArray((GLuint)textureCoordinatesAttribute);
glEnableVertexAttribArray((GLuint)lateralAttribute);
const GLsizei vertexStride = OutputVertexSize; const GLsizei vertexStride = OutputVertexSize;
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
glVertexAttribPointer((GLuint)positionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfPosition); glVertexAttribPointer((GLuint)positionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfPosition);
glVertexAttribPointer((GLuint)textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfTexCoord); glVertexAttribPointer((GLuint)textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfTexCoord);
glVertexAttribPointer((GLuint)lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfLateral);
} }
} }
@ -497,6 +453,7 @@ void OpenGLOutputBuilder::set_output_device(OutputDevice output_device)
void OpenGLOutputBuilder::set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider) void OpenGLOutputBuilder::set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider)
{ {
_output_mutex->lock();
_input_frequency = input_frequency; _input_frequency = input_frequency;
_cycles_per_line = cycles_per_line; _cycles_per_line = cycles_per_line;
_height_of_display = height_of_display; _height_of_display = height_of_display;
@ -505,13 +462,13 @@ void OpenGLOutputBuilder::set_timing(unsigned int input_frequency, unsigned int
_vertical_period_divider = vertical_period_divider; _vertical_period_divider = vertical_period_divider;
set_timing_uniforms(); set_timing_uniforms();
_output_mutex->unlock();
} }
#pragma mark - Internal Configuration #pragma mark - Internal Configuration
void OpenGLOutputBuilder::set_colour_space_uniforms() void OpenGLOutputBuilder::set_colour_space_uniforms()
{ {
_output_mutex->lock();
GLfloat rgbToYUV[] = {0.299f, -0.14713f, 0.615f, 0.587f, -0.28886f, -0.51499f, 0.114f, 0.436f, -0.10001f}; GLfloat rgbToYUV[] = {0.299f, -0.14713f, 0.615f, 0.587f, -0.28886f, -0.51499f, 0.114f, 0.436f, -0.10001f};
GLfloat yuvToRGB[] = {1.0f, 1.0f, 1.0f, 0.0f, -0.39465f, 2.03211f, 1.13983f, -0.58060f, 0.0f}; GLfloat yuvToRGB[] = {1.0f, 1.0f, 1.0f, 0.0f, -0.39465f, 2.03211f, 1.13983f, -0.58060f, 0.0f};
@ -535,13 +492,10 @@ void OpenGLOutputBuilder::set_colour_space_uniforms()
if(composite_input_shader_program) composite_input_shader_program->set_colour_conversion_matrices(fromRGB, toRGB); if(composite_input_shader_program) composite_input_shader_program->set_colour_conversion_matrices(fromRGB, toRGB);
if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_colour_conversion_matrices(fromRGB, toRGB); if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_colour_conversion_matrices(fromRGB, toRGB);
_output_mutex->unlock();
} }
void OpenGLOutputBuilder::set_timing_uniforms() void OpenGLOutputBuilder::set_timing_uniforms()
{ {
_output_mutex->lock();
OpenGL::IntermediateShader *intermediate_shaders[] = { OpenGL::IntermediateShader *intermediate_shaders[] = {
composite_input_shader_program.get(), composite_input_shader_program.get(),
composite_separation_filter_program.get(), composite_separation_filter_program.get(),
@ -563,6 +517,4 @@ void OpenGLOutputBuilder::set_timing_uniforms()
if(composite_y_filter_shader_program) composite_y_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.66f); if(composite_y_filter_shader_program) composite_y_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.66f);
if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.5f); if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.5f);
if(rgb_filter_shader_program) rgb_filter_shader_program->set_filter_coefficients(_cycles_per_line, (float)_input_frequency * 0.5f); if(rgb_filter_shader_program) rgb_filter_shader_program->set_filter_coefficients(_cycles_per_line, (float)_input_frequency * 0.5f);
_output_mutex->unlock();
} }

View File

@ -57,7 +57,7 @@ class OpenGLOutputBuilder {
// the run and input data buffers // the run and input data buffers
std::unique_ptr<CRTInputBufferBuilder> _buffer_builder; std::unique_ptr<CRTInputBufferBuilder> _buffer_builder;
std::shared_ptr<std::mutex> _output_mutex; std::unique_ptr<std::mutex> _output_mutex;
// transient buffers indicating composite data not yet decoded // transient buffers indicating composite data not yet decoded
uint16_t _composite_src_output_y, _cleared_composite_output_y; uint16_t _composite_src_output_y, _cleared_composite_output_y;
@ -91,10 +91,12 @@ class OpenGLOutputBuilder {
inline void set_colour_format(ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator) inline void set_colour_format(ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator)
{ {
_output_mutex->lock();
_colour_space = colour_space; _colour_space = colour_space;
_colour_cycle_numerator = colour_cycle_numerator; _colour_cycle_numerator = colour_cycle_numerator;
_colour_cycle_denominator = colour_cycle_denominator; _colour_cycle_denominator = colour_cycle_denominator;
set_colour_space_uniforms(); set_colour_space_uniforms();
_output_mutex->unlock();
} }
inline void set_visible_area(Rect visible_area) inline void set_visible_area(Rect visible_area)
@ -104,8 +106,9 @@ class OpenGLOutputBuilder {
inline uint8_t *get_next_source_run() inline uint8_t *get_next_source_run()
{ {
if(_source_buffer_data_pointer == _drawn_source_buffer_data_pointer + SourceVertexBufferDataSize) return nullptr;
_output_mutex->lock(); _output_mutex->lock();
return &_source_buffer_data[_source_buffer_data_pointer % SourceVertexBufferDataSize]; return &_source_buffer_data.get()[_source_buffer_data_pointer % SourceVertexBufferDataSize];
} }
inline void complete_source_run() inline void complete_source_run()
@ -114,10 +117,16 @@ class OpenGLOutputBuilder {
_output_mutex->unlock(); _output_mutex->unlock();
} }
inline bool composite_output_run_has_room_for_vertices(GLsizei vertices_to_write)
{
return _output_buffer_data_pointer <= _drawn_output_buffer_data_pointer + OutputVertexBufferDataSize - vertices_to_write * OutputVertexSize;
}
inline uint8_t *get_next_output_run() inline uint8_t *get_next_output_run()
{ {
if(_output_buffer_data_pointer == _drawn_output_buffer_data_pointer + OutputVertexBufferDataSize) return nullptr;
_output_mutex->lock(); _output_mutex->lock();
return &_output_buffer_data[_output_buffer_data_pointer % OutputVertexBufferDataSize]; return &_output_buffer_data.get()[_output_buffer_data_pointer % OutputVertexBufferDataSize];
} }
inline void complete_output_run(GLsizei vertices_written) inline void complete_output_run(GLsizei vertices_written)
@ -131,38 +140,46 @@ class OpenGLOutputBuilder {
return _output_device; return _output_device;
} }
inline bool composite_output_buffer_has_room_for_vertices(GLsizei vertices_to_write)
{
return _composite_src_output_y <= _cleared_composite_output_y + IntermediateBufferHeight - vertices_to_write * OutputVertexSize;
}
inline uint16_t get_composite_output_y() inline uint16_t get_composite_output_y()
{ {
return _composite_src_output_y % IntermediateBufferHeight; return _composite_src_output_y % IntermediateBufferHeight;
} }
inline bool composite_output_buffer_is_full()
{
return _composite_src_output_y == _cleared_composite_output_y + IntermediateBufferHeight;
}
inline void increment_composite_output_y() inline void increment_composite_output_y()
{ {
_composite_src_output_y++; if(!composite_output_buffer_is_full())
_composite_src_output_y++;
} }
inline uint8_t *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); _buffer_builder->allocate_write_area(required_length);
uint8_t *output = _input_texture_data ? _buffer_builder->get_write_target(_input_texture_data) : nullptr; return _buffer_builder->get_write_target();
_output_mutex->unlock();
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);
} }
inline uint16_t get_last_write_x_posiiton() inline uint16_t get_last_write_x_posititon()
{ {
return _buffer_builder->_write_x_position; return _buffer_builder->get_last_write_x_position();
} }
inline uint16_t get_last_write_y_posiiton() inline uint16_t get_last_write_y_posititon()
{ {
return _buffer_builder->_write_y_position; return _buffer_builder->get_last_write_y_position();
} }
void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty); void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty);
@ -172,18 +189,15 @@ class OpenGLOutputBuilder {
void set_output_device(OutputDevice output_device); void set_output_device(OutputDevice output_device);
void set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider); void set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider);
uint8_t *_input_texture_data; std::unique_ptr<uint8_t> _source_buffer_data;
GLuint _input_texture_array;
GLsync _input_texture_sync;
GLsizeiptr _input_texture_array_size;
uint8_t *_source_buffer_data;
GLsizei _source_buffer_data_pointer; GLsizei _source_buffer_data_pointer;
GLsizei _drawn_source_buffer_data_pointer; GLsizei _drawn_source_buffer_data_pointer;
uint8_t *_output_buffer_data; std::unique_ptr<uint8_t> _output_buffer_data;
GLsizei _output_buffer_data_pointer; GLsizei _output_buffer_data_pointer;
GLsizei _drawn_output_buffer_data_pointer; GLsizei _drawn_output_buffer_data_pointer;
GLsync _fence;
}; };
} }

View File

@ -20,7 +20,7 @@ namespace {
{ {
{"inputPosition", 0}, {"inputPosition", 0},
{"outputPosition", 1}, {"outputPosition", 1},
{"phaseAmplitudeAndOffset", 2}, {"phaseAndAmplitude", 2},
{"phaseTime", 3}, {"phaseTime", 3},
{nullptr} {nullptr}
}; };
@ -37,7 +37,7 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
"in vec2 inputPosition;" "in vec2 inputPosition;"
"in vec2 outputPosition;" "in vec2 outputPosition;"
"in vec3 phaseAmplitudeAndOffset;" "in vec2 phaseAndAmplitude;"
"in float phaseTime;" "in float phaseTime;"
"uniform float phaseCyclesPerTick;" "uniform float phaseCyclesPerTick;"
@ -53,7 +53,8 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
"void main(void)" "void main(void)"
"{" "{"
"vec2 extensionVector = vec2(extension, 0.0) * 2.0 * (phaseAmplitudeAndOffset.z - 0.5);" "float direction = float(gl_VertexID & 1);"
"vec2 extensionVector = vec2(extension, 0.0) * 2.0 * (direction - 0.5);"
"vec2 extendedInputPosition = %s + extensionVector;" "vec2 extendedInputPosition = %s + extensionVector;"
"vec2 extendedOutputPosition = outputPosition + extensionVector;" "vec2 extendedOutputPosition = outputPosition + extensionVector;"
@ -74,10 +75,10 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
"inputPositionsVarying[10] = mappedInputPosition + (vec2(offsets[0], 0.0) / textureSize);" "inputPositionsVarying[10] = mappedInputPosition + (vec2(offsets[0], 0.0) / textureSize);"
"delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);" "delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);"
"phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAmplitudeAndOffset.x) * 2.0 * 3.141592654;" "phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAndAmplitude.x) * 2.0 * 3.141592654;"
"phaseAndAmplitudeVarying.y = 0.33;" "phaseAndAmplitudeVarying.y = 0.33;" // TODO: reinstate connection with phaseAndAmplitude
"vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(0.5)/textureSize;" "vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(1.0)/outputTextureSize;"
"gl_Position = vec4(eyePosition, 0.0, 1.0);" "gl_Position = vec4(eyePosition, 0.0, 1.0);"
"}", sampler_type, input_variable); "}", sampler_type, input_variable);

View File

@ -18,7 +18,6 @@ namespace {
{ {
{"position", 0}, {"position", 0},
{"srcCoordinates", 1}, {"srcCoordinates", 1},
{"lateral", 2},
{nullptr} {nullptr}
}; };
} }
@ -33,7 +32,6 @@ std::unique_ptr<OutputShader> OutputShader::make_shader(const char *fragment_met
"in vec2 position;" "in vec2 position;"
"in vec2 srcCoordinates;" "in vec2 srcCoordinates;"
"in float lateral;"
"uniform vec2 boundsOrigin;" "uniform vec2 boundsOrigin;"
"uniform vec2 boundsSize;" "uniform vec2 boundsSize;"
@ -47,6 +45,8 @@ std::unique_ptr<OutputShader> OutputShader::make_shader(const char *fragment_met
"void main(void)" "void main(void)"
"{" "{"
"float laterals[] = float[](0, 0, 1, 0, 1, 1);"
"float lateral = laterals[gl_VertexID %% 6];"
"lateralVarying = lateral - 0.5;" "lateralVarying = lateral - 0.5;"
"ivec2 textureSize = textureSize(texID, 0);" "ivec2 textureSize = textureSize(texID, 0);"

View File

@ -8,6 +8,7 @@
#include "TextureTarget.hpp" #include "TextureTarget.hpp"
#include <math.h> #include <math.h>
#include <stdlib.h>
using namespace OpenGL; using namespace OpenGL;
@ -29,7 +30,9 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit)
glGenTextures(1, &_texture); glGenTextures(1, &_texture);
glActiveTexture(texture_unit); glActiveTexture(texture_unit);
glBindTexture(GL_TEXTURE_2D, _texture); glBindTexture(GL_TEXTURE_2D, _texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)_expanded_width, (GLsizei)_expanded_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); uint8_t *blank_buffer = (uint8_t *)calloc((size_t)(_expanded_width * _expanded_height), 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)_expanded_width, (GLsizei)_expanded_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, blank_buffer);
free(blank_buffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);