mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-15 05:31:30 +00:00
Merge pull request #5 from TomHarte/Glitches
Started tackling the sources of graphical glitching
This commit is contained in:
commit
95ee2fffdb
@ -157,7 +157,7 @@ void Machine::get_output_pixel(uint8_t *pixel, int offset)
|
||||
}
|
||||
|
||||
// store colour
|
||||
pixel[0] = outputColour;
|
||||
*pixel = outputColour;
|
||||
}
|
||||
|
||||
// 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(_outputBuffer)
|
||||
get_output_pixel(&_outputBuffer[_lastOutputStateDuration], 159 - _horizontalTimer);
|
||||
uint8_t throwaway_pixel;
|
||||
get_output_pixel(_outputBuffer ? &_outputBuffer[_lastOutputStateDuration] : &throwaway_pixel, 159 - _horizontalTimer);
|
||||
|
||||
// increment all graphics counters
|
||||
increment_object_counter(0);
|
||||
|
@ -537,7 +537,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()
|
||||
@ -564,7 +564,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;
|
||||
@ -582,97 +582,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;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,8 @@ CRT::CRT(unsigned int common_output_divisor) :
|
||||
_common_output_divisor(common_output_divisor),
|
||||
_is_writing_composite_run(false),
|
||||
_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)
|
||||
{
|
||||
@ -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_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_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_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_y(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfOutputPosition + 2])
|
||||
#define source_phase(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAmplitudeAndOffset + 0]
|
||||
#define source_amplitude(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAmplitudeAndOffset + 1]
|
||||
#define source_offset(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAmplitudeAndOffset + 2]
|
||||
#define source_phase(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAndAmplitude + 0]
|
||||
#define source_amplitude(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAndAmplitude + 1]
|
||||
#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)
|
||||
@ -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());
|
||||
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();
|
||||
}
|
||||
@ -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_amplitude(0) = source_amplitude(1) = _colour_burst_amplitude;
|
||||
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
|
||||
@ -189,17 +186,23 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
|
||||
|
||||
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();
|
||||
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_lateral(0) = 0;
|
||||
output_lateral(1) = _is_writing_composite_run ? 1 : 0;
|
||||
output_lateral(2) = 1;
|
||||
|
||||
_openGL_output_builder->complete_output_run(3);
|
||||
_openGL_output_builder->complete_output_run(3);
|
||||
_did_start_run ^= true;
|
||||
}
|
||||
}
|
||||
_is_writing_composite_run ^= true;
|
||||
}
|
||||
|
||||
@ -297,8 +300,8 @@ void CRT::output_level(unsigned int number_of_cycles)
|
||||
Scan scan {
|
||||
.type = Scan::Type::Level,
|
||||
.number_of_cycles = number_of_cycles,
|
||||
.tex_x = _openGL_output_builder->get_last_write_x_posiiton(),
|
||||
.tex_y = _openGL_output_builder->get_last_write_y_posiiton()
|
||||
.tex_x = _openGL_output_builder->get_last_write_x_posititon(),
|
||||
.tex_y = _openGL_output_builder->get_last_write_y_posititon()
|
||||
};
|
||||
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)
|
||||
{
|
||||
_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_posiiton(),
|
||||
.tex_y = _openGL_output_builder->get_last_write_y_posiiton(),
|
||||
.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)
|
||||
|
@ -68,7 +68,7 @@ class CRT {
|
||||
|
||||
uint8_t _colour_burst_phase, _colour_burst_amplitude;
|
||||
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
|
||||
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);
|
||||
|
||||
/*! 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)
|
||||
{
|
||||
@ -240,18 +242,6 @@ class CRT {
|
||||
_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)
|
||||
{
|
||||
_openGL_output_builder->set_output_device(output_device);
|
||||
|
@ -19,30 +19,29 @@ namespace CRT {
|
||||
// or is one of the intermediate buffers that we've used to convert from composite towards RGB.
|
||||
const GLsizei OutputVertexOffsetOfPosition = 0;
|
||||
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
|
||||
// remapping occurs to ensure a continous stream of data for each scan, giving correct out-of-bounds behaviour
|
||||
const GLsizei SourceVertexOffsetOfInputPosition = 0;
|
||||
const GLsizei SourceVertexOffsetOfOutputPosition = 4;
|
||||
const GLsizei SourceVertexOffsetOfPhaseAmplitudeAndOffset = 8;
|
||||
const GLsizei SourceVertexOffsetOfPhaseAndAmplitude = 8;
|
||||
const GLsizei SourceVertexOffsetOfPhaseTime = 12;
|
||||
|
||||
const GLsizei SourceVertexSize = 16;
|
||||
|
||||
// These constants hold the size of the rolling buffer to which the CPU writes
|
||||
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
|
||||
const GLsizei IntermediateBufferWidth = 2048;
|
||||
const GLsizei IntermediateBufferHeight = 2048;
|
||||
const GLsizei IntermediateBufferHeight = 512;
|
||||
|
||||
// Some internal buffer sizes
|
||||
const GLsizeiptr OutputVertexBufferDataSize = 89856; // a multiple of 6 * OutputVertexSize
|
||||
const GLsizeiptr SourceVertexBufferDataSize = 87360; // a multiple of 2 * SourceVertexSize
|
||||
const GLsizeiptr OutputVertexBufferDataSize = 6 * 8 * 312; // a multiple of 6 * OutputVertexSize
|
||||
const GLsizeiptr SourceVertexBufferDataSize = 2 * 16 * 312; // a multiple of 2 * SourceVertexSize
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -13,45 +13,93 @@
|
||||
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),
|
||||
_wraparound_sync(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
|
||||
_image(new uint8_t[bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight])
|
||||
{}
|
||||
|
||||
CRTInputBufferBuilder::~CRTInputBufferBuilder()
|
||||
{
|
||||
glDeleteSync(_wraparound_sync);
|
||||
}
|
||||
|
||||
void CRTInputBufferBuilder::allocate_write_area(size_t required_length)
|
||||
{
|
||||
_last_allocation_amount = required_length;
|
||||
|
||||
if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth)
|
||||
if(_next_write_y_position != InputBufferBuilderHeight)
|
||||
{
|
||||
move_to_new_line();
|
||||
}
|
||||
_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 * 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++;
|
||||
|
||||
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
|
||||
// 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( &image_pointer[(_write_target_pointer - 1) * _bytes_per_pixel],
|
||||
&image_pointer[_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( &image_pointer[(_write_target_pointer + actual_length) * _bytes_per_pixel],
|
||||
&image_pointer[(_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -14,41 +14,44 @@
|
||||
#include <stddef.h>
|
||||
#include "CRTConstants.hpp"
|
||||
#include "OpenGL.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace Outputs {
|
||||
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);
|
||||
|
||||
// a pointer to the section of content buffer currently being
|
||||
// returned and to where the next section will begin
|
||||
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;
|
||||
uint16_t get_and_finalise_current_line();
|
||||
uint8_t *get_image_pointer();
|
||||
|
||||
// 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;
|
||||
uint8_t *get_write_target();
|
||||
|
||||
GLsync _wraparound_sync;
|
||||
uint16_t get_last_write_x_position();
|
||||
|
||||
inline void move_to_new_line()
|
||||
{
|
||||
_next_write_x_position = 0;
|
||||
_next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight;
|
||||
}
|
||||
uint16_t get_last_write_y_position();
|
||||
|
||||
inline uint8_t *get_write_target(uint8_t *buffer)
|
||||
{
|
||||
return &buffer[_write_target_pointer * bytes_per_pixel];
|
||||
}
|
||||
size_t get_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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
if(startOffset)
|
||||
{
|
||||
start -= startOffset;
|
||||
}
|
||||
start -= start%granularity;
|
||||
end -= end%granularity;
|
||||
|
||||
GLsizei length = end - start;
|
||||
if(!length) return 0;
|
||||
if(length > buffer_length)
|
||||
if(length >= buffer_length)
|
||||
{
|
||||
ranges[0] = 0;
|
||||
ranges[1] = buffer_length;
|
||||
ranges[0].location = 0;
|
||||
ranges[0].length = buffer_length;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ranges[0] = start % buffer_length;
|
||||
if(ranges[0]+length < buffer_length)
|
||||
ranges[0].location = start % buffer_length;
|
||||
if(ranges[0].location + length <= buffer_length)
|
||||
{
|
||||
ranges[1] = length;
|
||||
ranges[0].length = length;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ranges[1] = buffer_length - ranges[0];
|
||||
ranges[2] = 0;
|
||||
ranges[3] = length - ranges[1];
|
||||
ranges[0].length = buffer_length - ranges[0].location;
|
||||
ranges[1].location = 0;
|
||||
ranges[1].length = length - ranges[0].length;
|
||||
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;
|
||||
|
||||
namespace {
|
||||
@ -89,15 +110,15 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
|
||||
_cleared_composite_output_y(0),
|
||||
_composite_shader(nullptr),
|
||||
_rgb_shader(nullptr),
|
||||
_output_buffer_data(nullptr),
|
||||
_source_buffer_data(nullptr),
|
||||
_input_texture_data(nullptr),
|
||||
_output_buffer_data(new uint8_t[OutputVertexBufferDataSize]),
|
||||
_source_buffer_data(new uint8_t[SourceVertexBufferDataSize]),
|
||||
_output_buffer_data_pointer(0),
|
||||
_drawn_output_buffer_data_pointer(0),
|
||||
_source_buffer_data_pointer(0),
|
||||
_drawn_source_buffer_data_pointer(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));
|
||||
|
||||
@ -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_MAG_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);
|
||||
|
||||
// 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);
|
||||
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 the 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);
|
||||
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
|
||||
glGenVertexArrays(1, &source_vertex_array);
|
||||
|
||||
@ -147,17 +156,13 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
|
||||
glGenBuffers(1, &source_array_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
|
||||
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()
|
||||
{
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
// glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
// glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
glDeleteTextures(1, &textureName);
|
||||
glDeleteBuffers(1, &_input_texture_array);
|
||||
glDeleteBuffers(1, &output_array_buffer);
|
||||
glDeleteBuffers(1, &source_array_buffer);
|
||||
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)
|
||||
{
|
||||
// lock down any further work on the current frame
|
||||
_output_mutex->lock();
|
||||
|
||||
// establish essentials
|
||||
if(!output_shader_program)
|
||||
{
|
||||
@ -180,30 +188,39 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
||||
|
||||
set_timing_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
|
||||
_output_mutex->lock();
|
||||
// determine how many lines are newly reclaimed; they'll need to be cleared
|
||||
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
|
||||
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
submitArrayData(output_array_buffer, _output_buffer_data.get(), number_of_output_drawing_zones, output_drawing_zones);
|
||||
|
||||
// bind and flush the source array buffer
|
||||
submitArrayData(source_array_buffer, _source_buffer_data.get(), number_of_source_drawing_zones, source_drawing_zones);
|
||||
|
||||
// make sure there's a target to draw to
|
||||
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));
|
||||
if(framebuffer)
|
||||
{
|
||||
@ -217,30 +234,17 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
||||
new_framebuffer->bind_texture();
|
||||
}
|
||||
framebuffer = std::move(new_framebuffer);
|
||||
}
|
||||
|
||||
// upload new source pixels
|
||||
if(completed_texture_y)
|
||||
{
|
||||
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,
|
||||
0, (GLint)_buffer_builder->last_uploaded_line,
|
||||
InputBufferBuilderWidth, (GLint)(InputBufferBuilderHeight - _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 = 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;
|
||||
0, 0,
|
||||
InputBufferBuilderWidth, completed_texture_y,
|
||||
formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE,
|
||||
_buffer_builder->get_image_pointer());
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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
|
||||
glBindVertexArray(source_vertex_array);
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
// draw as desired
|
||||
for(int c = 0; c < number_of_drawing_zones; c++)
|
||||
{
|
||||
glDrawArrays(GL_LINES, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize);
|
||||
}
|
||||
drawArrayRanges(GL_LINES, SourceVertexSize, number_of_source_drawing_zones, source_drawing_zones);
|
||||
|
||||
active_pipeline++;
|
||||
}
|
||||
@ -323,22 +307,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
||||
// transfer to framebuffer
|
||||
framebuffer->bind_framebuffer();
|
||||
|
||||
// draw all pending lines
|
||||
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)
|
||||
if(number_of_output_drawing_zones)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
@ -355,10 +324,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
||||
output_shader_program->bind();
|
||||
|
||||
// draw
|
||||
for(int c = 0; c < number_of_drawing_zones; c++)
|
||||
{
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize);
|
||||
}
|
||||
drawArrayRanges(GL_TRIANGLE_STRIP, OutputVertexSize, number_of_output_drawing_zones, output_drawing_zones);
|
||||
}
|
||||
|
||||
// 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);
|
||||
glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
framebuffer->draw((float)output_width / (float)output_height);
|
||||
|
||||
// drawing commands having been issued, reclaim the array buffer pointer
|
||||
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);
|
||||
|
||||
_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
_output_mutex->unlock();
|
||||
}
|
||||
|
||||
@ -433,24 +392,24 @@ void OpenGLOutputBuilder::prepare_source_vertex_array()
|
||||
{
|
||||
if(composite_input_shader_program)
|
||||
{
|
||||
GLint inputPositionAttribute = composite_input_shader_program->get_attrib_location("inputPosition");
|
||||
GLint outputPositionAttribute = composite_input_shader_program->get_attrib_location("outputPosition");
|
||||
GLint phaseAmplitudeAndOffsetAttribute = composite_input_shader_program->get_attrib_location("phaseAmplitudeAndOffset");
|
||||
GLint phaseTimeAttribute = composite_input_shader_program->get_attrib_location("phaseTime");
|
||||
GLint inputPositionAttribute = composite_input_shader_program->get_attrib_location("inputPosition");
|
||||
GLint outputPositionAttribute = composite_input_shader_program->get_attrib_location("outputPosition");
|
||||
GLint phaseAndAmplitudeAttribute = composite_input_shader_program->get_attrib_location("phaseAndAmplitude");
|
||||
GLint phaseTimeAttribute = composite_input_shader_program->get_attrib_location("phaseTime");
|
||||
|
||||
glBindVertexArray(source_vertex_array);
|
||||
|
||||
glEnableVertexAttribArray((GLuint)inputPositionAttribute);
|
||||
glEnableVertexAttribArray((GLuint)outputPositionAttribute);
|
||||
glEnableVertexAttribArray((GLuint)phaseAmplitudeAndOffsetAttribute);
|
||||
glEnableVertexAttribArray((GLuint)phaseAndAmplitudeAttribute);
|
||||
glEnableVertexAttribArray((GLuint)phaseTimeAttribute);
|
||||
|
||||
const GLsizei vertexStride = SourceVertexSize;
|
||||
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
|
||||
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)phaseAmplitudeAndOffsetAttribute, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexStride, (void *)SourceVertexOffsetOfPhaseAmplitudeAndOffset);
|
||||
glVertexAttribPointer((GLuint)phaseTimeAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfPhaseTime);
|
||||
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)phaseAndAmplitudeAttribute, 2, GL_UNSIGNED_BYTE, GL_TRUE, vertexStride, (void *)SourceVertexOffsetOfPhaseAndAmplitude);
|
||||
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 textureCoordinatesAttribute = output_shader_program->get_attrib_location("srcCoordinates");
|
||||
GLint lateralAttribute = output_shader_program->get_attrib_location("lateral");
|
||||
|
||||
glBindVertexArray(output_vertex_array);
|
||||
|
||||
glEnableVertexAttribArray((GLuint)positionAttribute);
|
||||
glEnableVertexAttribArray((GLuint)textureCoordinatesAttribute);
|
||||
glEnableVertexAttribArray((GLuint)lateralAttribute);
|
||||
|
||||
const GLsizei vertexStride = OutputVertexSize;
|
||||
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
|
||||
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)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)
|
||||
{
|
||||
_output_mutex->lock();
|
||||
_input_frequency = input_frequency;
|
||||
_cycles_per_line = cycles_per_line;
|
||||
_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;
|
||||
|
||||
set_timing_uniforms();
|
||||
_output_mutex->unlock();
|
||||
}
|
||||
|
||||
#pragma mark - Internal Configuration
|
||||
|
||||
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 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_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||
_output_mutex->unlock();
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::set_timing_uniforms()
|
||||
{
|
||||
_output_mutex->lock();
|
||||
|
||||
OpenGL::IntermediateShader *intermediate_shaders[] = {
|
||||
composite_input_shader_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_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);
|
||||
|
||||
_output_mutex->unlock();
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ class OpenGLOutputBuilder {
|
||||
|
||||
// the run and input data buffers
|
||||
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
|
||||
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)
|
||||
{
|
||||
_output_mutex->lock();
|
||||
_colour_space = colour_space;
|
||||
_colour_cycle_numerator = colour_cycle_numerator;
|
||||
_colour_cycle_denominator = colour_cycle_denominator;
|
||||
set_colour_space_uniforms();
|
||||
_output_mutex->unlock();
|
||||
}
|
||||
|
||||
inline void set_visible_area(Rect visible_area)
|
||||
@ -104,8 +106,9 @@ class OpenGLOutputBuilder {
|
||||
|
||||
inline uint8_t *get_next_source_run()
|
||||
{
|
||||
if(_source_buffer_data_pointer == _drawn_source_buffer_data_pointer + SourceVertexBufferDataSize) return nullptr;
|
||||
_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()
|
||||
@ -114,10 +117,16 @@ class OpenGLOutputBuilder {
|
||||
_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()
|
||||
{
|
||||
if(_output_buffer_data_pointer == _drawn_output_buffer_data_pointer + OutputVertexBufferDataSize) return nullptr;
|
||||
_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)
|
||||
@ -131,38 +140,46 @@ class OpenGLOutputBuilder {
|
||||
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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
_composite_src_output_y++;
|
||||
if(!composite_output_buffer_is_full())
|
||||
_composite_src_output_y++;
|
||||
}
|
||||
|
||||
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;
|
||||
return _buffer_builder->get_write_target();
|
||||
}
|
||||
|
||||
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);
|
||||
@ -172,18 +189,15 @@ class OpenGLOutputBuilder {
|
||||
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);
|
||||
|
||||
uint8_t *_input_texture_data;
|
||||
GLuint _input_texture_array;
|
||||
GLsync _input_texture_sync;
|
||||
GLsizeiptr _input_texture_array_size;
|
||||
|
||||
uint8_t *_source_buffer_data;
|
||||
std::unique_ptr<uint8_t> _source_buffer_data;
|
||||
GLsizei _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 _drawn_output_buffer_data_pointer;
|
||||
|
||||
GLsync _fence;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace {
|
||||
{
|
||||
{"inputPosition", 0},
|
||||
{"outputPosition", 1},
|
||||
{"phaseAmplitudeAndOffset", 2},
|
||||
{"phaseAndAmplitude", 2},
|
||||
{"phaseTime", 3},
|
||||
{nullptr}
|
||||
};
|
||||
@ -37,7 +37,7 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
|
||||
|
||||
"in vec2 inputPosition;"
|
||||
"in vec2 outputPosition;"
|
||||
"in vec3 phaseAmplitudeAndOffset;"
|
||||
"in vec2 phaseAndAmplitude;"
|
||||
"in float phaseTime;"
|
||||
|
||||
"uniform float phaseCyclesPerTick;"
|
||||
@ -53,7 +53,8 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
|
||||
|
||||
"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 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);"
|
||||
"delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);"
|
||||
|
||||
"phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAmplitudeAndOffset.x) * 2.0 * 3.141592654;"
|
||||
"phaseAndAmplitudeVarying.y = 0.33;"
|
||||
"phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAndAmplitude.x) * 2.0 * 3.141592654;"
|
||||
"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);"
|
||||
"}", sampler_type, input_variable);
|
||||
|
||||
|
@ -18,7 +18,6 @@ namespace {
|
||||
{
|
||||
{"position", 0},
|
||||
{"srcCoordinates", 1},
|
||||
{"lateral", 2},
|
||||
{nullptr}
|
||||
};
|
||||
}
|
||||
@ -33,7 +32,6 @@ std::unique_ptr<OutputShader> OutputShader::make_shader(const char *fragment_met
|
||||
|
||||
"in vec2 position;"
|
||||
"in vec2 srcCoordinates;"
|
||||
"in float lateral;"
|
||||
|
||||
"uniform vec2 boundsOrigin;"
|
||||
"uniform vec2 boundsSize;"
|
||||
@ -47,6 +45,8 @@ std::unique_ptr<OutputShader> OutputShader::make_shader(const char *fragment_met
|
||||
|
||||
"void main(void)"
|
||||
"{"
|
||||
"float laterals[] = float[](0, 0, 1, 0, 1, 1);"
|
||||
"float lateral = laterals[gl_VertexID %% 6];"
|
||||
"lateralVarying = lateral - 0.5;"
|
||||
|
||||
"ivec2 textureSize = textureSize(texID, 0);"
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "TextureTarget.hpp"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace OpenGL;
|
||||
|
||||
@ -29,7 +30,9 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit)
|
||||
glGenTextures(1, &_texture);
|
||||
glActiveTexture(texture_unit);
|
||||
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_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user