From 8bc3f8046d93ab684e4baf04c63f597b57021096 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 4 Feb 2016 22:57:46 -0500 Subject: [PATCH] This endeavours to move everything into the CRT class except the final version of shader building. --- .../Mac/Clock Signal/Views/CSCathodeRayView.m | 194 +------------ .../Mac/Clock Signal/Wrappers/CSElectron.mm | 2 +- Outputs/CRT/CRT.cpp | 152 +---------- Outputs/CRT/CRT.hpp | 45 +-- Outputs/CRT/CRTOpenGL.cpp | 257 ++++++++++++++++-- 5 files changed, 260 insertions(+), 390 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Views/CSCathodeRayView.m b/OSBindings/Mac/Clock Signal/Views/CSCathodeRayView.m index 45a9a3f64..069f10dd0 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSCathodeRayView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSCathodeRayView.m @@ -185,44 +185,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt // self.frameBounds = CGRectMake(0.0, 0.0, 1.0, 1.0); } -/*- (GLint)formatForDepth:(unsigned int)depth -{ - switch(depth) - { - default: return -1; - case 1: return GL_RED; - case 2: return GL_RG; - case 3: return GL_RGB; - case 4: return GL_RGBA; - } -} - -- (BOOL)pushFrame:(nonnull CRTFrame *)crtFrame -{ - [[self openGLContext] makeCurrentContext]; - CGLLockContext([[self openGLContext] CGLContextObj]); - - BOOL hadFrame = _crtFrame ? YES : NO; - _crtFrame = crtFrame; - - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(_crtFrame->number_of_vertices * _crtFrame->size_per_vertex), _crtFrame->vertices, GL_DYNAMIC_DRAW); - - glBindTexture(GL_TEXTURE_2D, _textureName); - if(_textureSize.width != _crtFrame->size.width || _textureSize.height != _crtFrame->size.height) - { - GLint format = [self formatForDepth:_crtFrame->buffers[0].depth]; - glTexImage2D(GL_TEXTURE_2D, 0, format, _crtFrame->size.width, _crtFrame->size.height, 0, (GLenum)format, GL_UNSIGNED_BYTE, _crtFrame->buffers[0].data); - _textureSize = _crtFrame->size; - } - else - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _crtFrame->size.width, _crtFrame->dirty_size.height, (GLenum)[self formatForDepth:_crtFrame->buffers[0].depth], GL_UNSIGNED_BYTE, _crtFrame->buffers[0].data); - - [self drawView]; - - CGLUnlockContext([[self openGLContext] CGLContextObj]); - - return hadFrame; -} +/* #pragma mark - Frame output @@ -265,138 +228,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt OSAtomicIncrement32(&_signalDecoderGeneration); } -- (nonnull NSString *)vertexShaderForType:(CSCathodeRayViewSignalType)type -{ - // 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; there's also some setup for NTSC, PAL or whatever. - - NSString *const ntscVertexShaderGlobals = - @"out vec2 srcCoordinatesVarying[4];\n" - "out float phase;\n"; - - NSString *const ntscVertexShaderBody = - @"phase = srcCoordinates.x * 6.283185308;\n" - "\n" - "srcCoordinatesVarying[0] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n" - "srcCoordinatesVarying[1] = srcCoordinatesVarying[0] - vec2(0.5 / textureSize.x, 0.0);\n" - "srcCoordinatesVarying[2] = srcCoordinatesVarying[0] - vec2(0.25 / textureSize.x, 0.0);\n"; - - NSString *const rgbVertexShaderGlobals = - @"out vec2 srcCoordinatesVarying[5];\n"; - - NSString *const rgbVertexShaderBody = - @"srcCoordinatesVarying[2] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n" - "srcCoordinatesVarying[0] = srcCoordinatesVarying[1] - vec2(1.0 / textureSize.x, 0.0);\n" - "srcCoordinatesVarying[1] = srcCoordinatesVarying[1] - vec2(0.5 / textureSize.x, 0.0);\n" - "srcCoordinatesVarying[3] = srcCoordinatesVarying[1] + vec2(0.5 / textureSize.x, 0.0);\n" - "srcCoordinatesVarying[4] = srcCoordinatesVarying[1] + vec2(1.0 / textureSize.x, 0.0);\n"; - - NSString *const vertexShader = - @"#version 150\n" - "\n" - "in vec2 position;\n" - "in vec2 srcCoordinates;\n" - "in float lateral;\n" - "\n" - "uniform vec2 boundsOrigin;\n" - "uniform vec2 boundsSize;\n" - "\n" - "out float lateralVarying;\n" - "out vec2 shadowMaskCoordinates;\n" - "\n" - "uniform vec2 textureSize;\n" - "\n" - "const float shadowMaskMultiple = 600;\n" - "\n" - "%@\n" - "void main (void)\n" - "{\n" - "lateralVarying = lateral + 1.0707963267949;\n" - "\n" - "shadowMaskCoordinates = position * vec2(shadowMaskMultiple, shadowMaskMultiple * 0.85057471264368);\n" - "\n" - "%@\n" - "\n" - "vec2 mappedPosition = (position - boundsOrigin) / boundsSize;" - "gl_Position = vec4(mappedPosition.x * 2.0 - 1.0, 1.0 - mappedPosition.y * 2.0, 0.0, 1.0);\n" - "}\n"; - -// + mappedPosition.x / 131.0 - - switch(_signalType) - { - case CSCathodeRayViewSignalTypeNTSC: return [NSString stringWithFormat:vertexShader, ntscVertexShaderGlobals, ntscVertexShaderBody]; - case CSCathodeRayViewSignalTypeRGB: return [NSString stringWithFormat:vertexShader, rgbVertexShaderGlobals, rgbVertexShaderBody]; - } -} - -- (nonnull NSString *)fragmentShaderForType:(CSCathodeRayViewSignalType)type -{ - NSString *const fragmentShader = - @"#version 150\n" - "\n" - "in float lateralVarying;\n" - "in vec2 shadowMaskCoordinates;\n" - "out vec4 fragColour;\n" - "\n" - "uniform sampler2D texID;\n" - "uniform sampler2D shadowMaskTexID;\n" - "uniform float alpha;\n" - "\n" - "%@\n" - "%%@\n" - "\n" - "void main(void)\n" - "{\n" - "%@\n" - "}\n"; - - NSString *const ntscFragmentShaderGlobals = - @"in vec2 srcCoordinatesVarying[4];\n" - "in float phase;\n" - "\n" - "// for conversion from i and q are in the range [-0.5, 0.5] (so i needs to be multiplied by 1.1914 and q by 1.0452)\n" - "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);\n"; - - NSString *const ntscFragmentShaderBody = - @"vec3 angles = vec3(phase) + vec3(0.0, -3.141592654, -1.570796327);\n" - "vec3 samples = vec3(" - " sample(srcCoordinatesVarying[0], angles.x)," - " sample(srcCoordinatesVarying[1], angles.y)," - " sample(srcCoordinatesVarying[2], angles.z)" - ");\n" - "\n" - "float y = dot(vec2(0.5), samples.xy);\n" - "samples -= vec3(y);\n" - "\n" - "float i = dot(vec3(0.75), cos(angles) * samples);\n" - "float q = dot(vec3(0.75), sin(angles) * samples);\n" - "\n" - "fragColour = vec4(yiqToRGB * vec3(y, i, q), 1.0);\n"; //sin(lateralVarying)); - // 5.0 * texture(shadowMaskTexID, shadowMaskCoordinates) * -// "float y2 = dot(vec2(0.5), samples.zw);\n" - - NSString *const rgbFragmentShaderGlobals = - @"in vec2 srcCoordinatesVarying[5];\n"; // texture(shadowMaskTexID, shadowMaskCoordinates) * - - NSString *const rgbFragmentShaderBody = - @"fragColour = sample(srcCoordinatesVarying[2]);"; -// @"fragColour = (sample(srcCoordinatesVarying[0]) * -0.1) + \ -// (sample(srcCoordinatesVarying[1]) * 0.3) + \ -// (sample(srcCoordinatesVarying[2]) * 0.6) + \ -// (sample(srcCoordinatesVarying[3]) * 0.3) + \ -// (sample(srcCoordinatesVarying[4]) * -0.1);"; - -// dot(vec3(1.0/6.0, 2.0/3.0, 1.0/6.0), vec3(sample(srcCoordinatesVarying[0]), sample(srcCoordinatesVarying[0]), sample(srcCoordinatesVarying[0])));//sin(lateralVarying));\n"; - - switch(_signalType) - { - case CSCathodeRayViewSignalTypeNTSC: return [NSString stringWithFormat:fragmentShader, ntscFragmentShaderGlobals, ntscFragmentShaderBody]; - case CSCathodeRayViewSignalTypeRGB: return [NSString stringWithFormat:fragmentShader, rgbFragmentShaderGlobals, rgbFragmentShaderBody]; - } -} - - (void)prepareShader { if(_shaderProgram) @@ -427,10 +258,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt // [self logErrorForObject:_shaderProgram]; #endif - glGenVertexArrays(1, &_vertexArray); - glBindVertexArray(_vertexArray); - glGenBuffers(1, &_arrayBuffer); - glBindBuffer(GL_ARRAY_BUFFER, _arrayBuffer); glUseProgram(_shaderProgram); @@ -460,12 +287,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt glVertexAttribPointer((GLuint)_textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)kCRTVertexOffsetOfTexCoord); glVertexAttribPointer((GLuint)_lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, vertexStride, (void *)kCRTVertexOffsetOfLateral); - glGenTextures(1, &_textureName); - glBindTexture(GL_TEXTURE_2D, _textureName); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); }*/ - (void)drawRect:(NSRect)dirtyRect @@ -478,20 +299,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt [self.openGLContext makeCurrentContext]; CGLLockContext([[self openGLContext] CGLContextObj]); - glClear(GL_COLOR_BUFFER_BIT); [self.delegate openGLViewDrawView:self]; -// while((!_shaderProgram || (_signalDecoderGeneration != _compiledSignalDecoderGeneration)) && _signalDecoder) { -// _compiledSignalDecoderGeneration = _signalDecoderGeneration; -// [self prepareShader]; -// } -// -// glClear(GL_COLOR_BUFFER_BIT); -// -// if (_crtFrame) -// { -// if(_textureSizeUniform >= 0) glUniform2f(_textureSizeUniform, _crtFrame->size.width, _crtFrame->size.height); -// glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_crtFrame->number_of_vertices); -// } CGLFlushDrawable([[self openGLContext] CGLContextObj]); CGLUnlockContext([[self openGLContext] CGLContextObj]); diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm index 09a50c8f4..11957973b 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm @@ -33,7 +33,7 @@ } - (void)drawViewForPixelSize:(CGSize)pixelSize { - _electron.get_crt()->draw_frame((int)pixelSize.width, (int)pixelSize.height); + _electron.get_crt()->draw_frame((int)pixelSize.width, (int)pixelSize.height, false); } - (BOOL)openUEFAtURL:(NSURL *)URL { diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 153059d4f..e3a7dcfb2 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -105,10 +105,11 @@ CRT::CRT() : _is_receiving_sync(false), _is_in_hsync(false), _is_in_vsync(false), - _current_frame(nullptr), _current_frame_mutex(new std::mutex), _rasterPosition({.x = 0, .y = 0}) -{} +{ + construct_openGL(); +} CRT::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() { @@ -136,6 +137,7 @@ CRT::~CRT() { delete _frame_builders[frame]; } + destruct_openGL(); } #pragma mark - Sync loop @@ -434,149 +436,3 @@ uint8_t *CRT::get_write_target_for_buffer(int buffer) if (!_current_frame_builder) return nullptr; return _current_frame_builder->get_write_target_for_buffer(buffer); } - - - -char *CRT::get_vertex_shader() -{ - // 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; there's also some setup for NTSC, PAL or whatever. - - const char *const ntscVertexShaderGlobals = - "out vec2 srcCoordinatesVarying[4];\n" - "out float phase;\n"; - - const char *const ntscVertexShaderBody = - "phase = srcCoordinates.x * 6.283185308;\n" - "\n" - "srcCoordinatesVarying[0] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n" - "srcCoordinatesVarying[3] = srcCoordinatesVarying[0] + vec2(0.375 / textureSize.x, 0.0);\n" - "srcCoordinatesVarying[2] = srcCoordinatesVarying[0] + vec2(0.125 / textureSize.x, 0.0);\n" - "srcCoordinatesVarying[1] = srcCoordinatesVarying[0] - vec2(0.125 / textureSize.x, 0.0);\n" - "srcCoordinatesVarying[0] = srcCoordinatesVarying[0] - vec2(0.325 / textureSize.x, 0.0);\n"; - -// const char *const rgbVertexShaderGlobals = -// "out vec2 srcCoordinatesVarying[5];\n"; - -// const char *const rgbVertexShaderBody = -// "srcCoordinatesVarying[2] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n" -// "srcCoordinatesVarying[0] = srcCoordinatesVarying[1] - vec2(1.0 / textureSize.x, 0.0);\n" -// "srcCoordinatesVarying[1] = srcCoordinatesVarying[1] - vec2(0.5 / textureSize.x, 0.0);\n" -// "srcCoordinatesVarying[3] = srcCoordinatesVarying[1] + vec2(0.5 / textureSize.x, 0.0);\n" -// "srcCoordinatesVarying[4] = srcCoordinatesVarying[1] + vec2(1.0 / textureSize.x, 0.0);\n"; - - const char *const vertexShader = - "#version 150\n" - "\n" - "in vec2 position;\n" - "in vec2 srcCoordinates;\n" - "in float lateral;\n" - "\n" - "uniform vec2 boundsOrigin;\n" - "uniform vec2 boundsSize;\n" - "\n" - "out float lateralVarying;\n" - "out vec2 shadowMaskCoordinates;\n" - "\n" - "uniform vec2 textureSize;\n" - "\n" - "const float shadowMaskMultiple = 600;\n" - "\n" - "%@\n" - "void main (void)\n" - "{\n" - "lateralVarying = lateral + 1.0707963267949;\n" - "\n" - "shadowMaskCoordinates = position * vec2(shadowMaskMultiple, shadowMaskMultiple * 0.85057471264368);\n" - "\n" - "%@\n" - "\n" - "vec2 mappedPosition = (position - boundsOrigin) / boundsSize;" - "gl_Position = vec4(mappedPosition.x * 2.0 - 1.0, 1.0 - mappedPosition.y * 2.0, 0.0, 1.0);\n" - "}\n"; - - return nullptr; -// + mappedPosition.x / 131.0 - -// switch(_signalType) -// { -// case CSCathodeRayViewSignalTypeNTSC: return [NSString stringWithFormat:vertexShader, ntscVertexShaderGlobals, ntscVertexShaderBody]; -// case CSCathodeRayViewSignalTypeRGB: return [NSString stringWithFormat:vertexShader, rgbVertexShaderGlobals, rgbVertexShaderBody]; -// } -} - -char *CRT::get_fragment_shader(const char *sample_function) -{ - // assumes y = [0, 1], i and q = [-0.5, 0.5]; therefore i components are multiplied by 1.1914 versus standard matrices, q by 1.0452 - const char *const yiqToRGB = "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);"; - - // assumes y = [0,1], u and v = [-0.5, 0.5]; therefore u components are multiplied by 1.14678899082569, v by 0.8130081300813 - const char *const yuvToRGB = "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 0.0, -0.75213899082569, 2.33040137614679, 0.92669105691057, -0.4720325203252, 0.0);"; - - const char *const fragmentShader = - "#version 150\n" - "\n" - "in float lateralVarying;\n" - "in vec2 shadowMaskCoordinates;\n" - "out vec4 fragColour;\n" - "\n" - "uniform sampler2D texID;\n" - "uniform sampler2D shadowMaskTexID;\n" - "uniform float alpha;\n" - "\n" - "in vec2 srcCoordinatesVarying[4];\n" - "in float phase;\n" - "%@\n" - "%@\n" - "\n" - "void main(void)\n" - "{\n" - "%@\n" - "}\n"; - - const char *const ntscFragmentShaderGlobals = - "in vec2 srcCoordinatesVarying[4];\n" - "in float phase;\n" - "\n" - "// for conversion from i and q are in the range [-0.5, 0.5] (so i needs to be multiplied by 1.1914 and q by 1.0452)\n" - "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);\n"; - - const char *const ntscFragmentShaderBody = - "vec4 angles = vec4(phase) + vec4(-2.35619449019234, -0.78539816339745, 0.78539816339745, 2.35619449019234);\n" - "vec4 samples = vec4(" - " sample(srcCoordinatesVarying[0], angles.x)," - " sample(srcCoordinatesVarying[1], angles.y)," - " sample(srcCoordinatesVarying[2], angles.z)," - " sample(srcCoordinatesVarying[3], angles.w)" - ");\n" - "\n" - "float y = dot(vec4(0.25), samples);\n" - "samples -= vec4(y);\n" - "\n" - "float i = dot(cos(angles), samples);\n" - "float q = dot(sin(angles), samples);\n" - "\n" - "fragColour = 5.0 * texture(shadowMaskTexID, shadowMaskCoordinates) * vec4(yiqToRGB * vec3(y, i, q), 1.0);//sin(lateralVarying));\n"; - -// const char *const rgbFragmentShaderGlobals = -// "in vec2 srcCoordinatesVarying[5];\n"; // texture(shadowMaskTexID, shadowMaskCoordinates) * - -// const char *const rgbFragmentShaderBody = -// "fragColour = sample(srcCoordinatesVarying[2]);"; -// @"fragColour = (sample(srcCoordinatesVarying[0]) * -0.1) + \ -// (sample(srcCoordinatesVarying[1]) * 0.3) + \ -// (sample(srcCoordinatesVarying[2]) * 0.6) + \ -// (sample(srcCoordinatesVarying[3]) * 0.3) + \ -// (sample(srcCoordinatesVarying[4]) * -0.1);"; - -// dot(vec3(1.0/6.0, 2.0/3.0, 1.0/6.0), vec3(sample(srcCoordinatesVarying[0]), sample(srcCoordinatesVarying[0]), sample(srcCoordinatesVarying[0])));//sin(lateralVarying));\n"; - - return nullptr; - -// switch(_signalType) -// { -// case CSCathodeRayViewSignalTypeNTSC: return [NSString stringWithFormat:fragmentShader, ntscFragmentShaderGlobals, ntscFragmentShaderBody]; -// case CSCathodeRayViewSignalTypeRGB: return [NSString stringWithFormat:fragmentShader, rgbFragmentShaderGlobals, rgbFragmentShaderBody]; -// } -} diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index be40816f9..3d21807d4 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -140,7 +140,7 @@ class CRT { /*! 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. */ - void draw_frame(int output_width, int output_height); + void draw_frame(int output_width, int output_height, bool only_if_dirty); /*! Tells the CRT that the next call to draw_frame will occur on a different OpenGL context than the previous. @@ -251,34 +251,6 @@ class CRT { 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; @@ -310,10 +282,21 @@ class CRT { // the triple buffer and OpenGL state CRTFrameBuilder *_frame_builders[kCRTNumberOfFrames]; CRTFrameBuilder *_current_frame_builder; - CRTFrame *_current_frame; + CRTFrame *_current_frame, *_last_drawn_frame; std::shared_ptr _current_frame_mutex; int _frame_read_pointer; - void *openGLState; + + struct OpenGLState; + OpenGLState *_openGL_state; + + char *_composite_shader; + char *_rgb_shader; + + void construct_openGL(); + void destruct_openGL(); + + char *get_vertex_shader(); + char *get_fragment_shader(); }; } diff --git a/Outputs/CRT/CRTOpenGL.cpp b/Outputs/CRT/CRTOpenGL.cpp index 12174fd22..5c815a904 100644 --- a/Outputs/CRT/CRTOpenGL.cpp +++ b/Outputs/CRT/CRTOpenGL.cpp @@ -1,4 +1,4 @@ -// + // CRTOpenGL.cpp // Clock Signal // @@ -7,33 +7,111 @@ // #include "CRT.hpp" +#include // TODO: figure out correct include paths for other platforms. #include +#include using namespace Outputs; -namespace { - struct OpenGLState { - GLuint _vertexShader, _fragmentShader; - GLuint _shaderProgram; - GLuint _arrayBuffer, _vertexArray; +struct CRT::OpenGLState { + GLuint vertexShader, fragmentShader; + GLuint shaderProgram; + GLuint arrayBuffer, vertexArray; - GLint _positionAttribute; - GLint _textureCoordinatesAttribute; - GLint _lateralAttribute; + GLint positionAttribute; + GLint textureCoordinatesAttribute; + GLint lateralAttribute; - GLint _textureSizeUniform, _windowSizeUniform; - GLint _boundsOriginUniform, _boundsSizeUniform; - GLint _alphaUniform; + GLint textureSizeUniform, windowSizeUniform; + GLint boundsOriginUniform, boundsSizeUniform; + GLint alphaUniform; - GLuint _textureName, _shadowMaskTextureName; - }; + GLuint textureName, shadowMaskTextureName; + + CRTSize textureSize; +}; + +static GLenum formatForDepth(unsigned int depth) +{ + switch(depth) + { + default: return -1; + case 1: return GL_RED; + case 2: return GL_RG; + case 3: return GL_RGB; + case 4: return GL_RGBA; + } } -void CRT::draw_frame(int output_width, int output_height) + +void CRT::construct_openGL() { - printf("%d %d\n", output_width, output_height); + _openGL_state = nullptr; + _current_frame = _last_drawn_frame = nullptr; + _composite_shader = _rgb_shader = nullptr; +} + +void CRT::destruct_openGL() +{ + delete (OpenGLState *)_openGL_state; + if(_composite_shader) free(_composite_shader); + if(_rgb_shader) free(_rgb_shader); +} + +void CRT::draw_frame(int output_width, int output_height, bool only_if_dirty) +{ + _current_frame_mutex->lock(); + + if(!_current_frame) + { + glClear(GL_COLOR_BUFFER_BIT); + } + else + { + if(_current_frame != _last_drawn_frame) + { + if(!_openGL_state) + { + _openGL_state = new OpenGLState; + + glGenTextures(1, &_openGL_state->textureName); + glBindTexture(GL_TEXTURE_2D, _openGL_state->textureName); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glGenVertexArrays(1, &_openGL_state->vertexArray); + glBindVertexArray(_openGL_state->vertexArray); + glGenBuffers(1, &_openGL_state->arrayBuffer); + glBindBuffer(GL_ARRAY_BUFFER, _openGL_state->arrayBuffer); + } + + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(_current_frame->number_of_vertices * _current_frame->size_per_vertex), _current_frame->vertices, GL_DYNAMIC_DRAW); + + glBindTexture(GL_TEXTURE_2D, _openGL_state->textureName); + if(_openGL_state->textureSize.width != _current_frame->size.width || _openGL_state->textureSize.height != _current_frame->size.height) + { + GLenum format = formatForDepth(_current_frame->buffers[0].depth); + glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, _current_frame->size.width, _current_frame->size.height, 0, format, GL_UNSIGNED_BYTE, _current_frame->buffers[0].data); + _openGL_state->textureSize = _current_frame->size; + + if(_openGL_state->textureSizeUniform >= 0) glUniform2f(_openGL_state->textureSizeUniform, _current_frame->size.width, _current_frame->size.height); + } + else + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _current_frame->dirty_size.width, _current_frame->dirty_size.height, formatForDepth(_current_frame->buffers[0].depth), GL_UNSIGNED_BYTE, _current_frame->buffers[0].data); + } + } + + if(_current_frame != _last_drawn_frame || only_if_dirty) + { + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_current_frame->number_of_vertices); + } + + _current_frame_mutex->unlock(); } void CRT::set_openGL_context_will_change(bool should_delete_resources) @@ -42,9 +120,154 @@ void CRT::set_openGL_context_will_change(bool should_delete_resources) void CRT::set_composite_sampling_function(const char *shader) { + _composite_shader = strdup(shader); } void CRT::set_rgb_sampling_function(const char *shader) { - printf("%s\n", shader); + _rgb_shader = strdup(shader); +} + +char *CRT::get_vertex_shader() +{ + // 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; there's also some setup for NTSC, PAL or whatever. + + const char *const ntscVertexShaderGlobals = + "out vec2 srcCoordinatesVarying[4];\n" + "out float phase;\n"; + + const char *const ntscVertexShaderBody = + "phase = srcCoordinates.x * 6.283185308;\n" + "\n" + "srcCoordinatesVarying[0] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n" + "srcCoordinatesVarying[3] = srcCoordinatesVarying[0] + vec2(0.375 / textureSize.x, 0.0);\n" + "srcCoordinatesVarying[2] = srcCoordinatesVarying[0] + vec2(0.125 / textureSize.x, 0.0);\n" + "srcCoordinatesVarying[1] = srcCoordinatesVarying[0] - vec2(0.125 / textureSize.x, 0.0);\n" + "srcCoordinatesVarying[0] = srcCoordinatesVarying[0] - vec2(0.325 / textureSize.x, 0.0);\n"; + +// const char *const rgbVertexShaderGlobals = +// "out vec2 srcCoordinatesVarying[5];\n"; + +// const char *const rgbVertexShaderBody = +// "srcCoordinatesVarying[2] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n" +// "srcCoordinatesVarying[0] = srcCoordinatesVarying[1] - vec2(1.0 / textureSize.x, 0.0);\n" +// "srcCoordinatesVarying[1] = srcCoordinatesVarying[1] - vec2(0.5 / textureSize.x, 0.0);\n" +// "srcCoordinatesVarying[3] = srcCoordinatesVarying[1] + vec2(0.5 / textureSize.x, 0.0);\n" +// "srcCoordinatesVarying[4] = srcCoordinatesVarying[1] + vec2(1.0 / textureSize.x, 0.0);\n"; + + const char *const vertexShader = + "#version 150\n" + "\n" + "in vec2 position;\n" + "in vec2 srcCoordinates;\n" + "in float lateral;\n" + "\n" + "uniform vec2 boundsOrigin;\n" + "uniform vec2 boundsSize;\n" + "\n" + "out float lateralVarying;\n" + "out vec2 shadowMaskCoordinates;\n" + "\n" + "uniform vec2 textureSize;\n" + "\n" + "const float shadowMaskMultiple = 600;\n" + "\n" + "%@\n" + "void main (void)\n" + "{\n" + "lateralVarying = lateral + 1.0707963267949;\n" + "\n" + "shadowMaskCoordinates = position * vec2(shadowMaskMultiple, shadowMaskMultiple * 0.85057471264368);\n" + "\n" + "%@\n" + "\n" + "vec2 mappedPosition = (position - boundsOrigin) / boundsSize;" + "gl_Position = vec4(mappedPosition.x * 2.0 - 1.0, 1.0 - mappedPosition.y * 2.0, 0.0, 1.0);\n" + "}\n"; + + return nullptr; +// + mappedPosition.x / 131.0 + +// switch(_signalType) +// { +// case CSCathodeRayViewSignalTypeNTSC: return [NSString stringWithFormat:vertexShader, ntscVertexShaderGlobals, ntscVertexShaderBody]; +// case CSCathodeRayViewSignalTypeRGB: return [NSString stringWithFormat:vertexShader, rgbVertexShaderGlobals, rgbVertexShaderBody]; +// } +} + +char *CRT::get_fragment_shader() +{ + // assumes y = [0, 1], i and q = [-0.5, 0.5]; therefore i components are multiplied by 1.1914 versus standard matrices, q by 1.0452 + const char *const yiqToRGB = "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);"; + + // assumes y = [0,1], u and v = [-0.5, 0.5]; therefore u components are multiplied by 1.14678899082569, v by 0.8130081300813 + const char *const yuvToRGB = "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 0.0, -0.75213899082569, 2.33040137614679, 0.92669105691057, -0.4720325203252, 0.0);"; + + const char *const fragmentShader = + "#version 150\n" + "\n" + "in float lateralVarying;\n" + "in vec2 shadowMaskCoordinates;\n" + "out vec4 fragColour;\n" + "\n" + "uniform sampler2D texID;\n" + "uniform sampler2D shadowMaskTexID;\n" + "uniform float alpha;\n" + "\n" + "in vec2 srcCoordinatesVarying[4];\n" + "in float phase;\n" + "%@\n" + "%@\n" + "\n" + "void main(void)\n" + "{\n" + "%@\n" + "}\n"; + + const char *const ntscFragmentShaderGlobals = + "in vec2 srcCoordinatesVarying[4];\n" + "in float phase;\n" + "\n" + "// for conversion from i and q are in the range [-0.5, 0.5] (so i needs to be multiplied by 1.1914 and q by 1.0452)\n" + "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);\n"; + + const char *const ntscFragmentShaderBody = + "vec4 angles = vec4(phase) + vec4(-2.35619449019234, -0.78539816339745, 0.78539816339745, 2.35619449019234);\n" + "vec4 samples = vec4(" + " sample(srcCoordinatesVarying[0], angles.x)," + " sample(srcCoordinatesVarying[1], angles.y)," + " sample(srcCoordinatesVarying[2], angles.z)," + " sample(srcCoordinatesVarying[3], angles.w)" + ");\n" + "\n" + "float y = dot(vec4(0.25), samples);\n" + "samples -= vec4(y);\n" + "\n" + "float i = dot(cos(angles), samples);\n" + "float q = dot(sin(angles), samples);\n" + "\n" + "fragColour = 5.0 * texture(shadowMaskTexID, shadowMaskCoordinates) * vec4(yiqToRGB * vec3(y, i, q), 1.0);//sin(lateralVarying));\n"; + +// const char *const rgbFragmentShaderGlobals = +// "in vec2 srcCoordinatesVarying[5];\n"; // texture(shadowMaskTexID, shadowMaskCoordinates) * + +// const char *const rgbFragmentShaderBody = +// "fragColour = sample(srcCoordinatesVarying[2]);"; +// @"fragColour = (sample(srcCoordinatesVarying[0]) * -0.1) + \ +// (sample(srcCoordinatesVarying[1]) * 0.3) + \ +// (sample(srcCoordinatesVarying[2]) * 0.6) + \ +// (sample(srcCoordinatesVarying[3]) * 0.3) + \ +// (sample(srcCoordinatesVarying[4]) * -0.1);"; + +// dot(vec3(1.0/6.0, 2.0/3.0, 1.0/6.0), vec3(sample(srcCoordinatesVarying[0]), sample(srcCoordinatesVarying[0]), sample(srcCoordinatesVarying[0])));//sin(lateralVarying));\n"; + + return nullptr; + +// switch(_signalType) +// { +// case CSCathodeRayViewSignalTypeNTSC: return [NSString stringWithFormat:fragmentShader, ntscFragmentShaderGlobals, ntscFragmentShaderBody]; +// case CSCathodeRayViewSignalTypeRGB: return [NSString stringWithFormat:fragmentShader, rgbFragmentShaderGlobals, rgbFragmentShaderBody]; +// } }