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