mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +00:00
Started trying to clean up my Outputs namespace by moving stuff related to the CRT underneath a separate subnamespace.
This commit is contained in:
parent
bb09a5f58c
commit
c7976dd657
@ -24,7 +24,7 @@ Machine::Machine() :
|
||||
_piaDataValue{0xff, 0xff},
|
||||
_tiaInputValue{0xff, 0xff}
|
||||
{
|
||||
_crt = new Outputs::CRT(228, 1, Outputs::CRT::DisplayType::NTSC60, 1, 2);
|
||||
_crt = new Outputs::CRT::CRT(228, 1, Outputs::CRT::DisplayType::NTSC60, 1, 2);
|
||||
_crt->set_composite_sampling_function(
|
||||
"float composite_sample(vec2 coordinate, float phase)\n"
|
||||
"{\n"
|
||||
|
@ -29,7 +29,7 @@ class Machine: public CPU6502::Processor<Machine> {
|
||||
|
||||
void set_digital_input(Atari2600DigitalInput input, bool state);
|
||||
|
||||
Outputs::CRT *get_crt() { return _crt; }
|
||||
Outputs::CRT::CRT *get_crt() { return _crt; }
|
||||
|
||||
private:
|
||||
uint8_t *_rom, *_romPages[4], _ram[128];
|
||||
@ -90,7 +90,7 @@ class Machine: public CPU6502::Processor<Machine> {
|
||||
|
||||
void output_pixels(unsigned int count);
|
||||
void get_output_pixel(uint8_t *pixel, int offset);
|
||||
Outputs::CRT *_crt;
|
||||
Outputs::CRT::CRT *_crt;
|
||||
|
||||
// latched output state
|
||||
unsigned int _lastOutputStateDuration;
|
||||
|
@ -32,7 +32,7 @@ Machine::Machine() :
|
||||
_audioOutputPositionError(0),
|
||||
_currentOutputLine(0),
|
||||
_is_odd_field(false),
|
||||
_crt(std::unique_ptr<Outputs::CRT>(new Outputs::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, 1)))
|
||||
{
|
||||
_crt->set_rgb_sampling_function(
|
||||
"vec4 rgb_sample(vec2 coordinate)"
|
||||
|
@ -147,7 +147,7 @@ class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
|
||||
|
||||
void set_key_state(Key key, bool isPressed);
|
||||
|
||||
Outputs::CRT *get_crt() { return _crt.get(); }
|
||||
Outputs::CRT::CRT *get_crt() { return _crt.get(); }
|
||||
Outputs::Speaker *get_speaker() { return &_speaker; }
|
||||
|
||||
virtual void tape_did_change_interrupt_status(Tape *tape);
|
||||
@ -201,7 +201,7 @@ class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
|
||||
Tape _tape;
|
||||
|
||||
// Outputs.
|
||||
std::unique_ptr<Outputs::CRT> _crt;
|
||||
std::unique_ptr<Outputs::CRT::CRT> _crt;
|
||||
Speaker _speaker;
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
|
||||
using namespace Outputs;
|
||||
using namespace Outputs::CRT;
|
||||
|
||||
void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator)
|
||||
{
|
||||
@ -39,8 +39,8 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di
|
||||
_sync_capacitor_charge_threshold = ((syncCapacityLineChargeThreshold * _cycles_per_line) * 50) >> 7;
|
||||
|
||||
// create the two flywheels
|
||||
_horizontal_flywheel = std::unique_ptr<Outputs::Flywheel>(new Outputs::Flywheel(_cycles_per_line, (millisecondsHorizontalRetraceTime * _cycles_per_line) >> 6));
|
||||
_vertical_flywheel = std::unique_ptr<Outputs::Flywheel>(new Outputs::Flywheel(_cycles_per_line * height_of_display, scanlinesVerticalRetraceTime * _cycles_per_line));
|
||||
_horizontal_flywheel = std::unique_ptr<Flywheel>(new Flywheel(_cycles_per_line, (millisecondsHorizontalRetraceTime * _cycles_per_line) >> 6));
|
||||
_vertical_flywheel = std::unique_ptr<Flywheel>(new Flywheel(_cycles_per_line * height_of_display, scanlinesVerticalRetraceTime * _cycles_per_line));
|
||||
|
||||
// figure out the divisor necessary to get the horizontal flywheel into a 16-bit range
|
||||
unsigned int real_clock_scan_period = (_cycles_per_line * height_of_display) / (_time_multiplier * _common_output_divisor);
|
||||
@ -63,12 +63,12 @@ void CRT::set_new_display_type(unsigned int cycles_per_line, DisplayType display
|
||||
|
||||
void CRT::allocate_buffers(unsigned int number, va_list sizes)
|
||||
{
|
||||
_run_builders = new CRTRunBuilder *[kCRTNumberOfFields];
|
||||
for(int builder = 0; builder < kCRTNumberOfFields; builder++)
|
||||
_run_builders = new CRTRunBuilder *[NumberOfFields];
|
||||
for(int builder = 0; builder < NumberOfFields; builder++)
|
||||
{
|
||||
_run_builders[builder] = new CRTRunBuilder(kCRTOutputVertexSize);
|
||||
_run_builders[builder] = new CRTRunBuilder(OutputVertexSize);
|
||||
}
|
||||
_composite_src_runs = std::unique_ptr<CRTRunBuilder>(new CRTRunBuilder(kCRTInputVertexSize));
|
||||
_composite_src_runs = std::unique_ptr<CRTRunBuilder>(new CRTRunBuilder(InputVertexSize));
|
||||
|
||||
va_list va;
|
||||
va_copy(va, sizes);
|
||||
@ -92,7 +92,7 @@ CRT::CRT(unsigned int common_output_divisor) :
|
||||
|
||||
CRT::~CRT()
|
||||
{
|
||||
for(int builder = 0; builder < kCRTNumberOfFields; builder++)
|
||||
for(int builder = 0; builder < NumberOfFields; builder++)
|
||||
{
|
||||
delete _run_builders[builder];
|
||||
}
|
||||
@ -132,20 +132,20 @@ Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested,
|
||||
return _horizontal_flywheel->get_next_event_in_period(hsync_is_requested, cycles_to_run_for, cycles_advanced);
|
||||
}
|
||||
|
||||
#define output_position_x(v) (*(uint16_t *)&next_run[kCRTOutputVertexSize*v + kCRTOutputVertexOffsetOfPosition + 0])
|
||||
#define output_position_y(v) (*(uint16_t *)&next_run[kCRTOutputVertexSize*v + kCRTOutputVertexOffsetOfPosition + 2])
|
||||
#define output_tex_x(v) (*(uint16_t *)&next_run[kCRTOutputVertexSize*v + kCRTOutputVertexOffsetOfTexCoord + 0])
|
||||
#define output_tex_y(v) (*(uint16_t *)&next_run[kCRTOutputVertexSize*v + kCRTOutputVertexOffsetOfTexCoord + 2])
|
||||
#define output_lateral(v) next_run[kCRTOutputVertexSize*v + kCRTOutputVertexOffsetOfLateral]
|
||||
#define output_timestamp(v) (*(uint32_t *)&next_run[kCRTOutputVertexSize*v + kCRTOutputVertexOffsetOfTimestamp])
|
||||
#define output_position_x(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfPosition + 0])
|
||||
#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 output_timestamp(v) (*(uint32_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfTimestamp])
|
||||
|
||||
#define input_input_position_x(v) (*(uint16_t *)&next_run[kCRTInputVertexSize*v + kCRTInputVertexOffsetOfInputPosition + 0])
|
||||
#define input_input_position_y(v) (*(uint16_t *)&next_run[kCRTInputVertexSize*v + kCRTInputVertexOffsetOfInputPosition + 2])
|
||||
#define input_output_position_x(v) (*(uint16_t *)&next_run[kCRTInputVertexSize*v + kCRTInputVertexOffsetOfOutputPosition + 0])
|
||||
#define input_output_position_y(v) (*(uint16_t *)&next_run[kCRTInputVertexSize*v + kCRTInputVertexOffsetOfOutputPosition + 2])
|
||||
#define input_phase(v) next_run[kCRTOutputVertexSize*v + kCRTInputVertexOffsetOfPhaseAndAmplitude + 0]
|
||||
#define input_amplitude(v) next_run[kCRTOutputVertexSize*v + kCRTInputVertexOffsetOfPhaseAndAmplitude + 1]
|
||||
#define input_phase_time(v) (*(uint16_t *)&next_run[kCRTOutputVertexSize*v + kCRTInputVertexOffsetOfPhaseTime])
|
||||
#define input_input_position_x(v) (*(uint16_t *)&next_run[InputVertexSize*v + InputVertexOffsetOfInputPosition + 0])
|
||||
#define input_input_position_y(v) (*(uint16_t *)&next_run[InputVertexSize*v + InputVertexOffsetOfInputPosition + 2])
|
||||
#define input_output_position_x(v) (*(uint16_t *)&next_run[InputVertexSize*v + InputVertexOffsetOfOutputPosition + 0])
|
||||
#define input_output_position_y(v) (*(uint16_t *)&next_run[InputVertexSize*v + InputVertexOffsetOfOutputPosition + 2])
|
||||
#define input_phase(v) next_run[OutputVertexSize*v + InputVertexOffsetOfPhaseAndAmplitude + 0]
|
||||
#define input_amplitude(v) next_run[OutputVertexSize*v + InputVertexOffsetOfPhaseAndAmplitude + 1]
|
||||
#define input_phase_time(v) (*(uint16_t *)&next_run[OutputVertexSize*v + InputVertexOffsetOfPhaseTime])
|
||||
|
||||
void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Type type, uint16_t tex_x, uint16_t tex_y)
|
||||
{
|
||||
@ -171,7 +171,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
|
||||
if(is_output_segment)
|
||||
{
|
||||
_output_mutex->lock();
|
||||
next_run = (_output_device == CRT::Monitor) ? _run_builders[_run_write_pointer]->get_next_run(6) : _composite_src_runs->get_next_run(2);
|
||||
next_run = (_output_device == Monitor) ? _run_builders[_run_write_pointer]->get_next_run(6) : _composite_src_runs->get_next_run(2);
|
||||
}
|
||||
|
||||
// Vertex output is arranged for triangle strips, as:
|
||||
@ -181,7 +181,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
|
||||
// [0/1] 3
|
||||
if(next_run)
|
||||
{
|
||||
if(_output_device == CRT::Monitor)
|
||||
if(_output_device == Monitor)
|
||||
{
|
||||
// set the type, initial raster position and type of this run
|
||||
output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position();
|
||||
@ -226,7 +226,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
|
||||
// if this is a data run then advance the buffer pointer
|
||||
if(type == Type::Data && source_divider) tex_x += next_run_length / (_time_multiplier * source_divider);
|
||||
|
||||
if(_output_device == CRT::Monitor)
|
||||
if(_output_device == Monitor)
|
||||
{
|
||||
// store the final raster position
|
||||
output_position_x(3) = output_position_x(4) = output_position_x(5) = (uint16_t)_horizontal_flywheel->get_current_output_position();
|
||||
@ -247,7 +247,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
|
||||
}
|
||||
|
||||
// if this is horizontal retrace then advance the output line counter and bookend an output run
|
||||
if(_output_device == CRT::Television)
|
||||
if(_output_device == Television)
|
||||
{
|
||||
Flywheel::SyncEvent honoured_event = Flywheel::SyncEvent::None;
|
||||
if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event != Flywheel::SyncEvent::None) honoured_event = next_vertical_sync_event;
|
||||
@ -274,7 +274,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
|
||||
|
||||
if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::EndRetrace)
|
||||
{
|
||||
_composite_src_output_y = (_composite_src_output_y + 1) % CRTIntermediateBufferHeight;
|
||||
_composite_src_output_y = (_composite_src_output_y + 1) % IntermediateBufferHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@ -284,7 +284,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
|
||||
// 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)%kCRTNumberOfFields;
|
||||
_run_write_pointer = (_run_write_pointer + 1)%NumberOfFields;
|
||||
_run_builders[_run_write_pointer]->reset();
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "Flywheel.hpp"
|
||||
|
||||
namespace Outputs {
|
||||
namespace CRT {
|
||||
|
||||
struct Rect {
|
||||
struct {
|
||||
@ -33,25 +34,78 @@ struct Rect {
|
||||
origin({.x = x, .y = y}), size({.width = width, .height =height}) {}
|
||||
};
|
||||
|
||||
enum DisplayType {
|
||||
PAL50,
|
||||
NTSC60
|
||||
};
|
||||
|
||||
enum ColourSpace {
|
||||
YIQ,
|
||||
YUV
|
||||
};
|
||||
|
||||
enum OutputDevice {
|
||||
Monitor,
|
||||
Television
|
||||
};
|
||||
|
||||
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);
|
||||
uint8_t *get_write_target_for_buffer(int buffer);
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct CRTRunBuilder {
|
||||
CRTRunBuilder(size_t vertex_size) : _vertex_size(vertex_size) { reset(); }
|
||||
|
||||
// Resets the run builder.
|
||||
void reset();
|
||||
|
||||
// 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_run(size_t number_of_vertices);
|
||||
std::vector<uint8_t> _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_vertices;
|
||||
size_t number_of_vertices;
|
||||
|
||||
private:
|
||||
size_t _vertex_size;
|
||||
};
|
||||
|
||||
struct OpenGLState;
|
||||
|
||||
class CRT {
|
||||
public:
|
||||
~CRT();
|
||||
|
||||
enum DisplayType {
|
||||
PAL50,
|
||||
NTSC60
|
||||
};
|
||||
|
||||
enum ColourSpace {
|
||||
YIQ,
|
||||
YUV
|
||||
};
|
||||
|
||||
enum OutputDevice {
|
||||
Monitor,
|
||||
Television
|
||||
};
|
||||
|
||||
/*! Constructs the CRT with a specified clock rate, height and colour subcarrier frequency.
|
||||
The requested number of buffers, each with the requested number of bytes per pixel,
|
||||
is created for the machine to write raw pixel data to.
|
||||
@ -259,7 +313,7 @@ class CRT {
|
||||
Rect _visible_area;
|
||||
|
||||
// the two flywheels regulating scanning
|
||||
std::unique_ptr<Outputs::Flywheel> _horizontal_flywheel, _vertical_flywheel;
|
||||
std::unique_ptr<Flywheel> _horizontal_flywheel, _vertical_flywheel;
|
||||
uint16_t _vertical_flywheel_output_divider;
|
||||
|
||||
// elements of sync separation
|
||||
@ -295,57 +349,6 @@ class CRT {
|
||||
};
|
||||
void output_scan(Scan *scan);
|
||||
|
||||
struct CRTRunBuilder {
|
||||
CRTRunBuilder(size_t vertex_size) : _vertex_size(vertex_size) { reset(); }
|
||||
|
||||
// Resets the run builder.
|
||||
void reset();
|
||||
|
||||
// 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_run(size_t number_of_vertices);
|
||||
std::vector<uint8_t> _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_vertices;
|
||||
size_t number_of_vertices;
|
||||
|
||||
private:
|
||||
size_t _vertex_size;
|
||||
};
|
||||
|
||||
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);
|
||||
uint8_t *get_write_target_for_buffer(int buffer);
|
||||
|
||||
// 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;
|
||||
|
||||
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
|
||||
std::unique_ptr<CRTInputBufferBuilder> _buffer_builder;
|
||||
CRTRunBuilder **_run_builders;
|
||||
@ -360,7 +363,6 @@ class CRT {
|
||||
bool _is_writing_composite_run;
|
||||
|
||||
// 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.
|
||||
@ -390,6 +392,6 @@ class CRT {
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* CRT_cpp */
|
||||
|
@ -23,7 +23,7 @@ CRT::CRTInputBufferBuilder::CRTInputBufferBuilder(unsigned int 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];
|
||||
buffers[buffer].data = new uint8_t[InputBufferBuilderWidth * InputBufferBuilderHeight * buffers[buffer].bytes_per_pixel];
|
||||
}
|
||||
|
||||
_next_write_x_position = _next_write_y_position = 0;
|
||||
@ -42,15 +42,15 @@ void CRT::CRTInputBufferBuilder::allocate_write_area(size_t required_length)
|
||||
{
|
||||
_last_allocation_amount = required_length;
|
||||
|
||||
if(_next_write_x_position + required_length + 2 > CRTInputBufferBuilderWidth)
|
||||
if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth)
|
||||
{
|
||||
_next_write_x_position = 0;
|
||||
_next_write_y_position = (_next_write_y_position+1)%CRTInputBufferBuilderHeight;
|
||||
_next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight;
|
||||
}
|
||||
|
||||
_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;
|
||||
_write_target_pointer = (_write_y_position * InputBufferBuilderWidth) + _write_x_position;
|
||||
_next_write_x_position += required_length + 2;
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,10 @@
|
||||
#include "Shader.hpp"
|
||||
#include "CRTOpenGL.hpp"
|
||||
|
||||
using namespace Outputs;
|
||||
namespace Outputs {
|
||||
namespace CRT {
|
||||
|
||||
struct CRT::OpenGLState {
|
||||
struct OpenGLState {
|
||||
std::unique_ptr<OpenGL::Shader> rgb_shader_program;
|
||||
std::unique_ptr<OpenGL::Shader> composite_input_shader_program, composite_output_shader_program;
|
||||
|
||||
@ -35,6 +36,11 @@ struct CRT::OpenGLState {
|
||||
std::unique_ptr<OpenGL::TextureTarget> filteredTexture; // receives filtered YIQ or YUV
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Outputs::CRT;
|
||||
|
||||
namespace {
|
||||
static const GLenum first_supplied_buffer_texture_unit = 3;
|
||||
}
|
||||
@ -84,7 +90,7 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
GLenum format = formatForDepth(_buffer_builder->buffers[buffer].bytes_per_pixel);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, CRTInputBufferBuilderWidth, CRTInputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, _buffer_builder->buffers[buffer].data);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, InputBufferBuilderWidth, InputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, _buffer_builder->buffers[buffer].data);
|
||||
}
|
||||
|
||||
glGenVertexArrays(1, &_openGL_state->output_vertex_array);
|
||||
@ -105,11 +111,11 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
|
||||
|
||||
// Create intermediate textures and bind to slots 0, 1 and 2
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
_openGL_state->compositeTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(CRTIntermediateBufferWidth, CRTIntermediateBufferHeight));
|
||||
_openGL_state->compositeTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight));
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
_openGL_state->filteredYTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(CRTIntermediateBufferWidth, CRTIntermediateBufferHeight));
|
||||
_openGL_state->filteredYTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight));
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
_openGL_state->filteredTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(CRTIntermediateBufferWidth, CRTIntermediateBufferHeight));
|
||||
_openGL_state->filteredTexture = std::unique_ptr<OpenGL::TextureTarget>(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight));
|
||||
}
|
||||
|
||||
// glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&_openGL_state->defaultFramebuffer);
|
||||
@ -129,9 +135,9 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
|
||||
{
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
||||
0, (GLint)_buffer_builder->last_uploaded_line,
|
||||
CRTInputBufferBuilderWidth, (GLint)(CRTInputBufferBuilderHeight - _buffer_builder->last_uploaded_line),
|
||||
InputBufferBuilderWidth, (GLint)(InputBufferBuilderHeight - _buffer_builder->last_uploaded_line),
|
||||
format, GL_UNSIGNED_BYTE,
|
||||
&_buffer_builder->buffers[0].data[_buffer_builder->last_uploaded_line * CRTInputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel]);
|
||||
&_buffer_builder->buffers[0].data[_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel]);
|
||||
_buffer_builder->last_uploaded_line = 0;
|
||||
}
|
||||
|
||||
@ -139,9 +145,9 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
|
||||
{
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
||||
0, (GLint)_buffer_builder->last_uploaded_line,
|
||||
CRTInputBufferBuilderWidth, (GLint)(1 + _buffer_builder->_next_write_y_position - _buffer_builder->last_uploaded_line),
|
||||
InputBufferBuilderWidth, (GLint)(1 + _buffer_builder->_next_write_y_position - _buffer_builder->last_uploaded_line),
|
||||
format, GL_UNSIGNED_BYTE,
|
||||
&_buffer_builder->buffers[0].data[_buffer_builder->last_uploaded_line * CRTInputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel]);
|
||||
&_buffer_builder->buffers[0].data[_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel]);
|
||||
_buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position;
|
||||
}
|
||||
}
|
||||
@ -163,19 +169,19 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
|
||||
// ensure array buffer is up to date
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _openGL_state->output_array_buffer);
|
||||
size_t max_number_of_vertices = 0;
|
||||
for(int c = 0; c < kCRTNumberOfFields; c++)
|
||||
for(int c = 0; c < NumberOfFields; c++)
|
||||
{
|
||||
max_number_of_vertices = std::max(max_number_of_vertices, _run_builders[c]->number_of_vertices);
|
||||
}
|
||||
if(_openGL_state->output_vertices_per_slice < max_number_of_vertices)
|
||||
{
|
||||
_openGL_state->output_vertices_per_slice = max_number_of_vertices;
|
||||
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(max_number_of_vertices * kCRTOutputVertexSize * kCRTOutputVertexSize), NULL, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(max_number_of_vertices * OutputVertexSize * OutputVertexSize), NULL, GL_STREAM_DRAW);
|
||||
|
||||
for(unsigned int c = 0; c < kCRTNumberOfFields; c++)
|
||||
for(unsigned int c = 0; c < NumberOfFields; c++)
|
||||
{
|
||||
uint8_t *data = &_run_builders[c]->_runs[0];
|
||||
glBufferSubData(GL_ARRAY_BUFFER, (GLsizeiptr)(c * _openGL_state->output_vertices_per_slice * kCRTOutputVertexSize), (GLsizeiptr)(_run_builders[c]->number_of_vertices * kCRTOutputVertexSize), data);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, (GLsizeiptr)(c * _openGL_state->output_vertices_per_slice * OutputVertexSize), (GLsizeiptr)(_run_builders[c]->number_of_vertices * OutputVertexSize), data);
|
||||
_run_builders[c]->uploaded_vertices = _run_builders[c]->number_of_vertices;
|
||||
}
|
||||
}
|
||||
@ -198,7 +204,7 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
|
||||
unsigned int run = (unsigned int)_run_write_pointer;
|
||||
// printf("%d: %zu v %zu\n", run, _run_builders[run]->uploaded_vertices, _run_builders[run]->number_of_vertices);
|
||||
GLint total_age = 0;
|
||||
for(int c = 0; c < kCRTNumberOfFields; c++)
|
||||
for(int c = 0; c < NumberOfFields; c++)
|
||||
{
|
||||
// update the total age at the start of this set of runs
|
||||
total_age += _run_builders[run]->duration;
|
||||
@ -209,10 +215,10 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
|
||||
|
||||
if(_run_builders[run]->uploaded_vertices != _run_builders[run]->number_of_vertices)
|
||||
{
|
||||
uint8_t *data = &_run_builders[run]->_runs[_run_builders[run]->uploaded_vertices * kCRTOutputVertexSize];
|
||||
uint8_t *data = &_run_builders[run]->_runs[_run_builders[run]->uploaded_vertices * OutputVertexSize];
|
||||
glBufferSubData(GL_ARRAY_BUFFER,
|
||||
(GLsizeiptr)(((run * _openGL_state->output_vertices_per_slice) + _run_builders[run]->uploaded_vertices) * kCRTOutputVertexSize),
|
||||
(GLsizeiptr)((_run_builders[run]->number_of_vertices - _run_builders[run]->uploaded_vertices) * kCRTOutputVertexSize), data);
|
||||
(GLsizeiptr)(((run * _openGL_state->output_vertices_per_slice) + _run_builders[run]->uploaded_vertices) * OutputVertexSize),
|
||||
(GLsizeiptr)((_run_builders[run]->number_of_vertices - _run_builders[run]->uploaded_vertices) * OutputVertexSize), data);
|
||||
_run_builders[run]->uploaded_vertices = _run_builders[run]->number_of_vertices;
|
||||
}
|
||||
|
||||
@ -221,7 +227,7 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
|
||||
}
|
||||
|
||||
// advance back in time
|
||||
run = (run - 1 + kCRTNumberOfFields) % kCRTNumberOfFields;
|
||||
run = (run - 1 + NumberOfFields) % NumberOfFields;
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,8 +443,8 @@ void CRT::prepare_composite_input_shader()
|
||||
GLint phaseCyclesPerTickUniform = _openGL_state->composite_input_shader_program->get_uniform_location("phaseCyclesPerTick");
|
||||
|
||||
glUniform1i(texIDUniform, first_supplied_buffer_texture_unit);
|
||||
glUniform2f(outputTextureSizeUniform, CRTIntermediateBufferWidth, CRTIntermediateBufferHeight);
|
||||
glUniform2f(inputTextureSizeUniform, CRTInputBufferBuilderWidth, CRTInputBufferBuilderHeight);
|
||||
glUniform2f(outputTextureSizeUniform, IntermediateBufferWidth, IntermediateBufferHeight);
|
||||
glUniform2f(inputTextureSizeUniform, InputBufferBuilderWidth, InputBufferBuilderHeight);
|
||||
glUniform1f(phaseCyclesPerTickUniform, (float)_colour_cycle_numerator / (float)(_colour_cycle_denominator * _cycles_per_line));
|
||||
}
|
||||
free(vertex_shader);
|
||||
@ -509,7 +515,7 @@ void CRT::prepare_rgb_output_shader()
|
||||
|
||||
glUniform1i(texIDUniform, first_supplied_buffer_texture_unit);
|
||||
glUniform1i(shadowMaskTexIDUniform, 1);
|
||||
glUniform2f(textureSizeUniform, CRTInputBufferBuilderWidth, CRTInputBufferBuilderHeight);
|
||||
glUniform2f(textureSizeUniform, InputBufferBuilderWidth, InputBufferBuilderHeight);
|
||||
glUniform1f(ticksPerFrameUniform, (GLfloat)(_cycles_per_line * _height_of_display));
|
||||
glUniform2f(positionConversionUniform, _horizontal_flywheel->get_scan_period(), _vertical_flywheel->get_scan_period() / (unsigned int)_vertical_flywheel_output_divider);
|
||||
|
||||
@ -539,23 +545,23 @@ void CRT::prepare_output_vertex_array()
|
||||
glEnableVertexAttribArray((GLuint)lateralAttribute);
|
||||
glEnableVertexAttribArray((GLuint)timestampAttribute);
|
||||
|
||||
const GLsizei vertexStride = kCRTOutputVertexSize;
|
||||
glVertexAttribPointer((GLuint)positionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)kCRTOutputVertexOffsetOfPosition);
|
||||
glVertexAttribPointer((GLuint)textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)kCRTOutputVertexOffsetOfTexCoord);
|
||||
glVertexAttribPointer((GLuint)timestampAttribute, 4, GL_UNSIGNED_INT, GL_FALSE, vertexStride, (void *)kCRTOutputVertexOffsetOfTimestamp);
|
||||
glVertexAttribPointer((GLuint)lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, vertexStride, (void *)kCRTOutputVertexOffsetOfLateral);
|
||||
const GLsizei vertexStride = OutputVertexSize;
|
||||
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)timestampAttribute, 4, GL_UNSIGNED_INT, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfTimestamp);
|
||||
glVertexAttribPointer((GLuint)lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfLateral);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Configuration
|
||||
|
||||
void CRT::set_output_device(CRT::OutputDevice output_device)
|
||||
void CRT::set_output_device(OutputDevice output_device)
|
||||
{
|
||||
if (_output_device != output_device)
|
||||
{
|
||||
_output_device = output_device;
|
||||
|
||||
for(int builder = 0; builder < kCRTNumberOfFields; builder++)
|
||||
for(int builder = 0; builder < NumberOfFields; builder++)
|
||||
{
|
||||
_run_builders[builder]->reset();
|
||||
}
|
||||
|
@ -9,35 +9,41 @@
|
||||
#ifndef CRTOpenGL_h
|
||||
#define CRTOpenGL_h
|
||||
|
||||
namespace Outputs {
|
||||
namespace CRT {
|
||||
|
||||
// Output vertices are those used to copy from an input buffer — whether it describes data that maps directly to RGB
|
||||
// or is one of the intermediate buffers that we've used to convert from composite towards RGB.
|
||||
const size_t kCRTOutputVertexOffsetOfPosition = 0;
|
||||
const size_t kCRTOutputVertexOffsetOfTexCoord = 4;
|
||||
const size_t kCRTOutputVertexOffsetOfTimestamp = 8;
|
||||
const size_t kCRTOutputVertexOffsetOfLateral = 12;
|
||||
const size_t OutputVertexOffsetOfPosition = 0;
|
||||
const size_t OutputVertexOffsetOfTexCoord = 4;
|
||||
const size_t OutputVertexOffsetOfTimestamp = 8;
|
||||
const size_t OutputVertexOffsetOfLateral = 12;
|
||||
|
||||
const size_t kCRTOutputVertexSize = 16;
|
||||
const size_t OutputVertexSize = 16;
|
||||
|
||||
// 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 size_t kCRTInputVertexOffsetOfInputPosition = 0;
|
||||
const size_t kCRTInputVertexOffsetOfOutputPosition = 4;
|
||||
const size_t kCRTInputVertexOffsetOfPhaseAndAmplitude = 8;
|
||||
const size_t kCRTInputVertexOffsetOfPhaseTime = 12;
|
||||
const size_t InputVertexOffsetOfInputPosition = 0;
|
||||
const size_t InputVertexOffsetOfOutputPosition = 4;
|
||||
const size_t InputVertexOffsetOfPhaseAndAmplitude = 8;
|
||||
const size_t InputVertexOffsetOfPhaseTime = 12;
|
||||
|
||||
const size_t kCRTInputVertexSize = 16;
|
||||
const size_t InputVertexSize = 16;
|
||||
|
||||
// These constants hold the size of the rolling buffer to which the CPU writes
|
||||
const int CRTInputBufferBuilderWidth = 2048;
|
||||
const int CRTInputBufferBuilderHeight = 1024;
|
||||
const int InputBufferBuilderWidth = 2048;
|
||||
const int InputBufferBuilderHeight = 1024;
|
||||
|
||||
// This is the size of the intermediate buffers used during composite to RGB conversion
|
||||
const int CRTIntermediateBufferWidth = 2048;
|
||||
const int CRTIntermediateBufferHeight = 2048;
|
||||
const int IntermediateBufferWidth = 2048;
|
||||
const int IntermediateBufferHeight = 2048;
|
||||
|
||||
// Runs are divided discretely by vertical syncs in order to put a usable bounds on the uniform used to track
|
||||
// run age; that therefore creates a discrete number of fields that are stored. This number should be the
|
||||
// number of historic fields that are required fully to
|
||||
const int kCRTNumberOfFields = 3;
|
||||
const int NumberOfFields = 3;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CRTOpenGL_h */
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define Flywheel_hpp
|
||||
|
||||
namespace Outputs {
|
||||
namespace CRT {
|
||||
|
||||
/*!
|
||||
Provides timing for a two-phase signal consisting of a retrace phase followed by a scan phase,
|
||||
@ -211,6 +212,7 @@ struct Flywheel
|
||||
*/
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Flywheel_hpp */
|
||||
|
Loading…
Reference in New Issue
Block a user