1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-09 00:37:27 +00:00

Simplified API down to their being a single texture with a specified depth.

This commit is contained in:
Thomas Harte 2016-03-19 17:37:55 -04:00
parent 4ac1f959e9
commit aa8a192c7e
8 changed files with 61 additions and 119 deletions

View File

@ -24,7 +24,7 @@ Machine::Machine() :
_piaDataValue{0xff, 0xff},
_tiaInputValue{0xff, 0xff}
{
_crt = new Outputs::CRT::CRT(228, 1, Outputs::CRT::DisplayType::NTSC60, 1, 2);
_crt = new Outputs::CRT::CRT(228, 1, Outputs::CRT::DisplayType::NTSC60, 2);
_crt->set_composite_sampling_function(
"float composite_sample(vec2 coordinate, float phase)\n"
"{\n"
@ -226,8 +226,7 @@ void Machine::output_pixels(unsigned int count)
if(state == OutputState::Pixel)
{
_crt->allocate_write_area(160);
_outputBuffer = _crt->get_write_target_for_buffer(0);
_outputBuffer = _crt->allocate_write_area(160);
} else {
_outputBuffer = nullptr;
}

View File

@ -45,7 +45,7 @@ Machine::Machine() :
_audioOutputPositionError(0),
_current_pixel_line(-1),
_use_fast_tape_hack(false),
_crt(std::unique_ptr<Outputs::CRT::CRT>(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::CRT::DisplayType::PAL50, 1, 1)))
_crt(std::unique_ptr<Outputs::CRT::CRT>(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::CRT::DisplayType::PAL50, 1)))
{
_crt->set_rgb_sampling_function(
"vec4 rgb_sample(vec2 coordinate)"
@ -502,8 +502,7 @@ inline void Machine::start_pixel_line()
if(!_isBlankLine)
{
_crt->allocate_write_area(640);
_currentLine = _crt->get_write_target_for_buffer(0);
_currentLine = _crt->allocate_write_area(640);
}
}

View File

@ -68,22 +68,15 @@ CRT::CRT(unsigned int common_output_divisor) :
_common_output_divisor(common_output_divisor),
_is_writing_composite_run(false) {}
CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int number_of_buffers, ...) : CRT(common_output_divisor)
CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int buffer_depth) : CRT(common_output_divisor)
{
va_list buffer_sizes;
va_start(buffer_sizes, number_of_buffers);
_openGL_output_builder = std::unique_ptr<OpenGLOutputBuilder>(new OpenGLOutputBuilder(number_of_buffers, buffer_sizes));
va_end(buffer_sizes);
_openGL_output_builder = std::unique_ptr<OpenGLOutputBuilder>(new OpenGLOutputBuilder(buffer_depth));
set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator);
}
CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int number_of_buffers, ...) : CRT(common_output_divisor)
CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int buffer_depth) : CRT(common_output_divisor)
{
va_list buffer_sizes;
va_start(buffer_sizes, number_of_buffers);
_openGL_output_builder = std::unique_ptr<OpenGLOutputBuilder>(new OpenGLOutputBuilder(number_of_buffers, buffer_sizes));
va_end(buffer_sizes);
_openGL_output_builder = std::unique_ptr<OpenGLOutputBuilder>(new OpenGLOutputBuilder(buffer_depth));
set_new_display_type(cycles_per_line, displayType);
}

View File

@ -95,19 +95,15 @@ class CRT {
@param colour_cycle_denominator Specifies the denominator for the per-line frequency of the colour subcarrier.
The colour subcarrier is taken to have colour_cycle_numerator/colour_cycle_denominator cycles per line.
@param number_of_buffers The number of source data buffers to create for this machine. Machines
may provide per-clock-cycle data in any form that they consider convenient, supplying a sampling
@param buffer_depth The depth per pixel of source data buffers to create for this machine. Machines
may provide per-clock-cycle data in the depth that they consider convenient, supplying a sampling
function to convert between their data format and either a composite or RGB signal, allowing that
work to be offloaded onto the GPU and allowing the output signal to be sampled at a rate appropriate
to the display size.
@param ... A list of sizes for source data buffers, provided as the number of bytes per sample.
For compatibility with OpenGL ES, samples should be 14 bytes in size. If a machine requires more
than 4 bytes/sample then it should use multiple buffers.
@see @c set_rgb_sampling_function , @c set_composite_sampling_function
*/
CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int buffer_depth, ...);
CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int buffer_depth);
/*! Constructs the CRT with the specified clock rate, with the display height and colour
subcarrier frequency dictated by a standard display type and with the requested number of
@ -116,7 +112,7 @@ class CRT {
Exactly identical to calling the designated constructor with colour subcarrier information
looked up by display type.
*/
CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int number_of_buffers, ...);
CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int buffer_depth);
/*! Resets the CRT with new timing information. The CRT then continues as though the new timing had
been provided at construction. */
@ -171,30 +167,18 @@ class CRT {
/*! Ensures that the given number of output samples are allocated for writing.
Following this call, the caller should call @c get_write_target_for_buffer for each
buffer they requested to get the location of the allocated memory.
The beginning of the most recently allocated area is used as the start
of data written by a call to @c output_data; it is acceptable to write and to
output less data than the amount requested but that may be less efficient.
@param required_length The number of samples to allocate.
@returns A pointer to the allocated area.
*/
inline void allocate_write_area(size_t required_length)
inline uint8_t *allocate_write_area(size_t required_length)
{
return _openGL_output_builder->allocate_write_area(required_length);
}
/*! Gets a pointer for writing to the area created by the most recent call to @c allocate_write_area
for the nominated buffer.
@param buffer The buffer to get a write target for.
*/
inline uint8_t *get_write_target_for_buffer(int buffer)
{
return _openGL_output_builder->get_write_target_for_buffer(buffer);
}
/*! Causes appropriate OpenGL or OpenGL ES calls to be issued in order to draw the current CRT state.
The caller is responsible for ensuring that a valid OpenGL context exists for the duration of this call.
*/

View File

@ -12,29 +12,12 @@
using namespace Outputs::CRT;
CRTInputBufferBuilder::CRTInputBufferBuilder(unsigned int number_of_buffers, va_list buffer_sizes)
CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : bytes_per_pixel(bytes_per_pixel)
{
this->number_of_buffers = number_of_buffers;
buffers = new CRTInputBufferBuilder::Buffer[number_of_buffers];
for(int buffer = 0; buffer < number_of_buffers; buffer++)
{
buffers[buffer].bytes_per_pixel = va_arg(buffer_sizes, unsigned int);
buffers[buffer].data = new uint8_t[InputBufferBuilderWidth * InputBufferBuilderHeight * buffers[buffer].bytes_per_pixel];
}
_next_write_x_position = _next_write_y_position = 0;
last_uploaded_line = 0;
}
CRTInputBufferBuilder::~CRTInputBufferBuilder()
{
for(int buffer = 0; buffer < number_of_buffers; buffer++)
delete[] buffers[buffer].data;
delete buffers;
}
void CRTInputBufferBuilder::allocate_write_area(size_t required_length)
{
_last_allocation_amount = required_length;
@ -50,26 +33,18 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length)
_next_write_x_position += required_length + 2;
}
void CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length)
void CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer)
{
// book end the allocation with duplicates of the first and last pixel, to protect
// against rounding errors when this run is drawn
for(int c = 0; c < number_of_buffers; c++)
{
memcpy( &buffers[c].data[(_write_target_pointer - 1) * buffers[c].bytes_per_pixel],
&buffers[c].data[_write_target_pointer * buffers[c].bytes_per_pixel],
buffers[c].bytes_per_pixel);
memcpy( &buffer[(_write_target_pointer - 1) * bytes_per_pixel],
&buffer[_write_target_pointer * bytes_per_pixel],
bytes_per_pixel);
memcpy( &buffers[c].data[(_write_target_pointer + actual_length) * buffers[c].bytes_per_pixel],
&buffers[c].data[(_write_target_pointer + actual_length - 1) * buffers[c].bytes_per_pixel],
buffers[c].bytes_per_pixel);
}
memcpy( &buffer[(_write_target_pointer + actual_length) * bytes_per_pixel],
&buffer[(_write_target_pointer + actual_length - 1) * bytes_per_pixel],
bytes_per_pixel);
// return any allocated length that wasn't actually used to the available pool
_next_write_x_position -= (_last_allocation_amount - actual_length);
}
uint8_t *CRTInputBufferBuilder::get_write_target_for_buffer(int buffer)
{
return &buffers[buffer].data[_write_target_pointer * buffers[buffer].bytes_per_pixel];
}

View File

@ -18,12 +18,10 @@ namespace Outputs {
namespace CRT {
struct CRTInputBufferBuilder {
CRTInputBufferBuilder(unsigned int number_of_buffers, va_list buffer_sizes);
~CRTInputBufferBuilder();
CRTInputBufferBuilder(size_t bytes_per_pixel);
void allocate_write_area(size_t required_length);
void reduce_previous_allocation_to(size_t actual_length);
uint8_t *get_write_target_for_buffer(int buffer);
void reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer);
// a pointer to the section of content buffer currently being
// returned and to where the next section will begin
@ -31,12 +29,7 @@ struct CRTInputBufferBuilder {
uint16_t _write_x_position, _write_y_position;
size_t _write_target_pointer;
size_t _last_allocation_amount;
struct Buffer {
uint8_t *data;
size_t bytes_per_pixel;
} *buffers;
unsigned int number_of_buffers;
size_t bytes_per_pixel;
// Storage for the amount of buffer uploaded so far; initialised correctly by the buffer
// builder but otherwise entrusted to the CRT to update.
@ -47,6 +40,11 @@ struct CRTInputBufferBuilder {
_next_write_x_position = 0;
_next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight;
}
inline uint8_t *get_write_target(uint8_t *buffer)
{
return &buffer[_write_target_pointer * bytes_per_pixel];
}
};
}

View File

@ -17,7 +17,7 @@ namespace {
static const GLenum first_supplied_buffer_texture_unit = 3;
}
OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int number_of_buffers, va_list sizes) :
OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
_run_write_pointer(0),
_output_mutex(new std::mutex),
_visible_area(Rect(0, 0, 1, 1)),
@ -35,10 +35,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int number_of_buffers, va_list
}
// _composite_src_runs = std::unique_ptr<CRTRunBuilder>(new CRTRunBuilder(InputVertexSize));
va_list va;
va_copy(va, sizes);
_buffer_builder = std::unique_ptr<CRTInputBufferBuilder>(new CRTInputBufferBuilder(number_of_buffers, sizes));
va_end(va);
_buffer_builder = std::unique_ptr<CRTInputBufferBuilder>(new CRTInputBufferBuilder(buffer_depth));
}
OpenGLOutputBuilder::~OpenGLOutputBuilder()
@ -48,9 +45,13 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder()
delete _run_builders[builder];
}
delete[] _run_builders;
// delete[] _input_texture_data;
glUnmapBuffer(GL_ARRAY_BUFFER);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glDeleteTextures(1, &textureName);
glDeleteBuffers(1, &_input_texture_array);
glDeleteBuffers(1, &output_array_buffer);
glDeleteVertexArrays(1, &output_vertex_array);
free(_composite_shader);
free(_rgb_shader);
@ -73,24 +74,21 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
// establish essentials
if(!composite_input_shader_program && !rgb_shader_program)
{
// generate and bind textures for every one of the requested buffers
for(unsigned int buffer = 0; buffer < _buffer_builder->number_of_buffers; buffer++)
{
glGenTextures(1, &textureName);
glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit + buffer);
glBindTexture(GL_TEXTURE_2D, textureName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// generate and bind texture for input data
glGenTextures(1, &textureName);
glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit);
glBindTexture(GL_TEXTURE_2D, textureName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
GLenum format = formatForDepth(_buffer_builder->buffers[buffer].bytes_per_pixel);
glGenBuffers(1, &_input_texture_array);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array);
glBufferData(GL_PIXEL_UNPACK_BUFFER, InputTextureBufferDataSize, NULL, GL_STREAM_DRAW);
GLenum format = formatForDepth(_buffer_builder->bytes_per_pixel);
glGenBuffers(1, &_input_texture_array);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array);
glBufferData(GL_PIXEL_UNPACK_BUFFER, InputTextureBufferDataSize, NULL, GL_STREAM_DRAW);
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, InputBufferBuilderWidth, InputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, nullptr);
}
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, InputBufferBuilderWidth, InputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, nullptr);
prepare_composite_input_shader();
prepare_rgb_output_shader();
@ -134,17 +132,17 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
// upload more source pixel data if any; we'll always resubmit the last line submitted last
// time as it may have had extra data appended to it
for(unsigned int buffer = 0; buffer < _buffer_builder->number_of_buffers; buffer++)
{
// for(unsigned int buffer = 0; buffer < _buffer_builder->number_of_buffers; buffer++)
// {
// glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit + buffer);
GLenum format = formatForDepth(_buffer_builder->buffers[0].bytes_per_pixel);
GLenum format = formatForDepth(_buffer_builder->bytes_per_pixel);
if(_buffer_builder->_next_write_y_position < _buffer_builder->last_uploaded_line)
{
glTexSubImage2D(GL_TEXTURE_2D, 0,
0, (GLint)_buffer_builder->last_uploaded_line,
InputBufferBuilderWidth, (GLint)(InputBufferBuilderHeight - _buffer_builder->last_uploaded_line),
format, GL_UNSIGNED_BYTE,
(void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel));
(void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel));
_buffer_builder->last_uploaded_line = 0;
}
@ -154,10 +152,10 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
0, (GLint)_buffer_builder->last_uploaded_line,
InputBufferBuilderWidth, (GLint)(1 + _buffer_builder->_next_write_y_position - _buffer_builder->last_uploaded_line),
format, GL_UNSIGNED_BYTE,
(void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel));
(void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel));
_buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position;
}
}
// }
// check for anything to decode from composite
// if(_composite_src_runs->number_of_vertices)

View File

@ -89,7 +89,7 @@ class OpenGLOutputBuilder {
std::unique_ptr<OpenGL::TextureTarget> filteredTexture; // receives filtered YIQ or YUV
public:
OpenGLOutputBuilder(unsigned int number_of_buffers, va_list sizes);
OpenGLOutputBuilder(unsigned int buffer_depth);
~OpenGLOutputBuilder();
inline void set_colour_format(ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator)
@ -164,22 +164,18 @@ class OpenGLOutputBuilder {
_output_mutex->unlock();
}
inline void allocate_write_area(size_t required_length)
inline uint8_t *allocate_write_area(size_t required_length)
{
_output_mutex->lock();
_buffer_builder->allocate_write_area(required_length);
uint8_t *output = _input_texture_data ? _buffer_builder->get_write_target(_input_texture_data) : nullptr;
_output_mutex->unlock();
return output;
}
inline void reduce_previous_allocation_to(size_t actual_length)
{
_buffer_builder->reduce_previous_allocation_to(actual_length);
}
inline uint8_t *get_write_target_for_buffer(int buffer)
{
return &_input_texture_data[_buffer_builder->_write_target_pointer]; // * _buffer_builder->bytes_per_pixel
// return _buffer_builder->get_write_target_for_buffer(buffer);
_buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data);
}
inline uint16_t get_last_write_x_posiiton()