diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 1bacdb20c..479c73e93 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -6,8 +6,10 @@ // #include "CRT.hpp" -#include -#include + +#include +#include +#include #include "CRTOpenGL.hpp" #include "../../../SignalProcessing/FIRFilter.hpp" @@ -21,6 +23,8 @@ namespace { static const GLenum filtered_texture_unit = GL_TEXTURE2; static const GLenum source_data_texture_unit = GL_TEXTURE3; static const GLenum pixel_accumulation_texture_unit = GL_TEXTURE4; + + static const GLenum work_texture_unit = GL_TEXTURE0; } OpenGLOutputBuilder::OpenGLOutputBuilder(size_t bytes_per_pixel) : @@ -32,10 +36,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(size_t bytes_per_pixel) : last_output_height_(0), fence_(nullptr), texture_builder(bytes_per_pixel, source_data_texture_unit), - array_builder(SourceVertexBufferDataSize, OutputVertexBufferDataSize), - composite_texture_(IntermediateBufferWidth, IntermediateBufferHeight, composite_texture_unit), - separated_texture_(IntermediateBufferWidth, IntermediateBufferHeight, separated_texture_unit), - filtered_texture_(IntermediateBufferWidth, IntermediateBufferHeight, filtered_texture_unit) + array_builder(SourceVertexBufferDataSize, OutputVertexBufferDataSize) { glBlendFunc(GL_SRC_ALPHA, GL_CONSTANT_COLOR); glBlendColor(0.6f, 0.6f, 0.6f, 1.0f); @@ -45,6 +46,32 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(size_t bytes_per_pixel) : // create the source vertex array glGenVertexArrays(1, &source_vertex_array_); + + bool supports_texture_barrier = false; +#ifdef GL_NV_texture_barrier + GLint number_of_extensions; + glGetIntegerv(GL_NUM_EXTENSIONS, &number_of_extensions); + + for(GLuint c = 0; c < (GLuint)number_of_extensions; c++) + { + const char *extension_name = (const char *)glGetStringi(GL_EXTENSIONS, c); + if(!strcmp(extension_name, "GL_NV_texture_barrier")) + { + supports_texture_barrier = true; + } + } +#endif + + if(supports_texture_barrier) + { + work_texture_.reset(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight*2, work_texture_unit)); + } + else + { + composite_texture_.reset(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, composite_texture_unit)); + separated_texture_.reset(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, separated_texture_unit)); + filtered_texture_.reset(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, filtered_texture_unit)); + } } OpenGLOutputBuilder::~OpenGLOutputBuilder() @@ -121,26 +148,25 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out output_mutex_.unlock(); struct RenderStage { - OpenGL::TextureTarget *const target; OpenGL::Shader *const shader; + OpenGL::TextureTarget *const target; float clear_colour[3]; }; // for composite video, go through four steps to get to something that can be painted to the output RenderStage composite_render_stages[] = { - {&composite_texture_, composite_input_shader_program_.get(), {0.0, 0.0, 0.0}}, - {&separated_texture_, composite_separation_filter_program_.get(), {0.0, 0.5, 0.5}}, -// {&filtered_y_texture_, composite_y_filter_shader_program_.get(), {0.0, 0.5, 0.5}}, - {&filtered_texture_, composite_chrominance_filter_shader_program_.get(), {0.0, 0.0, 0.0}}, + {composite_input_shader_program_.get(), composite_texture_.get(), {0.0, 0.0, 0.0}}, + {composite_separation_filter_program_.get(), separated_texture_.get(), {0.0, 0.5, 0.5}}, + {composite_chrominance_filter_shader_program_.get(), filtered_texture_.get(), {0.0, 0.0, 0.0}}, {nullptr} }; // for RGB video, there's only two steps RenderStage rgb_render_stages[] = { - {&composite_texture_, rgb_input_shader_program_.get(), {0.0, 0.0, 0.0}}, - {&filtered_texture_, rgb_filter_shader_program_.get(), {0.0, 0.0, 0.0}}, + {rgb_input_shader_program_.get(), composite_texture_.get(), {0.0, 0.0, 0.0}}, + {rgb_filter_shader_program_.get(), filtered_texture_.get(), {0.0, 0.0, 0.0}}, {nullptr} }; @@ -152,24 +178,39 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindVertexArray(source_vertex_array_); glDisable(GL_BLEND); - while(active_pipeline->target) +#ifdef GL_NV_texture_barrier + if(work_texture_) + { + work_texture_->bind_framebuffer(); + glClear(GL_COLOR_BUFFER_BIT); + } +#endif + + while(active_pipeline->shader) { // switch to the framebuffer and shader associated with this stage active_pipeline->shader->bind(); - active_pipeline->target->bind_framebuffer(); - // if this is the final stage before painting to the CRT, clear the framebuffer before drawing in order to blank out - // those portions for which no input was provided - if(!active_pipeline[1].target) + if(!work_texture_) { - glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + active_pipeline->target->bind_framebuffer(); + + // if this is the final stage before painting to the CRT, clear the framebuffer before drawing in order to blank out + // those portions for which no input was provided + if(!active_pipeline[1].shader) + { + glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + } } // draw glDrawArraysInstanced(GL_LINES, 0, 2, (GLsizei)array_submission.input_size / SourceVertexSize); active_pipeline++; +#ifdef GL_NV_texture_barrier + glTextureBarrierNV(); +#endif } // prepare to transfer to framebuffer @@ -192,6 +233,10 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, (GLsizei)array_submission.output_size / OutputVertexSize); } +#ifdef GL_NV_texture_barrier + glTextureBarrierNV(); +#endif + // copy framebuffer to the intended place glDisable(GL_BLEND); glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -247,18 +292,17 @@ void OpenGLOutputBuilder::prepare_composite_input_shaders() composite_input_shader_program_ = OpenGL::IntermediateShader::make_source_conversion_shader(composite_shader_, rgb_shader_); composite_input_shader_program_->set_source_texture_unit(source_data_texture_unit); composite_input_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight); + composite_input_shader_program_->set_vertical_offsets(0.0f, 0.0f); composite_separation_filter_program_ = OpenGL::IntermediateShader::make_chroma_luma_separation_shader(); - composite_separation_filter_program_->set_source_texture_unit(composite_texture_unit); + composite_separation_filter_program_->set_source_texture_unit(work_texture_ ? work_texture_unit : composite_texture_unit); composite_separation_filter_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight); - -// composite_y_filter_shader_program_ = OpenGL::IntermediateShader::make_luma_filter_shader(); -// composite_y_filter_shader_program_->set_source_texture_unit(separated_texture_unit); -// composite_y_filter_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight); + composite_separation_filter_program_->set_vertical_offsets(0.0f, work_texture_ ? 0.5f : 0.0f); composite_chrominance_filter_shader_program_ = OpenGL::IntermediateShader::make_chroma_filter_shader(); - composite_chrominance_filter_shader_program_->set_source_texture_unit(separated_texture_unit); + composite_chrominance_filter_shader_program_->set_source_texture_unit(work_texture_ ? work_texture_unit : separated_texture_unit); composite_chrominance_filter_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight); + composite_chrominance_filter_shader_program_->set_vertical_offsets(work_texture_ ? 0.5f : 0.0f, 0.0f); } void OpenGLOutputBuilder::prepare_rgb_input_shaders() @@ -292,7 +336,7 @@ void OpenGLOutputBuilder::prepare_source_vertex_array() void OpenGLOutputBuilder::prepare_output_shader() { output_shader_program_ = OpenGL::OutputShader::make_shader("", "texture(texID, srcCoordinatesVarying).rgb", false); - output_shader_program_->set_source_texture_unit(filtered_texture_unit); + output_shader_program_->set_source_texture_unit(work_texture_ ? work_texture_unit : filtered_texture_unit); } void OpenGLOutputBuilder::prepare_output_vertex_array() diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index c6c19e74d..551a20193 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -66,12 +66,19 @@ class OpenGLOutputBuilder { GLsizei composite_src_output_y_; std::unique_ptr output_shader_program_; - std::unique_ptr composite_input_shader_program_, composite_separation_filter_program_, composite_chrominance_filter_shader_program_; - std::unique_ptr rgb_input_shader_program_, rgb_filter_shader_program_; - OpenGL::TextureTarget composite_texture_; // receives raw composite levels - OpenGL::TextureTarget separated_texture_; // receives filtered Y in the R channel plus unfiltered but demodulated chrominance in G and B - OpenGL::TextureTarget filtered_texture_; // receives filtered YIQ or YUV + std::unique_ptr composite_input_shader_program_; + std::unique_ptr composite_separation_filter_program_; + std::unique_ptr composite_chrominance_filter_shader_program_; + + std::unique_ptr rgb_input_shader_program_; + std::unique_ptr rgb_filter_shader_program_; + + std::unique_ptr composite_texture_; // receives raw composite levels + std::unique_ptr separated_texture_; // receives filtered Y in the R channel plus unfiltered but demodulated chrominance in G and B + std::unique_ptr filtered_texture_; // receives filtered YIQ or YUV + + std::unique_ptr work_texture_; // used for all intermediate rendering if texture fences are supported std::unique_ptr framebuffer_; // the current pixel output diff --git a/Outputs/CRT/Internals/OpenGL.hpp b/Outputs/CRT/Internals/OpenGL.hpp index 4d373546b..629ebd501 100644 --- a/Outputs/CRT/Internals/OpenGL.hpp +++ b/Outputs/CRT/Internals/OpenGL.hpp @@ -15,6 +15,7 @@ #else #include #include + #include #endif #endif diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp index 3832fc76f..c85735d21 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp @@ -46,6 +46,8 @@ std::unique_ptr IntermediateShader::make_shader(const char * "uniform %s texID;" "uniform float offsets[5];" "uniform vec2 widthScalers;" + "uniform float inputVerticalOffset;" + "uniform float outputVerticalOffset;" "out vec2 phaseAndAmplitudeVarying;" "out vec2 inputPositionsVarying[11];" @@ -60,8 +62,8 @@ std::unique_ptr IntermediateShader::make_shader(const char * // inputPosition.x is either inputStart.x or ends.x, depending on whether it is on the left or the right; // outputPosition.x is either outputStart.x or ends.y; // .ys are inputStart.y and outputStart.y respectively - "vec2 inputPosition = vec2(mix(inputStart.x, ends.x, extent)*widthScalers[0], inputStart.y);" - "vec2 outputPosition = vec2(mix(outputStart.x, ends.y, extent)*widthScalers[1], outputStart.y);" + "vec2 inputPosition = vec2(mix(inputStart.x, ends.x, extent)*widthScalers[0], inputStart.y + inputVerticalOffset);" + "vec2 outputPosition = vec2(mix(outputStart.x, ends.y, extent)*widthScalers[1], outputStart.y + outputVerticalOffset);" // extension is the amount to extend both the input and output by to add a full colour cycle at each end "vec2 extensionVector = vec2(extension, 0.0) * 2.0 * (extent - 0.5);" @@ -76,17 +78,17 @@ std::unique_ptr IntermediateShader::make_shader(const char * "vec2 mappedInputPosition = (extendedInputPosition + vec2(0.0, 0.5)) / textureSize;" // setup input positions spaced as per the supplied offsets; these are for filtering where required - "inputPositionsVarying[0] = mappedInputPosition - (vec2(offsets[0], 0.0) / textureSize);" - "inputPositionsVarying[1] = mappedInputPosition - (vec2(offsets[1], 0.0) / textureSize);" - "inputPositionsVarying[2] = mappedInputPosition - (vec2(offsets[2], 0.0) / textureSize);" - "inputPositionsVarying[3] = mappedInputPosition - (vec2(offsets[3], 0.0) / textureSize);" - "inputPositionsVarying[4] = mappedInputPosition - (vec2(offsets[4], 0.0) / textureSize);" + "inputPositionsVarying[0] = mappedInputPosition - (vec2(5.0, 0.0) / textureSize);" + "inputPositionsVarying[1] = mappedInputPosition - (vec2(4.0, 0.0) / textureSize);" + "inputPositionsVarying[2] = mappedInputPosition - (vec2(3.0, 0.0) / textureSize);" + "inputPositionsVarying[3] = mappedInputPosition - (vec2(2.0, 0.0) / textureSize);" + "inputPositionsVarying[4] = mappedInputPosition - (vec2(1.0, 0.0) / textureSize);" "inputPositionsVarying[5] = mappedInputPosition;" - "inputPositionsVarying[6] = mappedInputPosition + (vec2(offsets[4], 0.0) / textureSize);" - "inputPositionsVarying[7] = mappedInputPosition + (vec2(offsets[3], 0.0) / textureSize);" - "inputPositionsVarying[8] = mappedInputPosition + (vec2(offsets[2], 0.0) / textureSize);" - "inputPositionsVarying[9] = mappedInputPosition + (vec2(offsets[1], 0.0) / textureSize);" - "inputPositionsVarying[10] = mappedInputPosition + (vec2(offsets[0], 0.0) / textureSize);" + "inputPositionsVarying[6] = mappedInputPosition + (vec2(1.0, 0.0) / textureSize);" + "inputPositionsVarying[7] = mappedInputPosition + (vec2(2.0, 0.0) / textureSize);" + "inputPositionsVarying[8] = mappedInputPosition + (vec2(3.0, 0.0) / textureSize);" + "inputPositionsVarying[9] = mappedInputPosition + (vec2(4.0, 0.0) / textureSize);" + "inputPositionsVarying[10] = mappedInputPosition + (vec2(5.0, 0.0) / textureSize);" "delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);" // setup phaseAndAmplitudeVarying.x as colour burst subcarrier phase, in radians; @@ -474,3 +476,9 @@ void IntermediateShader::set_width_scalers(float input_scaler, float output_scal { set_uniform("widthScalers", input_scaler, output_scaler); } + +void IntermediateShader::set_vertical_offsets(float input, float output) +{ + set_uniform("inputVerticalOffset", input); + set_uniform("outputVerticalOffset", output); +} diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp index 335b52be8..c8c801809 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp @@ -89,6 +89,11 @@ public: */ void set_width_scalers(float input_scaler, float output_scaler); + /*! + Sets source and target vertical offsets. + */ + void set_vertical_offsets(float input, float output); + private: static std::unique_ptr make_shader(const char *fragment_shader, bool use_usampler, bool input_is_inputPosition); };