1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-26 10:29:31 +00:00

Made a decision to clean up by creating per-shader classes. Well, more or less. First thing to be factored out: the output shader.

This commit is contained in:
Thomas Harte 2016-04-27 22:29:54 -04:00
parent f6d58f1ce7
commit 496d979aca
9 changed files with 206 additions and 164 deletions

View File

@ -305,8 +305,9 @@
4BB73EC21B587A5100552FC2 /* Clock_SignalUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */; };
4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99081C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp */; };
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */; };
4BBF99171C8FBA6F0075DAFB /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99101C8FBA6F0075DAFB /* Shader.cpp */; };
4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; };
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; };
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; };
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; };
4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; };
4BCB70B41C947DDC005B1712 /* plus1.rom in Resources */ = {isa = PBXBuildFile; fileRef = 4BCB70B31C947DDC005B1712 /* plus1.rom */; };
@ -661,11 +662,13 @@
4BBF990B1C8FBA6F0075DAFB /* CRTOpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTOpenGL.hpp; sourceTree = "<group>"; };
4BBF990E1C8FBA6F0075DAFB /* Flywheel.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Flywheel.hpp; sourceTree = "<group>"; };
4BBF990F1C8FBA6F0075DAFB /* OpenGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OpenGL.hpp; sourceTree = "<group>"; };
4BBF99101C8FBA6F0075DAFB /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = "<group>"; };
4BBF99111C8FBA6F0075DAFB /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = "<group>"; };
4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureTarget.cpp; sourceTree = "<group>"; };
4BBF99131C8FBA6F0075DAFB /* TextureTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureTarget.hpp; sourceTree = "<group>"; };
4BBF99191C8FC2750075DAFB /* CRTTypes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CRTTypes.hpp; sourceTree = "<group>"; };
4BC3B74D1CD194CC00F86E85 /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = "<group>"; };
4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = "<group>"; };
4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = "<group>"; };
4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = "<group>"; };
4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = "<group>"; };
4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = "<group>"; };
4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
@ -1203,14 +1206,13 @@
4BBF99071C8FBA6F0075DAFB /* Internals */ = {
isa = PBXGroup;
children = (
4BC3B74C1CD194CC00F86E85 /* Shaders */,
4BBF99081C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp */,
4BBF99091C8FBA6F0075DAFB /* CRTInputBufferBuilder.hpp */,
4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */,
4BBF990B1C8FBA6F0075DAFB /* CRTOpenGL.hpp */,
4BBF990E1C8FBA6F0075DAFB /* Flywheel.hpp */,
4BBF990F1C8FBA6F0075DAFB /* OpenGL.hpp */,
4BBF99101C8FBA6F0075DAFB /* Shader.cpp */,
4BBF99111C8FBA6F0075DAFB /* Shader.hpp */,
4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */,
4BBF99131C8FBA6F0075DAFB /* TextureTarget.hpp */,
4B0B6E121C9DBD5D00FFB60D /* CRTConstants.hpp */,
@ -1218,6 +1220,17 @@
path = Internals;
sourceTree = "<group>";
};
4BC3B74C1CD194CC00F86E85 /* Shaders */ = {
isa = PBXGroup;
children = (
4BC3B74D1CD194CC00F86E85 /* Shader.cpp */,
4BC3B74E1CD194CC00F86E85 /* Shader.hpp */,
4BC3B7501CD1956900F86E85 /* OutputShader.cpp */,
4BC3B7511CD1956900F86E85 /* OutputShader.hpp */,
);
path = Shaders;
sourceTree = "<group>";
};
4BE5F85A1C3E1C2500C43F01 /* Resources */ = {
isa = PBXGroup;
children = (
@ -1647,6 +1660,7 @@
4B55CE541C3B7ABF0093A61B /* CSElectron.mm in Sources */,
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */,
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */,
4B55CE4B1C3B3B0C0093A61B /* CSAtari2600.mm in Sources */,
4B55CE581C3B7D360093A61B /* Atari2600Document.swift in Sources */,
4B0EBFB81C487F2F00A11F35 /* AudioQueue.m in Sources */,
@ -1658,10 +1672,10 @@
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */,
4B55CE4E1C3B3BDA0093A61B /* CSMachine.mm in Sources */,
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
4BBF99171C8FBA6F0075DAFB /* Shader.cpp in Sources */,
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */,
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */,
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */,
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */,
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */,
);

View File

@ -100,8 +100,6 @@ Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested,
#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_frame_id(v) next_run[OutputVertexSize*v + OutputVertexOffsetOfFrameID]
#define output_timestamp(v) (*(uint32_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfTimestamp])
#define source_input_position_x(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfInputPosition + 0])
#define source_input_position_y(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfInputPosition + 2])
@ -269,7 +267,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
#undef output_tex_x
#undef output_tex_y
#undef output_lateral
#undef output_timestamp
#undef input_input_position_x
#undef input_input_position_y

View File

@ -19,11 +19,9 @@ namespace CRT {
// or is one of the intermediate buffers that we've used to convert from composite towards RGB.
const GLsizei OutputVertexOffsetOfPosition = 0;
const GLsizei OutputVertexOffsetOfTexCoord = 4;
const GLsizei OutputVertexOffsetOfTimestamp = 8;
const GLsizei OutputVertexOffsetOfLateral = 12;
const GLsizei OutputVertexOffsetOfFrameID = 13;
const GLsizei OutputVertexOffsetOfLateral = 8;
const GLsizei OutputVertexSize = 16;
const GLsizei OutputVertexSize = 12;
// 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
@ -43,7 +41,7 @@ const GLsizei IntermediateBufferWidth = 2048;
const GLsizei IntermediateBufferHeight = 2048;
// Some internal buffer sizes
const GLsizeiptr OutputVertexBufferDataSize = 262080; // a multiple of 6 * OutputVertexSize
const GLsizeiptr OutputVertexBufferDataSize = 89856; // a multiple of 6 * OutputVertexSize
const GLsizeiptr SourceVertexBufferDataSize = 87360; // a multiple of 2 * SourceVertexSize

View File

@ -11,6 +11,7 @@
#include "CRTOpenGL.hpp"
#include "../../../SignalProcessing/FIRFilter.hpp"
#include "Shaders/OutputShader.hpp"
static const GLint internalFormatForDepth(size_t depth)
{
@ -310,7 +311,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
_output_mutex->unlock();
}
void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::Shader *const shader)
void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::OutputShader *const shader)
{
if(shader)
{
@ -331,10 +332,9 @@ void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsign
// Ensure we're back on the output framebuffer, drawing from the output array buffer
glBindVertexArray(output_vertex_array);
shader->bind();
// update uniforms
push_size_uniforms(output_width, output_height);
// update uniforms (implicitly binding the shader)
shader->set_output_size(output_width, output_height, _visible_area);
// draw
for(int c = 0; c < number_of_drawing_zones; c++)
@ -349,28 +349,6 @@ void OpenGLOutputBuilder::set_openGL_context_will_change(bool should_delete_reso
{
}
void OpenGLOutputBuilder::push_size_uniforms(unsigned int output_width, unsigned int output_height)
{
if(windowSizeUniform >= 0)
{
glUniform2f(windowSizeUniform, output_width, output_height);
}
GLfloat outputAspectRatioMultiplier = ((float)output_width / (float)output_height) / (4.0f / 3.0f);
Rect _aspect_ratio_corrected_bounds = _visible_area;
GLfloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * _visible_area.size.width;
_aspect_ratio_corrected_bounds.origin.x -= bonusWidth * 0.5f * _aspect_ratio_corrected_bounds.size.width;
_aspect_ratio_corrected_bounds.size.width *= outputAspectRatioMultiplier;
if(boundsOriginUniform >= 0)
glUniform2f(boundsOriginUniform, (GLfloat)_aspect_ratio_corrected_bounds.origin.x, (GLfloat)_aspect_ratio_corrected_bounds.origin.y);
if(boundsSizeUniform >= 0)
glUniform2f(boundsSizeUniform, (GLfloat)_aspect_ratio_corrected_bounds.size.width, (GLfloat)_aspect_ratio_corrected_bounds.size.height);
}
void OpenGLOutputBuilder::set_composite_sampling_function(const char *shader)
{
_composite_shader = strdup(shader);
@ -602,72 +580,6 @@ char *OpenGLOutputBuilder::get_chrominance_filter_fragment_shader()
#pragma mark - Intermediate vertex shaders (i.e. from intermediate line layout to intermediate line layout)
#pragma mark - Output vertex shader
char *OpenGLOutputBuilder::get_output_vertex_shader(const char *header)
{
// the main job of the vertex shader is just to map from an input area of [0,1]x[0,1], with the origin in the
// top left to OpenGL's [-1,1]x[-1,1] with the origin in the lower left, and to convert input data coordinates
// from integral to floating point.
char *result;
asprintf(&result,
"#version 150\n"
"in vec2 position;"
"in vec2 srcCoordinates;"
"in vec2 lateralAndTimestampBaseOffset;"
"in float timestamp;"
"uniform vec2 boundsOrigin;"
"uniform vec2 boundsSize;"
"out float lateralVarying;"
// "out vec2 shadowMaskCoordinates;"
"out float alpha;"
"uniform vec4 timestampBase;"
"uniform float ticksPerFrame;"
"uniform vec2 positionConversion;"
"uniform vec2 scanNormal;"
"\n%s\n"
// "uniform sampler2D shadowMaskTexID;"
// "const float shadowMaskMultiple = 600;"
"out vec2 srcCoordinatesVarying;"
"out vec2 iSrcCoordinatesVarying;"
"void main(void)"
"{"
"lateralVarying = lateralAndTimestampBaseOffset.x + 1.0707963267949;"
// "shadowMaskCoordinates = position * vec2(shadowMaskMultiple, shadowMaskMultiple * 0.85057471264368);"
"ivec2 textureSize = textureSize(texID, 0);"
"iSrcCoordinatesVarying = srcCoordinates;"
"srcCoordinatesVarying = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);"
"float age = (timestampBase[int(lateralAndTimestampBaseOffset.y)] - timestamp) / ticksPerFrame;"
"alpha = 0.6;"//15.0*exp(-age*3.0);"
"vec2 floatingPosition = (position / positionConversion) + lateralAndTimestampBaseOffset.x * scanNormal;"
"vec2 mappedPosition = (floatingPosition - boundsOrigin) / boundsSize;"
"gl_Position = vec4(mappedPosition.x * 2.0 - 1.0, 1.0 - mappedPosition.y * 2.0, 0.0, 1.0);"
"}", header);
return result;
}
char *OpenGLOutputBuilder::get_rgb_output_vertex_shader()
{
return get_output_vertex_shader("uniform usampler2D texID;");
}
char *OpenGLOutputBuilder::get_composite_output_vertex_shader()
{
return get_output_vertex_shader("uniform sampler2D texID;");
}
#pragma mark - Output fragment shaders; RGB and from composite
char *OpenGLOutputBuilder::get_rgb_output_fragment_shader()
@ -703,20 +615,17 @@ char *OpenGLOutputBuilder::get_output_fragment_shader(const char *sampling_funct
"#version 150\n"
"in float lateralVarying;"
"in float alpha;"
// "in vec2 shadowMaskCoordinates;"
"in vec2 srcCoordinatesVarying;"
"in vec2 iSrcCoordinatesVarying;"
"out vec4 fragColour;"
// "uniform sampler2D shadowMaskTexID;",
"%s\n"
"%s\n"
"void main(void)"
"{"
"\n%s\n"
"fragColour = vec4(colour, clamp(alpha, 0.0, 1.0)*sin(lateralVarying));"
"fragColour = vec4(colour, 0.5*cos(lateralVarying));"
"}",
header, sampling_function, fragColour_function);
@ -786,46 +695,24 @@ void OpenGLOutputBuilder::prepare_source_vertex_array()
}
}
std::unique_ptr<OpenGL::Shader> OpenGLOutputBuilder::prepare_output_shader(char *vertex_shader, char *fragment_shader, GLint source_texture_unit)
std::unique_ptr<OpenGL::OutputShader> OpenGLOutputBuilder::prepare_output_shader(char *fragment_shader, bool use_usampler, GLenum source_texture_unit)
{
std::unique_ptr<OpenGL::Shader> shader_program;
std::unique_ptr<OpenGL::OutputShader> shader_program;
if(vertex_shader && fragment_shader)
{
OpenGL::Shader::AttributeBinding bindings[] =
{
{"position", 0},
{"srcCoordinates", 1},
{"lateralAndTimestampBaseOffset", 2},
{"timestamp", 3},
{nullptr}
};
shader_program = std::unique_ptr<OpenGL::Shader>(new OpenGL::Shader(vertex_shader, fragment_shader, bindings));
shader_program->bind();
windowSizeUniform = shader_program->get_uniform_location("windowSize");
boundsSizeUniform = shader_program->get_uniform_location("boundsSize");
boundsOriginUniform = shader_program->get_uniform_location("boundsOrigin");
timestampBaseUniform = shader_program->get_uniform_location("timestampBase");
GLint texIDUniform = shader_program->get_uniform_location("texID");
glUniform1i(texIDUniform, source_texture_unit - GL_TEXTURE0);
}
free(vertex_shader);
free(fragment_shader);
shader_program = OpenGL::OutputShader::make_shader(fragment_shader, use_usampler);
shader_program->set_source_texture_unit(source_texture_unit);
return shader_program;
}
void OpenGLOutputBuilder::prepare_rgb_output_shader()
{
rgb_shader_program = prepare_output_shader(get_rgb_output_vertex_shader(), get_rgb_output_fragment_shader(), source_data_texture_unit);
rgb_shader_program = prepare_output_shader(get_rgb_output_fragment_shader(), true, source_data_texture_unit);
}
void OpenGLOutputBuilder::prepare_composite_output_shader()
{
composite_output_shader_program = prepare_output_shader(get_composite_output_vertex_shader(), get_composite_output_fragment_shader(), filtered_texture_unit);
composite_output_shader_program = prepare_output_shader(get_composite_output_fragment_shader(), false, filtered_texture_unit);
}
void OpenGLOutputBuilder::prepare_output_vertex_array()
@ -834,22 +721,19 @@ void OpenGLOutputBuilder::prepare_output_vertex_array()
{
GLint positionAttribute = rgb_shader_program->get_attrib_location("position");
GLint textureCoordinatesAttribute = rgb_shader_program->get_attrib_location("srcCoordinates");
GLint lateralAttribute = rgb_shader_program->get_attrib_location("lateralAndTimestampBaseOffset");
GLint timestampAttribute = rgb_shader_program->get_attrib_location("timestamp");
GLint lateralAttribute = rgb_shader_program->get_attrib_location("lateral");
glBindVertexArray(output_vertex_array);
glEnableVertexAttribArray((GLuint)positionAttribute);
glEnableVertexAttribArray((GLuint)textureCoordinatesAttribute);
glEnableVertexAttribArray((GLuint)lateralAttribute);
glEnableVertexAttribArray((GLuint)timestampAttribute);
const GLsizei vertexStride = OutputVertexSize;
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
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, 2, GL_UNSIGNED_BYTE, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfLateral);
glVertexAttribPointer((GLuint)lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfLateral);
}
}
@ -947,7 +831,7 @@ void OpenGLOutputBuilder::set_timing_uniforms()
extends = true;
}
OpenGL::Shader *output_shaders[] = {
OpenGL::OutputShader *output_shaders[] = {
rgb_shader_program.get(),
composite_output_shader_program.get()
};
@ -955,21 +839,7 @@ void OpenGLOutputBuilder::set_timing_uniforms()
{
if(output_shaders[c])
{
output_shaders[c]->bind();
GLint ticksPerFrameUniform = output_shaders[c]->get_uniform_location("ticksPerFrame");
GLint scanNormalUniform = output_shaders[c]->get_uniform_location("scanNormal");
GLint positionConversionUniform = output_shaders[c]->get_uniform_location("positionConversion");
glUniform1f(ticksPerFrameUniform, (GLfloat)(_cycles_per_line * _height_of_display));
float scan_angle = atan2f(1.0f / (float)_height_of_display, 1.0f);
float scan_normal[] = { -sinf(scan_angle), cosf(scan_angle)};
float multiplier = (float)_cycles_per_line / ((float)_height_of_display * (float)_horizontal_scan_period);
scan_normal[0] *= multiplier;
scan_normal[1] *= multiplier;
glUniform2f(scanNormalUniform, scan_normal[0], scan_normal[1]);
glUniform2f(positionConversionUniform, _horizontal_scan_period, _vertical_scan_period / (unsigned int)_vertical_period_divider);
output_shaders[c]->set_timing(_height_of_display, _cycles_per_line, _horizontal_scan_period, _vertical_scan_period, _vertical_period_divider);
}
}

View File

@ -16,6 +16,8 @@
#include "Shader.hpp"
#include "CRTInputBufferBuilder.hpp"
#include "Shaders/OutputShader.hpp"
#include <mutex>
namespace Outputs {
@ -46,14 +48,13 @@ class OpenGLOutputBuilder {
// Methods used by the OpenGL code
void prepare_rgb_output_shader();
void prepare_composite_output_shader();
std::unique_ptr<OpenGL::Shader> prepare_output_shader(char *vertex_shader, char *fragment_shader, GLint source_texture_unit);
std::unique_ptr<OpenGL::OutputShader> prepare_output_shader(char *fragment_shader, bool use_usampler, GLenum source_texture_unit);
void prepare_composite_input_shader();
std::unique_ptr<OpenGL::Shader> prepare_intermediate_shader(const char *input_position, const char *header, char *fragment_shader, GLenum texture_unit, bool extends);
void prepare_output_vertex_array();
void prepare_source_vertex_array();
void push_size_uniforms(unsigned int output_width, unsigned int output_height);
// the run and input data buffers
std::unique_ptr<CRTInputBufferBuilder> _buffer_builder;
@ -76,8 +77,8 @@ class OpenGLOutputBuilder {
char *get_y_filter_fragment_shader();
char *get_chrominance_filter_fragment_shader();
std::unique_ptr<OpenGL::Shader> rgb_shader_program;
std::unique_ptr<OpenGL::Shader> composite_input_shader_program, composite_y_filter_shader_program, composite_chrominance_filter_shader_program, composite_output_shader_program;
std::unique_ptr<OpenGL::OutputShader> rgb_shader_program, composite_output_shader_program;
std::unique_ptr<OpenGL::Shader> composite_input_shader_program, composite_y_filter_shader_program, composite_chrominance_filter_shader_program;
GLuint output_array_buffer, output_vertex_array;
GLuint source_array_buffer, source_vertex_array;
@ -93,7 +94,7 @@ class OpenGLOutputBuilder {
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
void perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::Shader *const shader);
void perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::OutputShader *const shader);
void set_timing_uniforms();
void set_colour_space_uniforms();

View File

@ -0,0 +1,103 @@
//
// OutputShader.cpp
// Clock Signal
//
// Created by Thomas Harte on 27/04/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "OutputShader.hpp"
#include <stdlib.h>
#include <math.h>
using namespace OpenGL;
namespace {
const OpenGL::Shader::AttributeBinding bindings[] =
{
{"position", 0},
{"srcCoordinates", 1},
{"lateral", 2},
{nullptr}
};
}
std::unique_ptr<OutputShader> OutputShader::make_shader(const char *fragment_shader, bool use_usampler)
{
char *vertex_shader;
asprintf(&vertex_shader,
"#version 150\n"
"in vec2 position;"
"in vec2 srcCoordinates;"
"in float lateral;"
"uniform vec2 boundsOrigin;"
"uniform vec2 boundsSize;"
"uniform vec2 positionConversion;"
"uniform vec2 scanNormal;"
"uniform %s texID;"
"out float lateralVarying;"
"out vec2 srcCoordinatesVarying;"
"out vec2 iSrcCoordinatesVarying;"
"void main(void)"
"{"
"lateralVarying = lateral - 0.5;"
"ivec2 textureSize = textureSize(texID, 0);"
"iSrcCoordinatesVarying = srcCoordinates;"
"srcCoordinatesVarying = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);"
"vec2 floatingPosition = (position / positionConversion) + lateral * scanNormal;"
"vec2 mappedPosition = (floatingPosition - boundsOrigin) / boundsSize;"
"gl_Position = vec4(mappedPosition.x * 2.0 - 1.0, 1.0 - mappedPosition.y * 2.0, 0.0, 1.0);"
"}", use_usampler ? "usampler2D" : "sampler2D");
std::unique_ptr<OutputShader> result = std::unique_ptr<OutputShader>(new OutputShader(vertex_shader, fragment_shader, bindings));
free(vertex_shader);
result->boundsSizeUniform = result->get_uniform_location("boundsSize");
result->boundsOriginUniform = result->get_uniform_location("boundsOrigin");
result->texIDUniform = result->get_uniform_location("texID");
result->scanNormalUniform = result->get_uniform_location("scanNormal");
result->positionConversionUniform = result->get_uniform_location("positionConversion");
return result;
}
void OutputShader::set_output_size(unsigned int output_width, unsigned int output_height, Outputs::CRT::Rect visible_area)
{
bind();
GLfloat outputAspectRatioMultiplier = ((float)output_width / (float)output_height) / (4.0f / 3.0f);
GLfloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * visible_area.size.width;
visible_area.origin.x -= bonusWidth * 0.5f * visible_area.size.width;
visible_area.size.width *= outputAspectRatioMultiplier;
glUniform2f(boundsOriginUniform, (GLfloat)visible_area.origin.x, (GLfloat)visible_area.origin.y);
glUniform2f(boundsSizeUniform, (GLfloat)visible_area.size.width, (GLfloat)visible_area.size.height);
}
void OutputShader::set_source_texture_unit(GLenum unit)
{
bind();
glUniform1i(texIDUniform, (GLint)(unit - GL_TEXTURE0));
}
void OutputShader::set_timing(unsigned int height_of_display, unsigned int cycles_per_line, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider)
{
bind();
float scan_angle = atan2f(1.0f / (float)height_of_display, 1.0f);
float scan_normal[] = { -sinf(scan_angle), cosf(scan_angle)};
float multiplier = (float)cycles_per_line / ((float)height_of_display * (float)horizontal_scan_period);
scan_normal[0] *= multiplier;
scan_normal[1] *= multiplier;
glUniform2f(scanNormalUniform, scan_normal[0], scan_normal[1]);
glUniform2f(positionConversionUniform, horizontal_scan_period, vertical_scan_period / (unsigned int)vertical_period_divider);
}

View File

@ -0,0 +1,59 @@
//
// OutputShader.hpp
// Clock Signal
//
// Created by Thomas Harte on 27/04/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef OutputShader_hpp
#define OutputShader_hpp
#include "Shader.hpp"
#include "../../CRTTypes.hpp"
#include <memory>
namespace OpenGL {
class OutputShader: public Shader {
public:
/*!
Constructs and returns an instance of OutputShader. OutputShaders are intended to read source data
from a texture and draw a single raster scan containing that data as output.
The fragment shader should expect to receive the inputs:
in float lateralVarying;
in vec2 srcCoordinatesVarying;
in vec2 iSrcCoordinatesVarying;
If `use_usampler` is `true` then a `uniform usampler2D texID` will be used as the source texture.
Otherwise it'll be a `sampler2D`.
`lateralVarying` is a value in radians that is equal to 0 at the centre of the scan and ± a suitable
angle at the top and bottom extremes.
`srcCoordinatesVarying` is a value representing the coordinates for source data in the texture
attached to the sampler `texID`.
`iSrcCoordinatesVarying` is a value corresponding to `srcCoordinatesVarying` but scaled up so that
one unit is the width of one source sample.
Does not catch any exceptions raised by `Shader::Shader`.
@returns an instance of OutputShader.
*/
static std::unique_ptr<OutputShader> make_shader(const char *fragment_shader, bool use_usampler);
using Shader::Shader;
void set_output_size(unsigned int output_width, unsigned int output_height, Outputs::CRT::Rect visible_area);
void set_source_texture_unit(GLenum unit);
void set_timing(unsigned int height_of_display, unsigned int cycles_per_line, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider);
private:
GLint boundsOriginUniform, boundsSizeUniform, texIDUniform, scanNormalUniform, positionConversionUniform;
};
}
#endif /* OutputShader_hpp */