1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-07 08:28:57 +00:00

Started the transition towards a more natural collection of rolling buffers, with phosphor decay in mind.

This commit is contained in:
Thomas Harte 2016-02-13 20:52:23 -05:00
parent f57d6d350b
commit eeb0e134fd
8 changed files with 251 additions and 300 deletions

View File

@ -31,7 +31,7 @@
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; };
4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
4B7BFEFE1C6446EF00089C1C /* CRTFrameBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BFEFD1C6446EF00089C1C /* CRTFrameBuilder.cpp */; };
4B7BFEFE1C6446EF00089C1C /* CRTBuilders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BFEFD1C6446EF00089C1C /* CRTBuilders.cpp */; };
4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* TimingTests.swift */; };
4BB298EE1B587D8400A49093 /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E01B587D8300A49093 /* 6502_functional_test.bin */; };
4BB298EF1B587D8400A49093 /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E11B587D8300A49093 /* AllSuiteA.bin */; };
@ -331,7 +331,6 @@
/* Begin PBXFileReference section */
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRT.cpp; sourceTree = "<group>"; };
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = "<group>"; };
4B0CCC441C62D0B3001CAC5F /* CRTFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CRTFrame.h; sourceTree = "<group>"; };
4B0CCC461C62D1A8001CAC5F /* CRTOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTOpenGL.cpp; sourceTree = "<group>"; };
4B0EBFB61C487F2F00A11F35 /* AudioQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioQueue.h; sourceTree = "<group>"; };
4B0EBFB71C487F2F00A11F35 /* AudioQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioQueue.m; sourceTree = "<group>"; };
@ -373,7 +372,7 @@
4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapeUEF.cpp; sourceTree = "<group>"; };
4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TapeUEF.hpp; sourceTree = "<group>"; };
4B69FB451C4D950F00B5F0AA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
4B7BFEFD1C6446EF00089C1C /* CRTFrameBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTFrameBuilder.cpp; sourceTree = "<group>"; };
4B7BFEFD1C6446EF00089C1C /* CRTBuilders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTBuilders.cpp; sourceTree = "<group>"; };
4B92EAC91B7C112B00246143 /* TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimingTests.swift; sourceTree = "<group>"; };
4BAE587D1C447B7A005B9AF0 /* KeyCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyCodes.h; sourceTree = "<group>"; };
4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Clock SignalTests-Bridging-Header.h"; sourceTree = "<group>"; };
@ -659,6 +658,7 @@
4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clock_SignalUITests.swift; sourceTree = "<group>"; };
4BB73EC31B587A5100552FC2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4BB73ECF1B587A6700552FC2 /* Clock Signal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Clock Signal.entitlements"; sourceTree = "<group>"; };
4BCC142A1C6FFD6F0033C621 /* CRTOpenGL.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CRTOpenGL.hpp; sourceTree = "<group>"; };
4BE5F85C1C3E1C2500C43F01 /* basic.rom */ = {isa = PBXFileReference; lastKnownFileType = file; path = basic.rom; sourceTree = "<group>"; };
4BE5F85D1C3E1C2500C43F01 /* os.rom */ = {isa = PBXFileReference; lastKnownFileType = file; path = os.rom; sourceTree = "<group>"; };
4BEDD0F41C6D70B800F6257C /* Flywheel.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Flywheel.hpp; sourceTree = "<group>"; };
@ -695,15 +695,15 @@
children = (
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */,
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */,
4B0CCC441C62D0B3001CAC5F /* CRTFrame.h */,
4B0CCC461C62D1A8001CAC5F /* CRTOpenGL.cpp */,
4B7BFEFD1C6446EF00089C1C /* CRTFrameBuilder.cpp */,
4B7BFEFD1C6446EF00089C1C /* CRTBuilders.cpp */,
4B2039921C67E20B001375C3 /* TextureTarget.cpp */,
4B2039931C67E20B001375C3 /* TextureTarget.hpp */,
4B2039951C67E2A3001375C3 /* OpenGL.hpp */,
4B2039971C67FA92001375C3 /* Shader.cpp */,
4B2039981C67FA92001375C3 /* Shader.hpp */,
4BEDD0F41C6D70B800F6257C /* Flywheel.hpp */,
4BCC142A1C6FFD6F0033C621 /* CRTOpenGL.hpp */,
);
name = CRT;
path = ../../Outputs/CRT;
@ -1624,7 +1624,7 @@
4B0CCC471C62D1A8001CAC5F /* CRTOpenGL.cpp in Sources */,
4B2039941C67E20B001375C3 /* TextureTarget.cpp in Sources */,
4B0EBFB81C487F2F00A11F35 /* AudioQueue.m in Sources */,
4B7BFEFE1C6446EF00089C1C /* CRTFrameBuilder.cpp in Sources */,
4B7BFEFE1C6446EF00089C1C /* CRTBuilders.cpp in Sources */,
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */,
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */,

View File

@ -7,6 +7,7 @@
//
#include "CRT.hpp"
#include "CRTOpenGL.hpp"
#include <stdarg.h>
#include <math.h>
@ -15,13 +16,6 @@ using namespace Outputs;
static const uint32_t kCRTFixedPointRange = 0xf7ffffff;
static const uint32_t kCRTFixedPointOffset = 0x04000000;
//static const size_t kCRTVertexOffsetOfPosition = 0;
//static const size_t kCRTVertexOffsetOfTexCoord = 4;
//static const size_t kCRTVertexOffsetOfLateral = 8;
//static const size_t kCRTVertexOffsetOfPhase = 9;
//
//static const int kCRTSizeOfVertex = 10;
#define kRetraceXMask 0x01
#define kRetraceYMask 0x02
@ -41,7 +35,7 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di
// a TV picture tube or camera tube to the starting point of a line or field. It is about 7 µs
// for horizontal retrace and 500 to 750 µs for vertical retrace in NTSC and PAL TV."
_time_multiplier = (1000 + cycles_per_line - 1) / cycles_per_line;
_time_multiplier = (2000 + cycles_per_line - 1) / cycles_per_line;
// store fundamental display configuration properties
_height_of_display = height_of_display;
@ -85,32 +79,38 @@ void CRT::set_new_display_type(unsigned int cycles_per_line, DisplayType display
void CRT::allocate_buffers(unsigned int number, va_list sizes)
{
// generate buffers for signal storage as requested — format is
// number of buffers, size of buffer 1, size of buffer 2...
const uint16_t bufferWidth = 2048;
const uint16_t bufferHeight = 2048;
for(int frame = 0; frame < sizeof(_frame_builders) / sizeof(*_frame_builders); frame++)
for(int builder = 0; builder < sizeof(_run_builders) / sizeof(*_run_builders); builder++)
{
va_list va;
va_copy(va, sizes);
_frame_builders[frame] = new CRTFrameBuilder(bufferWidth, bufferHeight, number, va);
va_end(va);
_run_builders[builder] = new CRTRunBuilder();
}
_current_frame_builder = _frame_builders[0];
va_list va;
va_copy(va, sizes);
_buffer_builder = std::unique_ptr<CRTInputBufferBuilder>(new CRTInputBufferBuilder(number, va));
va_end(va);
}
CRT::CRT() :
_next_scan(0),
_frame_read_pointer(0),
_run_write_pointer(0),
_sync_capacitor_charge_level(0),
_is_receiving_sync(false),
_current_frame_mutex(new std::mutex),
_output_mutex(new std::mutex),
_visible_area(Rect(0, 0, 1, 1)),
_rasterPosition({.x = 0, .y = 0})
{
construct_openGL();
}
CRT::~CRT()
{
for(int builder = 0; builder < sizeof(_run_builders) / sizeof(*_run_builders); builder++)
{
delete _run_builders[builder];
}
destruct_openGL();
}
CRT::CRT(unsigned int cycles_per_line, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int number_of_buffers, ...) : CRT()
{
set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator);
@ -131,15 +131,6 @@ CRT::CRT(unsigned int cycles_per_line, DisplayType displayType, unsigned int num
va_end(buffer_sizes);
}
CRT::~CRT()
{
for(int frame = 0; frame < sizeof(_frame_builders) / sizeof(*_frame_builders); frame++)
{
delete _frame_builders[frame];
}
destruct_openGL();
}
#pragma mark - Sync loop
Flywheel::SyncEvent CRT::get_next_vertical_sync_event(bool vsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced)
@ -172,7 +163,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
hsync_requested = false;
vsync_requested = false;
uint8_t *next_run = (is_output_run && _current_frame_builder && next_run_length) ? _current_frame_builder->get_next_run() : nullptr;
uint8_t *next_run = (is_output_run && next_run_length) ? _run_builders[_run_write_pointer]->get_next_input_run() : nullptr;
int lengthMask = (_horizontal_flywheel->is_in_retrace() ? kRetraceXMask : 0) | (_vertical_flywheel->is_in_retrace() ? kRetraceYMask : 0);
#define position_x(v) (*(uint16_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfPosition + 0])
@ -237,25 +228,11 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event == Flywheel::SyncEvent::EndRetrace)
{
if(_current_frame_builder)
{
_current_frame_builder->complete();
_current_frame_mutex->lock();
_current_frame = &_current_frame_builder->frame;
_current_frame_mutex->unlock();
// TODO: how to communicate did_detect_vsync? Bring the delegate back?
// _delegate->crt_did_end_frame(this, &_current_frame_builder->frame, _did_detect_vsync);
}
// if(_frames_with_delegate < kCRTNumberOfFrames)
// {
_frame_read_pointer = (_frame_read_pointer + 1)%kCRTNumberOfFrames;
_current_frame_builder = _frame_builders[_frame_read_pointer];
_current_frame_builder->reset();
// }
// else
// _current_frame_builder = nullptr;
// TODO: how to communicate did_detect_vsync? Bring the delegate back?
// _delegate->crt_did_end_frame(this, &_current_frame_builder->frame, _did_detect_vsync);
_run_write_pointer = (_run_write_pointer + 1)%kCRTNumberOfFrames;
_run_builders[_run_write_pointer]->reset();
}
}
}
@ -280,57 +257,69 @@ void CRT::output_scan()
*/
void CRT::output_sync(unsigned int number_of_cycles)
{
_output_mutex->lock();
_scans[_next_scan].type = Type::Sync;
_scans[_next_scan].number_of_cycles = number_of_cycles;
output_scan();
_output_mutex->unlock();
}
void CRT::output_blank(unsigned int number_of_cycles)
{
_output_mutex->lock();
_scans[_next_scan].type = Type::Blank;
_scans[_next_scan].number_of_cycles = number_of_cycles;
output_scan();
_output_mutex->unlock();
}
void CRT::output_level(unsigned int number_of_cycles)
{
_output_mutex->lock();
_scans[_next_scan].type = Type::Level;
_scans[_next_scan].number_of_cycles = number_of_cycles;
_scans[_next_scan].tex_x = _current_frame_builder ? _current_frame_builder->_write_x_position : 0;
_scans[_next_scan].tex_y = _current_frame_builder ? _current_frame_builder->_write_y_position : 0;
_scans[_next_scan].tex_x = _buffer_builder->_write_x_position;
_scans[_next_scan].tex_y = _buffer_builder->_write_y_position;
output_scan();
_output_mutex->unlock();
}
void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t magnitude)
{
_output_mutex->lock();
_scans[_next_scan].type = Type::ColourBurst;
_scans[_next_scan].number_of_cycles = number_of_cycles;
_scans[_next_scan].phase = phase;
_scans[_next_scan].magnitude = magnitude;
output_scan();
_output_mutex->unlock();
}
void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider)
{
if(_current_frame_builder) _current_frame_builder->reduce_previous_allocation_to(number_of_cycles / source_divider);
_output_mutex->lock();
_buffer_builder->reduce_previous_allocation_to(number_of_cycles / source_divider);
_scans[_next_scan].type = Type::Data;
_scans[_next_scan].number_of_cycles = number_of_cycles;
_scans[_next_scan].tex_x = _current_frame_builder ? _current_frame_builder->_write_x_position : 0;
_scans[_next_scan].tex_y = _current_frame_builder ? _current_frame_builder->_write_y_position : 0;
_scans[_next_scan].tex_x = _buffer_builder->_write_x_position;
_scans[_next_scan].tex_y = _buffer_builder->_write_y_position;
_scans[_next_scan].source_divider = source_divider;
output_scan();
_output_mutex->unlock();
}
#pragma mark - Buffer supply
void CRT::allocate_write_area(size_t required_length)
{
if(_current_frame_builder) _current_frame_builder->allocate_write_area(required_length);
_output_mutex->lock();
_buffer_builder->allocate_write_area(required_length);
_output_mutex->unlock();
}
uint8_t *CRT::get_write_target_for_buffer(int buffer)
{
if (!_current_frame_builder) return nullptr;
return _current_frame_builder->get_write_target_for_buffer(buffer);
return _buffer_builder->get_write_target_for_buffer(buffer);
}

View File

@ -15,7 +15,6 @@
#include <vector>
#include <mutex>
#include "CRTFrame.h"
#include "Flywheel.hpp"
namespace Outputs {
@ -233,6 +232,9 @@ class CRT {
unsigned int _colour_cycle_denominator;
OutputDevice _output_device;
// The user-supplied visible area
Rect _visible_area;
// the current scanning position (TODO: can I eliminate this in favour of just using the flywheels?)
struct Vector {
uint32_t x, y;
@ -274,19 +276,32 @@ class CRT {
int _next_scan;
void output_scan();
struct CRTFrameBuilder {
CRTFrame frame;
CRTFrameBuilder(uint16_t width, uint16_t height, unsigned int number_of_buffers, va_list buffer_sizes);
~CRTFrameBuilder();
std::vector<uint8_t> _all_runs;
struct CRTRunBuilder {
// Resets the run builder.
void reset();
void complete();
uint8_t *get_next_run();
// Getter for new storage plus backing storage; in RGB mode input runs will map directly
// from the input buffer to the screen. In composite mode input runs will map from the
// input buffer to the processing buffer, and output runs will map from the processing
// buffer to the screen.
uint8_t *get_next_input_run();
std::vector<uint8_t> _input_runs;
uint8_t *get_next_output_run();
std::vector<uint8_t> _output_runs;
// Container for total length in cycles of all contained runs.
uint32_t duration;
// Storage for the length of run data uploaded so far; reset to zero by reset but otherwise
// entrusted to the CRT to update.
size_t uploaded_run_data;
size_t number_of_vertices;
};
struct CRTInputBufferBuilder {
CRTInputBufferBuilder(unsigned int number_of_buffers, va_list buffer_sizes);
~CRTInputBufferBuilder();
void allocate_write_area(size_t required_length);
void reduce_previous_allocation_to(size_t actual_length);
@ -298,26 +313,38 @@ class CRT {
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;
// 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;
};
// the run and input data buffers
static const int kCRTNumberOfFrames = 4;
std::unique_ptr<CRTInputBufferBuilder> _buffer_builder;
CRTRunBuilder *_run_builders[kCRTNumberOfFrames];
int _run_write_pointer;
std::shared_ptr<std::mutex> _output_mutex;
// the triple buffer and OpenGL state
CRTFrameBuilder *_frame_builders[kCRTNumberOfFrames];
CRTFrameBuilder *_current_frame_builder;
CRTFrame *_current_frame, *_last_drawn_frame;
std::shared_ptr<std::mutex> _current_frame_mutex;
int _frame_read_pointer;
Rect _visible_area;
// OpenGL state, kept behind an opaque pointer to avoid inclusion of the GL headers here.
struct OpenGLState;
OpenGLState *_openGL_state;
// Other things the caller may have provided.
char *_composite_shader;
char *_rgb_shader;
// Setup and teardown for the OpenGL code
void construct_openGL();
void destruct_openGL();
// Methods used by the OpenGL code
void prepare_shader();
void push_size_uniforms(unsigned int output_width, unsigned int output_height);

101
Outputs/CRT/CRTBuilders.cpp Normal file
View File

@ -0,0 +1,101 @@
//
// CRTFrameBuilder.cpp
// Clock Signal
//
// Created by Thomas Harte on 04/02/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "CRT.hpp"
#include "CRTOpenGL.hpp"
using namespace Outputs;
/*
CRTInputBufferBuilder
*/
CRT::CRTInputBufferBuilder::CRTInputBufferBuilder(unsigned int number_of_buffers, va_list buffer_sizes)
{
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[CRTInputBufferBuilderWidth * CRTInputBufferBuilderHeight * buffers[buffer].bytes_per_pixel];
}
_next_write_x_position = _next_write_y_position = 0;
last_uploaded_line = 0;
}
CRT::CRTInputBufferBuilder::~CRTInputBufferBuilder()
{
for(int buffer = 0; buffer < number_of_buffers; buffer++)
delete[] buffers[buffer].data;
delete buffers;
}
void CRT::CRTInputBufferBuilder::allocate_write_area(size_t required_length)
{
_last_allocation_amount = required_length;
if(_next_write_x_position + required_length + 2 > CRTInputBufferBuilderWidth)
{
_next_write_x_position = 0;
_next_write_y_position = (_next_write_y_position+1)%CRTInputBufferBuilderWidth;
}
_write_x_position = _next_write_x_position + 1;
_write_y_position = _next_write_y_position;
_write_target_pointer = (_write_y_position * CRTInputBufferBuilderWidth) + _write_x_position;
_next_write_x_position += required_length + 2;
}
void CRT::CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length)
{
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( &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);
}
_next_write_x_position -= (_last_allocation_amount - actual_length);
}
uint8_t *CRT::CRTInputBufferBuilder::get_write_target_for_buffer(int buffer)
{
return &buffers[buffer].data[_write_target_pointer * buffers[buffer].bytes_per_pixel];
}
/*
CRTRunBuilder
*/
void CRT::CRTRunBuilder::reset()
{
number_of_vertices = 0;
}
uint8_t *CRT::CRTRunBuilder::get_next_input_run()
{
const size_t vertices_per_run = 6;
// get a run from the allocated list, allocating more if we're about to overrun
if((number_of_vertices + vertices_per_run) * kCRTSizeOfVertex >= _input_runs.size())
{
_input_runs.resize(_input_runs.size() + kCRTSizeOfVertex * vertices_per_run * 100);
}
uint8_t *next_run = &_input_runs[number_of_vertices * kCRTSizeOfVertex];
number_of_vertices += vertices_per_run;
return next_run;
}

View File

@ -1,68 +0,0 @@
//
// CRTFrame.h
// Clock Signal
//
// Created by Thomas Harte on 24/07/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
#ifndef CRTFrame_h
#define CRTFrame_h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint8_t *data;
unsigned int depth;
} CRTBuffer;
typedef struct {
uint16_t width, height;
} CRTSize;
typedef enum {
CRTGeometryModeTriangles
} CRTGeometryMode;
typedef struct {
/** The total size, in pixels, of the pixel buffer storage. Guaranteed to be a power of two. */
CRTSize size;
/** The portion of the pixel buffer that has been changed since the last time this set of buffers was provided. */
CRTSize dirty_size;
/** The number of individual buffers that adds up to the complete pixel buffer. */
unsigned int number_of_buffers;
/** A C array of those buffers. */
CRTBuffer *buffers;
/** The number of vertices that constitute the output. */
unsigned int number_of_vertices;
/** The type of output. */
CRTGeometryMode geometry_mode;
/** The size of each vertex in bytes. */
size_t size_per_vertex;
/** The vertex data. */
uint8_t *vertices;
} CRTFrame;
// The height of the intermediate buffers.
static const int kCRTFrameIntermediateBufferHeight = 2048;
static const size_t kCRTVertexOffsetOfPosition = 0;
static const size_t kCRTVertexOffsetOfTexCoord = 4;
static const size_t kCRTVertexOffsetOfLateral = 8;
static const int kCRTSizeOfVertex = 10;
#ifdef __cplusplus
}
#endif
#endif /* CRTFrame_h */

View File

@ -1,105 +0,0 @@
//
// CRTFrameBuilder.cpp
// Clock Signal
//
// Created by Thomas Harte on 04/02/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "CRT.hpp"
using namespace Outputs;
CRT::CRTFrameBuilder::CRTFrameBuilder(uint16_t width, uint16_t height, unsigned int number_of_buffers, va_list buffer_sizes)
{
frame.size.width = width;
frame.size.height = height;
frame.number_of_buffers = number_of_buffers;
frame.buffers = new CRTBuffer[number_of_buffers];
frame.size_per_vertex = kCRTSizeOfVertex;
frame.geometry_mode = CRTGeometryModeTriangles;
for(int buffer = 0; buffer < number_of_buffers; buffer++)
{
frame.buffers[buffer].depth = va_arg(buffer_sizes, unsigned int);
frame.buffers[buffer].data = new uint8_t[width * height * frame.buffers[buffer].depth];
}
reset();
}
CRT::CRTFrameBuilder::~CRTFrameBuilder()
{
for(int buffer = 0; buffer < frame.number_of_buffers; buffer++)
delete[] frame.buffers[buffer].data;
delete frame.buffers;
}
void CRT::CRTFrameBuilder::reset()
{
frame.number_of_vertices = 0;
_next_write_x_position = _next_write_y_position = 0;
frame.dirty_size.width = 0;
frame.dirty_size.height = 1;
}
void CRT::CRTFrameBuilder::complete()
{
frame.vertices = &_all_runs[0];
}
uint8_t *CRT::CRTFrameBuilder::get_next_run()
{
const size_t vertices_per_run = 6;
// get a run from the allocated list, allocating more if we're about to overrun
if((frame.number_of_vertices + vertices_per_run) * frame.size_per_vertex >= _all_runs.size())
{
_all_runs.resize(_all_runs.size() + frame.size_per_vertex * vertices_per_run * 100);
}
uint8_t *next_run = &_all_runs[frame.number_of_vertices * frame.size_per_vertex];
frame.number_of_vertices += vertices_per_run;
return next_run;
}
void CRT::CRTFrameBuilder::allocate_write_area(size_t required_length)
{
_last_allocation_amount = required_length;
if(_next_write_x_position + required_length + 2 > frame.size.width)
{
_next_write_x_position = 0;
_next_write_y_position = (_next_write_y_position+1)&(frame.size.height-1);
frame.dirty_size.height++;
}
_write_x_position = _next_write_x_position + 1;
_write_y_position = _next_write_y_position;
_write_target_pointer = (_write_y_position * frame.size.width) + _write_x_position;
_next_write_x_position += required_length + 2;
frame.dirty_size.width = std::max(frame.dirty_size.width, _next_write_x_position);
}
void CRT::CRTFrameBuilder::reduce_previous_allocation_to(size_t actual_length)
{
for(int c = 0; c < frame.number_of_buffers; c++)
{
memcpy( &frame.buffers[c].data[(_write_target_pointer - 1) * frame.buffers[c].depth],
&frame.buffers[c].data[_write_target_pointer * frame.buffers[c].depth],
frame.buffers[c].depth);
memcpy( &frame.buffers[c].data[(_write_target_pointer + actual_length) * frame.buffers[c].depth],
&frame.buffers[c].data[(_write_target_pointer + actual_length - 1) * frame.buffers[c].depth],
frame.buffers[c].depth);
}
_next_write_x_position -= (_last_allocation_amount - actual_length);
}
uint8_t *CRT::CRTFrameBuilder::get_write_target_for_buffer(int buffer)
{
return &frame.buffers[buffer].data[_write_target_pointer * frame.buffers[buffer].depth];
}

View File

@ -11,6 +11,7 @@
#include "OpenGL.hpp"
#include "TextureTarget.hpp"
#include "Shader.hpp"
#include "CRTOpenGL.hpp"
using namespace Outputs;
@ -30,8 +31,6 @@ struct CRT::OpenGLState {
GLuint defaultFramebuffer;
CRTSize textureSize;
std::unique_ptr<OpenGL::TextureTarget> compositeTexture; // receives raw composite levels
std::unique_ptr<OpenGL::TextureTarget> filteredYTexture; // receives filtered Y in the R channel plus unfiltered I/U and Q/V in G and B
std::unique_ptr<OpenGL::TextureTarget> filteredTexture; // receives filtered YIQ or YUV
@ -52,7 +51,6 @@ static GLenum formatForDepth(unsigned int depth)
void CRT::construct_openGL()
{
_openGL_state = nullptr;
_current_frame = _last_drawn_frame = nullptr;
_composite_shader = _rgb_shader = nullptr;
}
@ -87,56 +85,40 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&_openGL_state->defaultFramebuffer);
_openGL_state->compositeTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(2048, kCRTFrameIntermediateBufferHeight));
_openGL_state->filteredYTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(2048, kCRTFrameIntermediateBufferHeight));
_openGL_state->filteredTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(2048, kCRTFrameIntermediateBufferHeight));
// _openGL_state->compositeTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(2048, kCRTFrameIntermediateBufferHeight));
// _openGL_state->filteredYTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(2048, kCRTFrameIntermediateBufferHeight));
// _openGL_state->filteredTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(2048, kCRTFrameIntermediateBufferHeight));
}
// lock down any further work on the current frame
_current_frame_mutex->lock();
_output_mutex->lock();
if(!_current_frame && !only_if_dirty)
// update uniforms
push_size_uniforms(output_width, output_height);
glUniform1f(_openGL_state->alphaUniform, 1.0f);
// submit latest frame data if required
/* glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(current_frame->number_of_vertices * current_frame->size_per_vertex), current_frame->vertices, GL_DYNAMIC_DRAW);
glBindTexture(GL_TEXTURE_2D, _openGL_state->textureName);
if(_openGL_state->textureSize.width != _current_frame->size.width || _openGL_state->textureSize.height != _current_frame->size.height)
{
glClear(GL_COLOR_BUFFER_BIT);
GLenum format = formatForDepth(_current_frame->buffers[0].depth);
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, _current_frame->size.width, _current_frame->size.height, 0, format, GL_UNSIGNED_BYTE, _current_frame->buffers[0].data);
_openGL_state->textureSize = _current_frame->size;
if(_openGL_state->textureSizeUniform >= 0)
glUniform2f(_openGL_state->textureSizeUniform, _current_frame->size.width, _current_frame->size.height);
}
else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _current_frame->size.width, _current_frame->dirty_size.height, formatForDepth(_current_frame->buffers[0].depth), GL_UNSIGNED_BYTE, _current_frame->buffers[0].data);
if(_current_frame && (_current_frame != _last_drawn_frame || !only_if_dirty))
{
// update uniforms
push_size_uniforms(output_width, output_height);
glUniform1f(_openGL_state->alphaUniform, 1.0f);
// draw
glBindFramebuffer(GL_FRAMEBUFFER, _openGL_state->defaultFramebuffer);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_current_frame->number_of_vertices);*/
// submit new frame data if required
if (_current_frame != _last_drawn_frame)
{
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(_current_frame->number_of_vertices * _current_frame->size_per_vertex), _current_frame->vertices, GL_DYNAMIC_DRAW);
glBindTexture(GL_TEXTURE_2D, _openGL_state->textureName);
if(_openGL_state->textureSize.width != _current_frame->size.width || _openGL_state->textureSize.height != _current_frame->size.height)
{
GLenum format = formatForDepth(_current_frame->buffers[0].depth);
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, _current_frame->size.width, _current_frame->size.height, 0, format, GL_UNSIGNED_BYTE, _current_frame->buffers[0].data);
_openGL_state->textureSize = _current_frame->size;
if(_openGL_state->textureSizeUniform >= 0)
glUniform2f(_openGL_state->textureSizeUniform, _current_frame->size.width, _current_frame->size.height);
}
else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _current_frame->size.width, _current_frame->dirty_size.height, formatForDepth(_current_frame->buffers[0].depth), GL_UNSIGNED_BYTE, _current_frame->buffers[0].data);
}
// draw
_openGL_state->compositeTexture->bind_framebuffer();
glBindFramebuffer(GL_FRAMEBUFFER, _openGL_state->defaultFramebuffer);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_current_frame->number_of_vertices);
_last_drawn_frame = _current_frame;
}
_current_frame_mutex->unlock();
_output_mutex->unlock();
}
void CRT::set_openGL_context_will_change(bool should_delete_resources)

25
Outputs/CRT/CRTOpenGL.hpp Normal file
View File

@ -0,0 +1,25 @@
//
// CRTOpenGL.hpp
// Clock Signal
//
// Created by Thomas Harte on 13/02/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef CRTOpenGL_h
#define CRTOpenGL_h
const size_t kCRTVertexOffsetOfPosition = 0;
const size_t kCRTVertexOffsetOfTexCoord = 2;
const size_t kCRTVertexOffsetOfTimestamp = 4;
const size_t kCRTVertexOffsetOfLateral = 8;
const size_t kCRTSizeOfVertex = 10;
const int CRTInputBufferBuilderWidth = 2048;
const int CRTInputBufferBuilderHeight = 1024;
const int CRTIntermediateBufferWidth = 2048;
const int CRTIntermediateBufferHeight = 2048;
#endif /* CRTOpenGL_h */