2015-07-19 17:36:27 +00:00
//
// CRT.hpp
// Clock Signal
//
// Created by Thomas Harte on 19/07/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
2016-03-09 01:59:16 +00:00
# ifndef CRT_hpp
# define CRT_hpp
2015-07-19 17:36:27 +00:00
# include <stdint.h>
2016-03-09 03:40:23 +00:00
# include "CRTTypes.hpp"
2016-03-09 01:59:16 +00:00
# include "Internals/Flywheel.hpp"
2016-03-09 03:40:23 +00:00
# include "Internals/CRTOpenGL.hpp"
2016-11-17 03:00:11 +00:00
# include "Internals/ArrayBuilder.hpp"
# include "Internals/TextureBuilder.hpp"
2015-07-23 00:33:20 +00:00
2015-07-25 03:29:45 +00:00
namespace Outputs {
2016-03-09 01:49:07 +00:00
namespace CRT {
2015-07-23 00:33:20 +00:00
2016-04-24 22:36:22 +00:00
class CRT ;
class Delegate {
public :
virtual void crt_did_end_batch_of_frames ( CRT * crt , unsigned int number_of_frames , unsigned int number_of_unexpected_vertical_syncs ) = 0 ;
} ;
2016-03-09 03:40:23 +00:00
class CRT {
private :
2016-11-17 03:00:11 +00:00
CRT ( unsigned int common_output_divisor , unsigned int buffer_depth ) ;
2016-02-08 03:18:55 +00:00
2016-03-09 03:40:23 +00:00
// the incoming clock lengths will be multiplied by something to give at least 1000
// sample points per line
2016-11-17 03:00:11 +00:00
unsigned int time_multiplier_ ;
const unsigned int common_output_divisor_ ;
2016-02-08 03:18:55 +00:00
2016-03-09 03:40:23 +00:00
// the two flywheels regulating scanning
2016-11-17 03:00:11 +00:00
std : : unique_ptr < Flywheel > horizontal_flywheel_ , vertical_flywheel_ ;
uint16_t vertical_flywheel_output_divider_ ;
2015-07-23 00:33:20 +00:00
2016-03-09 03:40:23 +00:00
// elements of sync separation
2016-11-17 03:00:11 +00:00
bool is_receiving_sync_ ; // true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync)
int sync_capacitor_charge_level_ ; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync
int sync_capacitor_charge_threshold_ ; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync
unsigned int sync_period_ ;
2015-07-23 00:33:20 +00:00
2016-03-09 03:40:23 +00:00
struct Scan {
enum Type {
Sync , Level , Data , Blank , ColourBurst
} type ;
unsigned int number_of_cycles ;
union {
struct {
uint8_t phase , amplitude ;
} ;
} ;
} ;
2016-03-09 03:54:05 +00:00
void output_scan ( const Scan * scan ) ;
2016-02-07 22:32:38 +00:00
2016-11-17 03:00:11 +00:00
uint8_t colour_burst_phase_ , colour_burst_amplitude_ ;
bool is_writing_composite_run_ ;
2016-03-09 01:49:07 +00:00
2016-12-10 18:42:34 +00:00
unsigned int phase_denominator_ , phase_numerator_ , colour_cycle_numerator_ ;
bool is_alernate_line_ , phase_alternates_ ;
2016-03-09 03:40:23 +00:00
// the outer entry point for dispatching output_sync, output_blank, output_level and output_data
2017-03-06 01:17:55 +00:00
void advance_cycles ( unsigned int number_of_cycles , bool hsync_requested , bool vsync_requested , const Scan : : Type type ) ;
2016-03-09 03:40:23 +00:00
// the inner entry point that determines whether and when the next sync event will occur within
// the current output window
Flywheel : : SyncEvent get_next_vertical_sync_event ( bool vsync_is_requested , unsigned int cycles_to_run_for , unsigned int * cycles_advanced ) ;
Flywheel : : SyncEvent get_next_horizontal_sync_event ( bool hsync_is_requested , unsigned int cycles_to_run_for , unsigned int * cycles_advanced ) ;
2016-11-17 03:00:11 +00:00
// OpenGL state
OpenGLOutputBuilder openGL_output_builder_ ;
2016-02-07 22:32:38 +00:00
2016-05-10 23:04:03 +00:00
// temporary storage used during the construction of output runs
struct {
2016-11-16 03:34:05 +00:00
uint16_t x1 , y ;
2016-11-17 03:00:11 +00:00
} output_run_ ;
2016-05-10 23:04:03 +00:00
2017-02-20 02:46:07 +00:00
// the delegate
2016-11-17 03:00:11 +00:00
Delegate * delegate_ ;
unsigned int frames_since_last_delegate_call_ ;
2016-04-24 22:36:22 +00:00
2017-02-20 02:46:07 +00:00
// queued tasks for the OpenGL queue; performed before the next draw
std : : mutex function_mutex_ ;
std : : vector < std : : function < void ( void ) > > enqueued_openGL_functions_ ;
2017-03-26 18:34:47 +00:00
inline void enqueue_openGL_function ( const std : : function < void ( void ) > & function ) {
2017-02-20 02:46:07 +00:00
std : : lock_guard < std : : mutex > function_guard ( function_mutex_ ) ;
enqueued_openGL_functions_ . push_back ( function ) ;
}
2017-03-07 00:15:33 +00:00
// sync counter, for determining vertical sync
unsigned int cycles_of_sync_ ;
unsigned int cycles_since_sync_ ;
unsigned int cycles_per_line_ ;
2016-03-09 03:40:23 +00:00
public :
2016-02-03 03:41:33 +00:00
/*! 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 .
2015-07-23 00:33:20 +00:00
2016-02-03 03:41:33 +00:00
@ param cycles_per_line The clock rate at which this CRT will be driven , specified as the number
of cycles expected to take up one whole scanline of the display .
2016-12-10 18:42:34 +00:00
2016-02-28 03:39:01 +00:00
@ param common_output_divisor The greatest a priori common divisor of all cycle counts that will be
supplied to @ c output_sync , @ c output_data , etc ; supply 1 if no greater divisor is known . For many
machines output will run at a fixed multiple of the clock rate ; knowing this divisor can improve
internal precision .
2015-07-23 00:33:20 +00:00
2016-10-13 01:42:36 +00:00
@ param height_of_display The number of lines that nominally form one field of the display , rounded
2016-02-03 03:41:33 +00:00
up to the next whole integer .
2015-07-23 00:33:20 +00:00
2016-02-03 03:41:33 +00:00
@ param colour_cycle_numerator Specifies the numerator for the per - line frequency of the colour subcarrier .
2015-07-23 00:33:20 +00:00
2016-02-03 03:41:33 +00:00
@ 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 .
2015-07-23 00:33:20 +00:00
2016-03-19 21:37:55 +00:00
@ 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
2016-02-03 03:41:33 +00:00
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 .
2015-07-23 00:33:20 +00:00
2016-02-03 03:41:33 +00:00
@ see @ c set_rgb_sampling_function , @ c set_composite_sampling_function
*/
2016-12-10 18:42:34 +00:00
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 , bool should_alternate , unsigned int buffer_depth ) ;
2016-02-03 03:41:33 +00:00
/*! 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
buffers , each with the requested number of bytes per pixel .
Exactly identical to calling the designated constructor with colour subcarrier information
looked up by display type .
*/
2016-03-19 21:37:55 +00:00
CRT ( unsigned int cycles_per_line , unsigned int common_output_divisor , DisplayType displayType , unsigned int buffer_depth ) ;
2015-07-20 01:21:34 +00:00
2016-02-03 03:41:33 +00:00
/*! Resets the CRT with new timing information. The CRT then continues as though the new timing had
been provided at construction . */
2016-12-10 18:42:34 +00:00
void 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 , bool should_alternate ) ;
2016-02-03 03:41:33 +00:00
/*! Resets the CRT with new timing information derived from a new display type. The CRT then continues
as though the new timing had been provided at construction . */
2016-01-23 23:53:20 +00:00
void set_new_display_type ( unsigned int cycles_per_line , DisplayType displayType ) ;
2015-07-31 22:04:33 +00:00
2016-02-03 03:41:33 +00:00
/*! Output at the sync level.
2016-01-22 02:17:47 +00:00
@ param number_of_cycles The amount of time to putput sync for .
*/
2015-08-16 20:08:29 +00:00
void output_sync ( unsigned int number_of_cycles ) ;
2016-01-22 02:17:47 +00:00
2016-02-03 03:41:33 +00:00
/*! Output at the blanking level.
2016-01-22 02:17:47 +00:00
@ param number_of_cycles The amount of time to putput the blanking level for .
*/
2015-08-16 20:08:29 +00:00
void output_blank ( unsigned int number_of_cycles ) ;
2016-01-22 02:17:47 +00:00
2016-02-03 03:41:33 +00:00
/*! Outputs the first written to the most-recently created run of data repeatedly for a prolonged period.
2016-01-22 02:17:47 +00:00
@ param number_of_cycles The number of cycles to repeat the output for .
*/
2015-09-06 00:25:30 +00:00
void output_level ( unsigned int number_of_cycles ) ;
2016-01-22 02:17:47 +00:00
2016-02-03 03:41:33 +00:00
/*! Declares that the caller has created a run of data via @c allocate_write_area and @c get_write_target_for_buffer
2016-01-22 02:17:47 +00:00
that is at least @ c number_of_cycles long , and that the first @ c number_of_cycles / source_divider should be spread
over that amount of time .
@ param number_of_cycles The amount of data to output .
@ param source_divider A divider for source data ; if the divider is 1 then one source pixel is output every cycle ,
if it is 2 then one source pixel covers two cycles ; if it is n then one source pixel covers n cycles .
2016-02-03 03:41:33 +00:00
@ see @ c allocate_write_area , @ c get_write_target_for_buffer
2016-01-22 02:17:47 +00:00
*/
void output_data ( unsigned int number_of_cycles , unsigned int source_divider ) ;
2016-02-03 03:41:33 +00:00
/*! Outputs a colour burst.
2016-01-22 02:17:47 +00:00
@ param number_of_cycles The length of the colour burst .
@ param phase The initial phase of the colour burst in a measuring system with 256 units
per circle , e . g . 0 = 0 degrees , 128 = 180 degrees , 256 = 360 degree .
2016-03-06 01:47:11 +00:00
@ param amplitude The amplitude of the colour burst in 1 / 256 ths of the amplitude of the
2016-01-22 02:17:47 +00:00
positive portion of the wave .
*/
2016-03-06 01:47:11 +00:00
void output_colour_burst ( unsigned int number_of_cycles , uint8_t phase , uint8_t amplitude ) ;
2015-07-19 22:32:42 +00:00
2016-12-10 18:42:34 +00:00
/*! Outputs a colour burst exactly in phase with CRT expectations using the idiomatic amplitude.
@ param number_of_cycles The length of the colour burst ;
*/
void output_default_colour_burst ( unsigned int number_of_cycles ) ;
2016-05-04 11:39:45 +00:00
/*! Attempts to allocate the given number of output samples for writing.
2016-02-03 03:41:33 +00:00
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 .
2016-05-04 11:39:45 +00:00
Allocation should fail only if emulation is running significantly below real speed .
2016-02-03 03:41:33 +00:00
@ param required_length The number of samples to allocate .
2016-05-04 11:39:45 +00:00
@ returns A pointer to the allocated area if room is available ; @ c nullptr otherwise .
2016-02-03 03:41:33 +00:00
*/
2017-03-26 18:34:47 +00:00
inline uint8_t * allocate_write_area ( size_t required_length ) {
2016-12-06 23:48:30 +00:00
std : : unique_lock < std : : mutex > output_lock = openGL_output_builder_ . get_output_lock ( ) ;
2016-11-17 04:26:04 +00:00
return openGL_output_builder_ . texture_builder . allocate_write_area ( required_length ) ;
2016-03-09 03:40:23 +00:00
}
2016-02-03 03:41:33 +00:00
2016-02-04 00:11:18 +00:00
/*! 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 .
2016-01-24 00:06:32 +00:00
*/
2017-03-26 18:34:47 +00:00
inline void draw_frame ( unsigned int output_width , unsigned int output_height , bool only_if_dirty ) {
2017-02-20 02:46:07 +00:00
{
std : : lock_guard < std : : mutex > function_guard ( function_mutex_ ) ;
for ( std : : function < void ( void ) > function : enqueued_openGL_functions_ )
{
function ( ) ;
}
enqueued_openGL_functions_ . clear ( ) ;
}
2016-11-17 03:00:11 +00:00
openGL_output_builder_ . draw_frame ( output_width , output_height , only_if_dirty ) ;
2016-03-09 03:40:23 +00:00
}
2016-01-24 00:06:32 +00:00
2016-02-03 03:41:33 +00:00
/*! Tells the CRT that the next call to draw_frame will occur on a different OpenGL context than
the previous .
2016-01-24 00:06:32 +00:00
2016-02-03 03:41:33 +00:00
@ param should_delete_resources If @ c true then all resources — textures , vertex arrays , etc —
currently held by the CRT will be deleted now via calls to glDeleteTexture and equivalent . If
@ c false then the references are simply marked as invalid .
2016-01-24 00:06:32 +00:00
*/
2017-03-26 18:34:47 +00:00
inline void set_openGL_context_will_change ( bool should_delete_resources ) {
2017-02-20 15:39:31 +00:00
enqueue_openGL_function ( [ should_delete_resources , this ] {
openGL_output_builder_ . set_openGL_context_will_change ( should_delete_resources ) ;
} ) ;
2016-03-09 03:40:23 +00:00
}
2016-01-24 00:06:32 +00:00
2016-02-03 03:41:33 +00:00
/*! Sets a function that will map from whatever data the machine provided to a composite signal.
2016-01-24 00:06:32 +00:00
2016-02-03 03:41:33 +00:00
@ param shader A GLSL fragment including a function with the signature
2016-04-24 10:56:08 +00:00
` float composite_sample ( usampler2D texID , vec2 coordinate , vec2 iCoordinate , float phase , float amplitude ) `
that evaluates to the composite signal level as a function of a source buffer , sampling location , colour
carrier phase and amplitude .
2016-02-03 03:41:33 +00:00
*/
2017-03-26 18:34:47 +00:00
inline void set_composite_sampling_function ( const std : : string & shader ) {
2017-02-20 15:39:31 +00:00
enqueue_openGL_function ( [ shader , this ] {
openGL_output_builder_ . set_composite_sampling_function ( shader ) ;
} ) ;
2016-03-09 03:40:23 +00:00
}
2016-02-03 03:41:33 +00:00
/*! Sets a function that will map from whatever data the machine provided to an RGB signal.
If the output mode is composite then a default mapping from RGB to the display ' s composite
format will be applied .
@ param shader A GLSL fragent including a function with the signature
2016-04-13 02:38:49 +00:00
` vec3 rgb_sample ( usampler2D sampler , vec2 coordinate , vec2 icoordinate ) ` that evaluates to an RGB colour
as a function of :
* ` usampler2D sampler ` representing the source buffer ;
* ` vec2 coordinate ` representing the source buffer location to sample from in the range [ 0 , 1 ) ; and
* ` vec2 icoordinate ` representing the source buffer location to sample from as a pixel count , for easier multiple - pixels - per - byte unpacking .
2016-02-03 03:41:33 +00:00
*/
2017-03-26 18:34:47 +00:00
inline void set_rgb_sampling_function ( const std : : string & shader ) {
2017-02-20 15:39:31 +00:00
enqueue_openGL_function ( [ shader , this ] {
openGL_output_builder_ . set_rgb_sampling_function ( shader ) ;
} ) ;
2016-03-09 03:40:23 +00:00
}
2016-01-24 00:06:32 +00:00
2017-03-26 18:34:47 +00:00
inline void set_output_device ( OutputDevice output_device ) {
2017-02-20 15:39:31 +00:00
enqueue_openGL_function ( [ output_device , this ] {
openGL_output_builder_ . set_output_device ( output_device ) ;
} ) ;
2016-02-20 22:32:51 +00:00
}
2017-03-26 18:34:47 +00:00
inline void set_visible_area ( Rect visible_area ) {
2017-02-20 15:39:31 +00:00
enqueue_openGL_function ( [ visible_area , this ] {
openGL_output_builder_ . set_visible_area ( visible_area ) ;
} ) ;
2016-02-20 22:32:51 +00:00
}
2016-04-12 01:47:23 +00:00
Rect get_rect_for_area ( int first_line_after_sync , int number_of_lines , int first_cycle_after_sync , int number_of_cycles , float aspect_ratio ) ;
2016-04-24 22:36:22 +00:00
2017-03-26 18:34:47 +00:00
inline void set_delegate ( Delegate * delegate ) {
2016-11-17 03:00:11 +00:00
delegate_ = delegate ;
2016-04-24 22:36:22 +00:00
}
2015-07-19 17:36:27 +00:00
} ;
}
2016-03-09 01:49:07 +00:00
}
2015-07-19 17:36:27 +00:00
# endif /* CRT_cpp */