mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-26 15:32:04 +00:00
It's broken now, but this is what I now intend the full public interface of the CRT to be.
This commit is contained in:
parent
4554abb755
commit
19393390ac
261
Outputs/CRT.hpp
261
Outputs/CRT.hpp
@ -18,67 +18,80 @@
|
||||
|
||||
namespace Outputs {
|
||||
|
||||
class CRT;
|
||||
struct CRTFrameBuilder {
|
||||
CRTFrame frame;
|
||||
|
||||
CRTFrameBuilder(uint16_t width, uint16_t height, unsigned int number_of_buffers, va_list buffer_sizes);
|
||||
~CRTFrameBuilder();
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> _all_runs;
|
||||
|
||||
void reset();
|
||||
void complete();
|
||||
|
||||
uint8_t *get_next_run();
|
||||
friend CRT;
|
||||
|
||||
void allocate_write_area(int required_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;
|
||||
};
|
||||
|
||||
static const int kCRTNumberOfFrames = 4;
|
||||
|
||||
class CRT {
|
||||
public:
|
||||
~CRT();
|
||||
|
||||
enum DisplayType {
|
||||
PAL50,
|
||||
NTSC60
|
||||
};
|
||||
|
||||
CRT(unsigned int cycles_per_line, unsigned int height_of_display, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int number_of_buffers, ...);
|
||||
CRT(unsigned int cycles_per_line, DisplayType displayType, unsigned int number_of_buffers, ...);
|
||||
~CRT();
|
||||
/*! 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.
|
||||
|
||||
@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.
|
||||
|
||||
@param height_of_dispaly The number of lines that nominally form one field of the display, rounded
|
||||
up to the next whole integer.
|
||||
|
||||
@param colour_cycle_numerator Specifies the numerator for the per-line frequency of the colour subcarrier.
|
||||
|
||||
@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
|
||||
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 height_of_display, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int number_of_buffers, ...);
|
||||
|
||||
/*! 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.
|
||||
*/
|
||||
CRT(unsigned int cycles_per_line, DisplayType displayType, unsigned int number_of_buffers, ...);
|
||||
|
||||
/*! Resets the CRT with new timing information. The CRT then continues as though the new timing had
|
||||
been provided at construction. */
|
||||
void set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator);
|
||||
|
||||
/*! 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. */
|
||||
void set_new_display_type(unsigned int cycles_per_line, DisplayType displayType);
|
||||
|
||||
/*! Output at the sync level.
|
||||
/*! Output at the sync level.
|
||||
|
||||
@param number_of_cycles The amount of time to putput sync for.
|
||||
*/
|
||||
void output_sync(unsigned int number_of_cycles);
|
||||
|
||||
/*! Output at the blanking level.
|
||||
/*! Output at the blanking level.
|
||||
|
||||
@param number_of_cycles The amount of time to putput the blanking level for.
|
||||
*/
|
||||
void output_blank(unsigned int number_of_cycles);
|
||||
|
||||
/*! Outputs the first written to the most-recently created run of data repeatedly for a prolonged period.
|
||||
/*! Outputs the first written to the most-recently created run of data repeatedly for a prolonged period.
|
||||
|
||||
@param number_of_cycles The number of cycles to repeat the output for.
|
||||
*/
|
||||
void output_level(unsigned int number_of_cycles);
|
||||
|
||||
/*! Declares that the caller has created a run of data via @c allocate_write_area and @c get_write_target_for_buffer
|
||||
/*! Declares that the caller has created a run of data via @c allocate_write_area and @c get_write_target_for_buffer
|
||||
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.
|
||||
|
||||
@ -86,10 +99,12 @@ class CRT {
|
||||
|
||||
@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.
|
||||
|
||||
@see @c allocate_write_area , @c get_write_target_for_buffer
|
||||
*/
|
||||
void output_data(unsigned int number_of_cycles, unsigned int source_divider);
|
||||
|
||||
/*! Outputs a colour burst.
|
||||
/*! Outputs a colour burst.
|
||||
|
||||
@param number_of_cycles The length of the colour burst.
|
||||
|
||||
@ -101,43 +116,101 @@ class CRT {
|
||||
*/
|
||||
void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t magnitude);
|
||||
|
||||
class Delegate {
|
||||
public:
|
||||
virtual void crt_did_end_frame(CRT *crt, CRTFrame *frame, bool did_detect_vsync) = 0;
|
||||
};
|
||||
void set_delegate(Delegate *delegate);
|
||||
void return_frame();
|
||||
/*! 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.
|
||||
*/
|
||||
void allocate_write_area(int 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.
|
||||
*/
|
||||
uint8_t *get_write_target_for_buffer(int buffer);
|
||||
|
||||
/*! Gets the vertex shader for display of vended CRTFrames.
|
||||
// MARK: Binding
|
||||
class Delegate {
|
||||
public:
|
||||
/*! Notifies the delegate that a new frame is complete, providing an ID for it.
|
||||
|
||||
@returns A vertex shader, allocated using a C function. The caller then owns the memory
|
||||
and is responsible for free'ing it.
|
||||
The delegate then owns that frame. It can draw it by issuing an @c draw_frame
|
||||
call to the CRT. It should call @c return_frame to return the oldest frame it
|
||||
is currently holding.
|
||||
|
||||
@param crt The CRT that produced the new frame.
|
||||
@param frame_id An identifier for the finished frame, used for calls to @c draw_frame.
|
||||
@param did_detect_vsync @c true if this frame ended due to a vsync signal detected in
|
||||
the incoming signal; @c false otherwise.
|
||||
*/
|
||||
virtual void crt_did_end_frame(CRT *crt, int frame_id, bool did_detect_vsync) = 0;
|
||||
};
|
||||
|
||||
/*! Sets the CRT frame delegate. The delegate will be notified as frames are completed; it is
|
||||
responsible for requesting that they be drawn and returning them when they are no longer needed.
|
||||
|
||||
@param delegate The delegate.
|
||||
*/
|
||||
char *get_vertex_shader();
|
||||
void set_delegate(std::weak_ptr<Delegate> delegate);
|
||||
|
||||
/*! Gets a fragment shader for display of vended CRTFrames based on the supplied sampling function.
|
||||
/*! Causes appropriate OpenGL or OpenGL ES calls to be made in order to draw a frame. The caller
|
||||
is responsible for ensuring that a valid OpenGL context exists for the duration of this call.
|
||||
|
||||
@param sample_function A GLSL fragment including a function with the signature
|
||||
`float sample(vec2 coordinate, float phase)` that evaluates to the composite signal level
|
||||
as a function of a source buffer sampling location and the current colour carrier phase.
|
||||
|
||||
@returns A complete fragment shader.
|
||||
@param frame_id The frame to draw.
|
||||
*/
|
||||
char *get_fragment_shader(const char *sample_function);
|
||||
void draw_frame(int frame_id);
|
||||
|
||||
/*! Gets a fragment shader for composite display of vended CRTFrames based on a default encoding
|
||||
of the supplied sampling function.
|
||||
/*! Indicates that the delegate has no further interest in the oldest frame posted to it. */
|
||||
void return_frame();
|
||||
|
||||
@param sample_function A GLSL fragent including a function with the signature
|
||||
/*! Tells the CRT that the next call to draw_frame will occur on a different OpenGL context than
|
||||
the previous.
|
||||
|
||||
@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.
|
||||
*/
|
||||
void set_openGL_context_will_change(bool should_delete_resources);
|
||||
|
||||
/*! Sets a function that will map from whatever data the machine provided to a composite signal.
|
||||
|
||||
@param shader A GLSL fragment including a function with the signature
|
||||
`float composite_sample(vec2 coordinate, float phase)` that evaluates to the composite signal
|
||||
level as a function of a source buffer sampling location and the provided colour carrier phase.
|
||||
The shader may assume a uniform array of sampler2Ds named `buffers` provides access to all input data.
|
||||
*/
|
||||
void set_composite_sampling_function(const char *shader);
|
||||
|
||||
/*! 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
|
||||
`vec3 rgb_sample(vec2 coordinate)` that evaluates to an RGB colour as a function of
|
||||
the source buffer sampling location.
|
||||
|
||||
@returns A complete fragment shader.
|
||||
The shader may assume a uniform array of sampler2Ds named `buffers` provides access to all input data.
|
||||
*/
|
||||
char *get_rgb_encoding_fragment_shader(const char *sample_function);
|
||||
void set_rgb_sampling_function(const char *shader);
|
||||
|
||||
/*! Optionally sets a function that will map from an input cycle count to a colour carrier phase.
|
||||
|
||||
If this function is not supplied then the colour phase is determined from
|
||||
the input clock rate and the the colour cycle clock rate. Machines whose per-line clock rate
|
||||
is not intended exactly to match the normal line time may prefer to supply a custom function.
|
||||
|
||||
@param A GLSL fragent including a function with the signature
|
||||
`float phase_for_clock_cycle(int cycle)` that returns the colour phase at the beginning of
|
||||
the supplied cycle.
|
||||
*/
|
||||
void set_phase_function(const char *shader);
|
||||
|
||||
private:
|
||||
CRT();
|
||||
@ -159,13 +232,6 @@ class CRT {
|
||||
uint32_t x, y;
|
||||
} _rasterPosition, _scanSpeed[4], _beamWidth[4];
|
||||
|
||||
// the run delegate and the triple buffer
|
||||
CRTFrameBuilder *_frame_builders[kCRTNumberOfFrames];
|
||||
CRTFrameBuilder *_current_frame_builder;
|
||||
int _frames_with_delegate;
|
||||
int _frame_read_pointer;
|
||||
Delegate *_delegate;
|
||||
|
||||
// outer elements of sync separation
|
||||
bool _is_receiving_sync; // true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync)
|
||||
bool _did_detect_hsync; // true if horizontal sync was detected during this scanline (so, this affects flywheel adjustments)
|
||||
@ -212,6 +278,69 @@ class CRT {
|
||||
} _scans[2];
|
||||
int _next_scan;
|
||||
void output_scan();
|
||||
|
||||
// MARK: shader storage and information.
|
||||
/*! Gets the vertex shader for display of vended CRTFrames.
|
||||
|
||||
@returns A vertex shader, allocated using a C function. The caller then owns the memory
|
||||
and is responsible for free'ing it.
|
||||
*/
|
||||
char *get_vertex_shader();
|
||||
|
||||
/*! Gets a fragment shader for display of vended CRTFrames based on the supplied sampling function.
|
||||
|
||||
@param sample_function A GLSL fragment including a function with the signature
|
||||
`float sample(vec2 coordinate, float phase)` that evaluates to the composite signal level
|
||||
as a function of a source buffer sampling location and the current colour carrier phase.
|
||||
|
||||
@returns A complete fragment shader.
|
||||
*/
|
||||
char *get_fragment_shader(const char *sample_function);
|
||||
|
||||
/*! Gets a fragment shader for composite display of vended CRTFrames based on a default encoding
|
||||
of the supplied sampling function.
|
||||
|
||||
@param sample_function A GLSL fragent including a function with the signature
|
||||
`vec3 rgb_sample(vec2 coordinate)` that evaluates to an RGB colour as a function of
|
||||
the source buffer sampling location.
|
||||
|
||||
@returns A complete fragment shader.
|
||||
*/
|
||||
char *get_rgb_encoding_fragment_shader(const char *sample_function);
|
||||
|
||||
struct CRTFrameBuilder {
|
||||
CRTFrame frame;
|
||||
|
||||
CRTFrameBuilder(uint16_t width, uint16_t height, unsigned int number_of_buffers, va_list buffer_sizes);
|
||||
~CRTFrameBuilder();
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> _all_runs;
|
||||
|
||||
void reset();
|
||||
void complete();
|
||||
|
||||
uint8_t *get_next_run();
|
||||
friend CRT;
|
||||
|
||||
void allocate_write_area(int required_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;
|
||||
};
|
||||
|
||||
static const int kCRTNumberOfFrames = 4;
|
||||
|
||||
// the run delegate and the triple buffer
|
||||
CRTFrameBuilder *_frame_builders[kCRTNumberOfFrames];
|
||||
CRTFrameBuilder *_current_frame_builder;
|
||||
int _frames_with_delegate;
|
||||
int _frame_read_pointer;
|
||||
Delegate *_delegate;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user