mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-16 18:30:32 +00:00
Advances back to a semi-complete monochrome composite output.
i.e. composite phase and amplitude is ostensibly flowing to its new destination.
This commit is contained in:
parent
ccb52fb625
commit
5d9521fcb9
@ -219,11 +219,8 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_
|
||||
|
||||
// Announce horizontal retrace events.
|
||||
if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) {
|
||||
// Prepare for the next line.
|
||||
if(next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) {
|
||||
is_alernate_line_ ^= phase_alternates_;
|
||||
colour_burst_amplitude_ = 0;
|
||||
} else {
|
||||
// Reset the cycles-since-sync counter if this is the end of retrace.
|
||||
if(next_horizontal_sync_event == Flywheel::SyncEvent::EndRetrace) {
|
||||
cycles_since_horizontal_sync_ = 0;
|
||||
}
|
||||
|
||||
@ -234,7 +231,14 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_
|
||||
scan_target_->announce(
|
||||
event,
|
||||
!(horizontal_flywheel_->is_in_retrace() || vertical_flywheel_->is_in_retrace()),
|
||||
end_point(uint16_t((total_cycles - number_of_cycles) * number_of_samples / total_cycles)));
|
||||
end_point(uint16_t((total_cycles - number_of_cycles) * number_of_samples / total_cycles)),
|
||||
colour_burst_amplitude_);
|
||||
|
||||
// If retrace is starting, update phase if required and mark no colour burst spotted yet.
|
||||
if(next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) {
|
||||
is_alernate_line_ ^= phase_alternates_;
|
||||
colour_burst_amplitude_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Also announce vertical retrace events.
|
||||
@ -245,7 +249,8 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_
|
||||
scan_target_->announce(
|
||||
event,
|
||||
!(horizontal_flywheel_->is_in_retrace() || vertical_flywheel_->is_in_retrace()),
|
||||
end_point(uint16_t((total_cycles - number_of_cycles) * number_of_samples / total_cycles)));
|
||||
end_point(uint16_t((total_cycles - number_of_cycles) * number_of_samples / total_cycles)),
|
||||
colour_burst_amplitude_);
|
||||
}
|
||||
|
||||
// if this is vertical retrace then adcance a field
|
||||
|
@ -222,7 +222,7 @@ void ScanTarget::submit() {
|
||||
allocation_has_failed_ = false;
|
||||
}
|
||||
|
||||
void ScanTarget::announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location) {
|
||||
void ScanTarget::announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location, uint8_t composite_amplitude) {
|
||||
if(event == ScanTarget::Event::EndVerticalRetrace) {
|
||||
is_first_in_frame_ = true;
|
||||
frame_was_complete_ = true;
|
||||
@ -258,13 +258,16 @@ void ScanTarget::announce(Event event, bool is_visible, const Outputs::Display::
|
||||
active_line_->end_points[0].x = location.x;
|
||||
active_line_->end_points[0].y = location.y;
|
||||
active_line_->end_points[0].cycles_since_end_of_horizontal_retrace = location.cycles_since_end_of_horizontal_retrace;
|
||||
active_line_->end_points[0].composite_angle = location.composite_angle;
|
||||
active_line_->line = write_pointers_.line;
|
||||
active_line_->composite_amplitude = composite_amplitude;
|
||||
}
|
||||
} else {
|
||||
if(active_line_) {
|
||||
active_line_->end_points[1].x = location.x;
|
||||
active_line_->end_points[1].y = location.y;
|
||||
active_line_->end_points[1].cycles_since_end_of_horizontal_retrace = location.cycles_since_end_of_horizontal_retrace;
|
||||
active_line_->end_points[1].composite_angle = location.composite_angle;
|
||||
}
|
||||
}
|
||||
output_is_visible_ = is_visible;
|
||||
@ -287,7 +290,7 @@ void ScanTarget::setup_pipeline() {
|
||||
processing_width_ = modals_.cycles_per_line / modals_.clocks_per_pixel_greatest_common_divisor;
|
||||
|
||||
// Establish an output shader. TODO: add proper decoding and gamma correction here.
|
||||
output_shader_ = conversion_shader(modals_.input_data_type, modals_.display_type, modals_.colour_cycle_numerator, modals_.colour_cycle_denominator, processing_width_);
|
||||
output_shader_ = conversion_shader(modals_.input_data_type, modals_.display_type, modals_.composite_colour_space);
|
||||
glBindVertexArray(line_vertex_array_);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, line_buffer_name_);
|
||||
enable_vertex_attributes(ShaderType::Line, *output_shader_);
|
||||
|
@ -53,7 +53,7 @@ class ScanTarget: public Outputs::Display::ScanTarget {
|
||||
uint8_t *begin_data(size_t required_length, size_t required_alignment) override;
|
||||
void end_data(size_t actual_length) override;
|
||||
void submit() override;
|
||||
void announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location) override;
|
||||
void announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location, uint8_t colour_burst_amplitude) override;
|
||||
|
||||
bool output_is_visible_ = false;
|
||||
|
||||
@ -96,8 +96,10 @@ class ScanTarget: public Outputs::Display::ScanTarget {
|
||||
struct EndPoint {
|
||||
uint16_t x, y;
|
||||
uint16_t cycles_since_end_of_horizontal_retrace;
|
||||
int16_t composite_angle;
|
||||
} end_points[2];
|
||||
uint16_t line;
|
||||
uint8_t composite_amplitude;
|
||||
};
|
||||
struct LineMetadata {
|
||||
bool is_first_in_frame;
|
||||
@ -183,7 +185,7 @@ class ScanTarget: public Outputs::Display::ScanTarget {
|
||||
std::unique_ptr<Shader> output_shader_;
|
||||
|
||||
static std::unique_ptr<Shader> composition_shader(InputDataType input_data_type);
|
||||
static std::unique_ptr<Shader> conversion_shader(InputDataType input_data_type, DisplayType display_type, int colour_cycle_numerator, int colour_cycle_denominator, int processing_width);
|
||||
static std::unique_ptr<Shader> conversion_shader(InputDataType input_data_type, DisplayType display_type, ColourSpace colour_space);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -51,9 +51,12 @@ std::string ScanTarget::glsl_globals(ShaderType type) {
|
||||
"in vec2 endPoint;"
|
||||
|
||||
"in float startClock;"
|
||||
"in float startCompositeAngle;"
|
||||
"in float endClock;"
|
||||
"in float endCompositeAngle;"
|
||||
|
||||
"in float lineY;"
|
||||
"in float compositeAmplitude;"
|
||||
|
||||
"uniform sampler2D textureName;"
|
||||
"uniform vec2 origin;"
|
||||
@ -86,6 +89,9 @@ std::vector<Shader::AttributeBinding> ScanTarget::attribute_bindings(ShaderType
|
||||
{"startClock", 2},
|
||||
{"endClock", 3},
|
||||
{"lineY", 4},
|
||||
{"compositeAmplitude", 5},
|
||||
{"startCompositeAngle", 6},
|
||||
{"endCompositeAngle", 7},
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -164,13 +170,19 @@ std::string ScanTarget::glsl_default_vertex_shader(ShaderType type) {
|
||||
|
||||
case ShaderType::Line:
|
||||
return
|
||||
"out vec2 textureCoordinate;"
|
||||
"out vec2 textureCoordinates[4];"
|
||||
|
||||
"out float compositeAngle;"
|
||||
"out float oneOverCompositeAmplitude;"
|
||||
|
||||
"void main(void) {"
|
||||
"float lateral = float(gl_VertexID & 1);"
|
||||
"float longitudinal = float((gl_VertexID & 2) >> 1);"
|
||||
|
||||
"textureCoordinate = vec2(mix(startClock, endClock, lateral), lineY + 0.5) / textureSize(textureName, 0);"
|
||||
"textureCoordinates[0] = vec2(mix(startClock, endClock, lateral), lineY + 0.5) / textureSize(textureName, 0);"
|
||||
|
||||
"compositeAngle = (mix(startCompositeAngle, endCompositeAngle, lateral) / 32.0) * 3.141592654;"
|
||||
"oneOverCompositeAmplitude = mix(0.0, 255.0 / compositeAmplitude, step(0.01, compositeAmplitude));"
|
||||
|
||||
"vec2 centrePoint = mix(startPoint, endPoint, lateral) / scale;"
|
||||
"vec2 height = normalize(endPoint - startPoint).yx * (longitudinal - 0.5) * rowHeight;"
|
||||
@ -250,6 +262,13 @@ void ScanTarget::enable_vertex_attributes(ShaderType type, Shader &target) {
|
||||
sizeof(Line),
|
||||
reinterpret_cast<void *>(offsetof(Line, end_points[c].cycles_since_end_of_horizontal_retrace)),
|
||||
1);
|
||||
|
||||
target.enable_vertex_attribute_with_pointer(
|
||||
prefix + "CompositeAngle",
|
||||
1, GL_UNSIGNED_SHORT, GL_FALSE,
|
||||
sizeof(Line),
|
||||
reinterpret_cast<void *>(offsetof(Line, end_points[c].composite_angle)),
|
||||
1);
|
||||
}
|
||||
|
||||
target.enable_vertex_attribute_with_pointer(
|
||||
@ -258,6 +277,13 @@ void ScanTarget::enable_vertex_attributes(ShaderType type, Shader &target) {
|
||||
sizeof(Line),
|
||||
reinterpret_cast<void *>(offsetof(Line, line)),
|
||||
1);
|
||||
|
||||
target.enable_vertex_attribute_with_pointer(
|
||||
"compositeAmplitude",
|
||||
1, GL_UNSIGNED_BYTE, GL_FALSE,
|
||||
sizeof(Line),
|
||||
reinterpret_cast<void *>(offsetof(Line, composite_amplitude)),
|
||||
1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -420,37 +446,193 @@ std::unique_ptr<Shader> ScanTarget::composition_shader(InputDataType input_data_
|
||||
));
|
||||
}
|
||||
|
||||
std::unique_ptr<Shader> ScanTarget::conversion_shader(InputDataType input_data_type, DisplayType display_type, int colour_cycle_numerator, int colour_cycle_denominator, int processing_width) {
|
||||
return std::unique_ptr<Shader>(new Shader(
|
||||
glsl_globals(ShaderType::Line) + glsl_default_vertex_shader(ShaderType::Line),
|
||||
std::unique_ptr<Shader> ScanTarget::conversion_shader(InputDataType input_data_type, DisplayType display_type, ColourSpace colour_space) {
|
||||
display_type = DisplayType::CompositeMonochrome; // Just a test.
|
||||
|
||||
// Compose a vertex shader. If the display type is RGB, generate just the proper
|
||||
// geometry position, plus a solitary textureCoordinate.
|
||||
//
|
||||
// If the display type is anything other than RGB, also produce composite
|
||||
// angle and 1/composite amplitude as outputs.
|
||||
//
|
||||
// If the display type is composite colour, generate four textureCoordinates,
|
||||
// spanning a range of -135, -45, +45, +135 degrees.
|
||||
//
|
||||
// If the display type is S-Video, generate three textureCoordinates, at
|
||||
// -45, 0, +45.
|
||||
std::string vertex_shader = glsl_globals(ShaderType::Line);
|
||||
std::string fragment_shader =
|
||||
"#version 150\n"
|
||||
|
||||
"out vec4 fragColour;"
|
||||
"in vec2 textureCoordinate;"
|
||||
|
||||
"uniform sampler2D textureName;"
|
||||
"out vec4 fragColour;";
|
||||
|
||||
if(display_type != DisplayType::RGB) {
|
||||
vertex_shader +=
|
||||
"out float compositeAngle;"
|
||||
"out float oneOverCompositeAmplitude;";
|
||||
fragment_shader +=
|
||||
"in float compositeAngle;"
|
||||
"in float oneOverCompositeAmplitude;";
|
||||
}
|
||||
|
||||
switch(display_type){
|
||||
case DisplayType::RGB:
|
||||
case DisplayType::CompositeMonochrome:
|
||||
vertex_shader += "out vec2 textureCoordinate;";
|
||||
fragment_shader += "in vec2 textureCoordinate;";
|
||||
break;
|
||||
|
||||
case DisplayType::CompositeColour:
|
||||
vertex_shader += "out vec2 textureCoordinates[4];";
|
||||
fragment_shader += "in vec2 textureCoordinates[4];";
|
||||
break;
|
||||
|
||||
case DisplayType::SVideo:
|
||||
vertex_shader += "out vec2 textureCoordinates[3];";
|
||||
fragment_shader += "in vec2 textureCoordinates[3];";
|
||||
break;
|
||||
}
|
||||
|
||||
// Add the code to generate a proper output position; this applies to all display types.
|
||||
vertex_shader +=
|
||||
"void main(void) {"
|
||||
"float lateral = float(gl_VertexID & 1);"
|
||||
"float longitudinal = float((gl_VertexID & 2) >> 1);"
|
||||
"vec2 centrePoint = mix(startPoint, endPoint, lateral) / scale;"
|
||||
"vec2 height = normalize(endPoint - startPoint).yx * (longitudinal - 0.5) * rowHeight;"
|
||||
"vec2 eyePosition = vec2(-1.0, 1.0) + vec2(2.0, -2.0) * (((centrePoint + height) - origin) / size);"
|
||||
"gl_Position = vec4(eyePosition, 0.0, 1.0);";
|
||||
|
||||
// For everything other than RGB, calculate the two composite outputs.
|
||||
if(display_type != DisplayType::RGB) {
|
||||
vertex_shader +=
|
||||
"compositeAngle = (mix(startCompositeAngle, endCompositeAngle, lateral) / 32.0) * 3.141592654;"
|
||||
"oneOverCompositeAmplitude = mix(0.0, 255.0 / compositeAmplitude, step(0.01, compositeAmplitude));";
|
||||
}
|
||||
|
||||
// For RGB and monochrome composite, generate the single texture coordinate; otherwise generate either three
|
||||
// or four depending on the type of decoding to apply.
|
||||
switch(display_type){
|
||||
case DisplayType::RGB:
|
||||
case DisplayType::CompositeMonochrome:
|
||||
vertex_shader +=
|
||||
"textureCoordinate = vec2(mix(startClock, endClock, lateral), lineY + 0.5) / textureSize(textureName, 0);";
|
||||
break;
|
||||
|
||||
case DisplayType::CompositeColour:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case DisplayType::SVideo:
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
vertex_shader += "}";
|
||||
|
||||
// Compose a fragment shader.
|
||||
//
|
||||
// For an RGB display ... [TODO]
|
||||
|
||||
if(display_type != DisplayType::RGB) {
|
||||
fragment_shader +=
|
||||
"uniform mat3 lumaChromaToRGB;"
|
||||
"uniform mat3 rgbToLumaChroma;";
|
||||
}
|
||||
|
||||
fragment_shader +=
|
||||
"void main(void) {"
|
||||
"fragColour = vec4(texture(textureName, textureCoordinate).rgb, 0.64);"
|
||||
"}",
|
||||
attribute_bindings(ShaderType::Line)
|
||||
));
|
||||
"vec3 fragColour3;";
|
||||
|
||||
// switch(modals_.composite_colour_space) {
|
||||
// case ColourSpace::YIQ: {
|
||||
// const GLfloat rgbToYIQ[] = {0.299f, 0.596f, 0.211f, 0.587f, -0.274f, -0.523f, 0.114f, -0.322f, 0.312f};
|
||||
// const GLfloat yiqToRGB[] = {1.0f, 1.0f, 1.0f, 0.956f, -0.272f, -1.106f, 0.621f, -0.647f, 1.703f};
|
||||
// shader->set_uniform_matrix("lumaChromaToRGB", 3, false, yiqToRGB);
|
||||
// shader->set_uniform_matrix("rgbToLumaChroma", 3, false, rgbToYIQ);
|
||||
// } break;
|
||||
//
|
||||
// case ColourSpace::YUV: {
|
||||
// const GLfloat rgbToYUV[] = {0.299f, -0.14713f, 0.615f, 0.587f, -0.28886f, -0.51499f, 0.114f, 0.436f, -0.10001f};
|
||||
// const GLfloat yuvToRGB[] = {1.0f, 1.0f, 1.0f, 0.0f, -0.39465f, 2.03211f, 1.13983f, -0.58060f, 0.0f};
|
||||
// shader->set_uniform_matrix("lumaChromaToRGB", 3, false, yuvToRGB);
|
||||
// shader->set_uniform_matrix("rgbToLumaChroma", 3, false, rgbToYUV);
|
||||
// } break;
|
||||
// }
|
||||
switch(display_type) {
|
||||
case DisplayType::RGB:
|
||||
// Easy, just copy across.
|
||||
fragment_shader += "fragColour3 = texture(textureName, textureCoordinate).rgb;";
|
||||
break;
|
||||
|
||||
case DisplayType::SVideo:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case DisplayType::CompositeColour:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case DisplayType::CompositeMonochrome: {
|
||||
switch(input_data_type) {
|
||||
case InputDataType::Luminance1:
|
||||
case InputDataType::Luminance8:
|
||||
// Easy, just copy across.
|
||||
fragment_shader += "fragColour3 = texture(textureName, textureCoordinate).rgb;";
|
||||
break;
|
||||
|
||||
case InputDataType::PhaseLinkedLuminance8:
|
||||
fragment_shader +=
|
||||
"uint iPhase = uint((compositeAngle * 2.0 / 3.141592654) ) & 3u;" // + phaseOffset*4.0
|
||||
"fragColour3 = vec3(texture(textureName, textureCoordinate)[iPhase] / 255.0);";
|
||||
break;
|
||||
|
||||
case InputDataType::Luminance8Phase8:
|
||||
fragment_shader +=
|
||||
"vec2 yc = texture(textureName, textureCoordinate).rg / vec2(255.0);"
|
||||
|
||||
"float phaseOffset = 3.141592654 * 2.0 * 2.0 * yc.y;"
|
||||
"float rawChroma = step(yc.y, 0.75) * cos(compositeAngle + phaseOffset);"
|
||||
"float level = mix(yc.x, yc.y * rawChroma, 1.0 / oneOverCompositeAmplitude);" // TODO: no divide by zero.
|
||||
"fragColour3 = vec3(level);";
|
||||
// "fragColour3 = vec3(yc.x, 0.5 + rawChroma*0.5, 0.0);";
|
||||
break;
|
||||
|
||||
case InputDataType::Red1Green1Blue1:
|
||||
case InputDataType::Red2Green2Blue2:
|
||||
case InputDataType::Red4Green4Blue4:
|
||||
case InputDataType::Red8Green8Blue8:
|
||||
fragment_shader +=
|
||||
"vec3 colour = rgbToLumaChroma * texture(textureName, textureCoordinate).rgb;"
|
||||
"vec2 quadrature = vec2(cos(compositeAngle), sin(compositeAngle));"
|
||||
"float level = mix(colour.r, dot(quadrature, colour.gb), 1.0 / oneOverCompositeAmplitude);" // TODO: no divide by zero.
|
||||
"fragColour3 = vec3(level);";
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
// TODO
|
||||
} break;
|
||||
}
|
||||
|
||||
fragment_shader +=
|
||||
"fragColour = vec4(fragColour3, 0.64);"
|
||||
"}";
|
||||
|
||||
// TODO gamma and range corrections.
|
||||
|
||||
const auto shader = new Shader(
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
attribute_bindings(ShaderType::Line)
|
||||
);
|
||||
|
||||
// If this isn't an RGB or composite colour shader, set the proper colour space.
|
||||
if(display_type != DisplayType::RGB) {
|
||||
switch(colour_space) {
|
||||
case ColourSpace::YIQ: {
|
||||
const GLfloat rgbToYIQ[] = {0.299f, 0.596f, 0.211f, 0.587f, -0.274f, -0.523f, 0.114f, -0.322f, 0.312f};
|
||||
const GLfloat yiqToRGB[] = {1.0f, 1.0f, 1.0f, 0.956f, -0.272f, -1.106f, 0.621f, -0.647f, 1.703f};
|
||||
shader->set_uniform_matrix("lumaChromaToRGB", 3, false, yiqToRGB);
|
||||
shader->set_uniform_matrix("rgbToLumaChroma", 3, false, rgbToYIQ);
|
||||
} break;
|
||||
|
||||
case ColourSpace::YUV: {
|
||||
const GLfloat rgbToYUV[] = {0.299f, -0.14713f, 0.615f, 0.587f, -0.28886f, -0.51499f, 0.114f, 0.436f, -0.10001f};
|
||||
const GLfloat yuvToRGB[] = {1.0f, 1.0f, 1.0f, 0.0f, -0.39465f, 2.03211f, 1.13983f, -0.58060f, 0.0f};
|
||||
shader->set_uniform_matrix("lumaChromaToRGB", 3, false, yuvToRGB);
|
||||
shader->set_uniform_matrix("rgbToLumaChroma", 3, false, rgbToYUV);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<Shader>(shader);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -293,8 +293,9 @@ struct ScanTarget {
|
||||
@param event The event.
|
||||
@param is_visible @c true if the output stream is visible immediately after this event; @c false otherwise.
|
||||
@param location The location of the event.
|
||||
@param composite_amplitude The amplitude of the colour burst on this line (0, if no colour burst was found).
|
||||
*/
|
||||
virtual void announce(Event event, bool is_visible, const Scan::EndPoint &location) {}
|
||||
virtual void announce(Event event, bool is_visible, const Scan::EndPoint &location, uint8_t composite_amplitude) {}
|
||||
};
|
||||
|
||||
/*!
|
||||
|
Loading…
x
Reference in New Issue
Block a user