1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-10-13 21:24:23 +00:00

Started taking steps towards using a texture barrier where possible to reduce all of my framebuffer binds. Some output appears, but it's not correct.

This commit is contained in:
Thomas Harte 2017-01-08 11:13:20 -05:00
parent 0487b8c178
commit be48c950b4
5 changed files with 109 additions and 44 deletions

View File

@ -6,8 +6,10 @@
// //
#include "CRT.hpp" #include "CRT.hpp"
#include <stdlib.h>
#include <math.h> #include <cstdlib>
#include <cstring>
#include <cmath>
#include "CRTOpenGL.hpp" #include "CRTOpenGL.hpp"
#include "../../../SignalProcessing/FIRFilter.hpp" #include "../../../SignalProcessing/FIRFilter.hpp"
@ -21,6 +23,8 @@ namespace {
static const GLenum filtered_texture_unit = GL_TEXTURE2; static const GLenum filtered_texture_unit = GL_TEXTURE2;
static const GLenum source_data_texture_unit = GL_TEXTURE3; static const GLenum source_data_texture_unit = GL_TEXTURE3;
static const GLenum pixel_accumulation_texture_unit = GL_TEXTURE4; static const GLenum pixel_accumulation_texture_unit = GL_TEXTURE4;
static const GLenum work_texture_unit = GL_TEXTURE0;
} }
OpenGLOutputBuilder::OpenGLOutputBuilder(size_t bytes_per_pixel) : OpenGLOutputBuilder::OpenGLOutputBuilder(size_t bytes_per_pixel) :
@ -32,10 +36,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(size_t bytes_per_pixel) :
last_output_height_(0), last_output_height_(0),
fence_(nullptr), fence_(nullptr),
texture_builder(bytes_per_pixel, source_data_texture_unit), texture_builder(bytes_per_pixel, source_data_texture_unit),
array_builder(SourceVertexBufferDataSize, OutputVertexBufferDataSize), 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)
{ {
glBlendFunc(GL_SRC_ALPHA, GL_CONSTANT_COLOR); glBlendFunc(GL_SRC_ALPHA, GL_CONSTANT_COLOR);
glBlendColor(0.6f, 0.6f, 0.6f, 1.0f); 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 // create the source vertex array
glGenVertexArrays(1, &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() OpenGLOutputBuilder::~OpenGLOutputBuilder()
@ -121,26 +148,25 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
output_mutex_.unlock(); output_mutex_.unlock();
struct RenderStage { struct RenderStage {
OpenGL::TextureTarget *const target;
OpenGL::Shader *const shader; OpenGL::Shader *const shader;
OpenGL::TextureTarget *const target;
float clear_colour[3]; float clear_colour[3];
}; };
// for composite video, go through four steps to get to something that can be painted to the output // for composite video, go through four steps to get to something that can be painted to the output
RenderStage composite_render_stages[] = RenderStage composite_render_stages[] =
{ {
{&composite_texture_, composite_input_shader_program_.get(), {0.0, 0.0, 0.0}}, {composite_input_shader_program_.get(), composite_texture_.get(), {0.0, 0.0, 0.0}},
{&separated_texture_, composite_separation_filter_program_.get(), {0.0, 0.5, 0.5}}, {composite_separation_filter_program_.get(), separated_texture_.get(), {0.0, 0.5, 0.5}},
// {&filtered_y_texture_, composite_y_filter_shader_program_.get(), {0.0, 0.5, 0.5}}, {composite_chrominance_filter_shader_program_.get(), filtered_texture_.get(), {0.0, 0.0, 0.0}},
{&filtered_texture_, composite_chrominance_filter_shader_program_.get(), {0.0, 0.0, 0.0}},
{nullptr} {nullptr}
}; };
// for RGB video, there's only two steps // for RGB video, there's only two steps
RenderStage rgb_render_stages[] = RenderStage rgb_render_stages[] =
{ {
{&composite_texture_, rgb_input_shader_program_.get(), {0.0, 0.0, 0.0}}, {rgb_input_shader_program_.get(), composite_texture_.get(), {0.0, 0.0, 0.0}},
{&filtered_texture_, rgb_filter_shader_program_.get(), {0.0, 0.0, 0.0}}, {rgb_filter_shader_program_.get(), filtered_texture_.get(), {0.0, 0.0, 0.0}},
{nullptr} {nullptr}
}; };
@ -152,24 +178,39 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
glBindVertexArray(source_vertex_array_); glBindVertexArray(source_vertex_array_);
glDisable(GL_BLEND); 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 // switch to the framebuffer and shader associated with this stage
active_pipeline->shader->bind(); 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 if(!work_texture_)
// those portions for which no input was provided
if(!active_pipeline[1].target)
{ {
glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0f); active_pipeline->target->bind_framebuffer();
glClear(GL_COLOR_BUFFER_BIT);
// 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 // draw
glDrawArraysInstanced(GL_LINES, 0, 2, (GLsizei)array_submission.input_size / SourceVertexSize); glDrawArraysInstanced(GL_LINES, 0, 2, (GLsizei)array_submission.input_size / SourceVertexSize);
active_pipeline++; active_pipeline++;
#ifdef GL_NV_texture_barrier
glTextureBarrierNV();
#endif
} }
// prepare to transfer to framebuffer // 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); 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 // copy framebuffer to the intended place
glDisable(GL_BLEND); glDisable(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, 0); 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_ = 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_source_texture_unit(source_data_texture_unit);
composite_input_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight); 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_ = 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_separation_filter_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
composite_separation_filter_program_->set_vertical_offsets(0.0f, work_texture_ ? 0.5f : 0.0f);
// 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_chrominance_filter_shader_program_ = OpenGL::IntermediateShader::make_chroma_filter_shader(); 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_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() void OpenGLOutputBuilder::prepare_rgb_input_shaders()
@ -292,7 +336,7 @@ void OpenGLOutputBuilder::prepare_source_vertex_array()
void OpenGLOutputBuilder::prepare_output_shader() void OpenGLOutputBuilder::prepare_output_shader()
{ {
output_shader_program_ = OpenGL::OutputShader::make_shader("", "texture(texID, srcCoordinatesVarying).rgb", false); 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() void OpenGLOutputBuilder::prepare_output_vertex_array()

View File

@ -66,12 +66,19 @@ class OpenGLOutputBuilder {
GLsizei composite_src_output_y_; GLsizei composite_src_output_y_;
std::unique_ptr<OpenGL::OutputShader> output_shader_program_; std::unique_ptr<OpenGL::OutputShader> output_shader_program_;
std::unique_ptr<OpenGL::IntermediateShader> composite_input_shader_program_, composite_separation_filter_program_, composite_chrominance_filter_shader_program_;
std::unique_ptr<OpenGL::IntermediateShader> rgb_input_shader_program_, rgb_filter_shader_program_;
OpenGL::TextureTarget composite_texture_; // receives raw composite levels std::unique_ptr<OpenGL::IntermediateShader> composite_input_shader_program_;
OpenGL::TextureTarget separated_texture_; // receives filtered Y in the R channel plus unfiltered but demodulated chrominance in G and B std::unique_ptr<OpenGL::IntermediateShader> composite_separation_filter_program_;
OpenGL::TextureTarget filtered_texture_; // receives filtered YIQ or YUV std::unique_ptr<OpenGL::IntermediateShader> composite_chrominance_filter_shader_program_;
std::unique_ptr<OpenGL::IntermediateShader> rgb_input_shader_program_;
std::unique_ptr<OpenGL::IntermediateShader> rgb_filter_shader_program_;
std::unique_ptr<OpenGL::TextureTarget> composite_texture_; // receives raw composite levels
std::unique_ptr<OpenGL::TextureTarget> separated_texture_; // receives filtered Y in the R channel plus unfiltered but demodulated chrominance in G and B
std::unique_ptr<OpenGL::TextureTarget> filtered_texture_; // receives filtered YIQ or YUV
std::unique_ptr<OpenGL::TextureTarget> work_texture_; // used for all intermediate rendering if texture fences are supported
std::unique_ptr<OpenGL::TextureTarget> framebuffer_; // the current pixel output std::unique_ptr<OpenGL::TextureTarget> framebuffer_; // the current pixel output

View File

@ -15,6 +15,7 @@
#else #else
#include <OpenGL/OpenGL.h> #include <OpenGL/OpenGL.h>
#include <OpenGL/gl3.h> #include <OpenGL/gl3.h>
#include <OpenGL/gl3ext.h>
#endif #endif
#endif #endif

View File

@ -46,6 +46,8 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
"uniform %s texID;" "uniform %s texID;"
"uniform float offsets[5];" "uniform float offsets[5];"
"uniform vec2 widthScalers;" "uniform vec2 widthScalers;"
"uniform float inputVerticalOffset;"
"uniform float outputVerticalOffset;"
"out vec2 phaseAndAmplitudeVarying;" "out vec2 phaseAndAmplitudeVarying;"
"out vec2 inputPositionsVarying[11];" "out vec2 inputPositionsVarying[11];"
@ -60,8 +62,8 @@ std::unique_ptr<IntermediateShader> 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; // 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; // outputPosition.x is either outputStart.x or ends.y;
// .ys are inputStart.y and outputStart.y respectively // .ys are inputStart.y and outputStart.y respectively
"vec2 inputPosition = vec2(mix(inputStart.x, ends.x, extent)*widthScalers[0], inputStart.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);" "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 // 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);" "vec2 extensionVector = vec2(extension, 0.0) * 2.0 * (extent - 0.5);"
@ -76,17 +78,17 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
"vec2 mappedInputPosition = (extendedInputPosition + vec2(0.0, 0.5)) / textureSize;" "vec2 mappedInputPosition = (extendedInputPosition + vec2(0.0, 0.5)) / textureSize;"
// setup input positions spaced as per the supplied offsets; these are for filtering where required // 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[0] = mappedInputPosition - (vec2(5.0, 0.0) / textureSize);"
"inputPositionsVarying[1] = mappedInputPosition - (vec2(offsets[1], 0.0) / textureSize);" "inputPositionsVarying[1] = mappedInputPosition - (vec2(4.0, 0.0) / textureSize);"
"inputPositionsVarying[2] = mappedInputPosition - (vec2(offsets[2], 0.0) / textureSize);" "inputPositionsVarying[2] = mappedInputPosition - (vec2(3.0, 0.0) / textureSize);"
"inputPositionsVarying[3] = mappedInputPosition - (vec2(offsets[3], 0.0) / textureSize);" "inputPositionsVarying[3] = mappedInputPosition - (vec2(2.0, 0.0) / textureSize);"
"inputPositionsVarying[4] = mappedInputPosition - (vec2(offsets[4], 0.0) / textureSize);" "inputPositionsVarying[4] = mappedInputPosition - (vec2(1.0, 0.0) / textureSize);"
"inputPositionsVarying[5] = mappedInputPosition;" "inputPositionsVarying[5] = mappedInputPosition;"
"inputPositionsVarying[6] = mappedInputPosition + (vec2(offsets[4], 0.0) / textureSize);" "inputPositionsVarying[6] = mappedInputPosition + (vec2(1.0, 0.0) / textureSize);"
"inputPositionsVarying[7] = mappedInputPosition + (vec2(offsets[3], 0.0) / textureSize);" "inputPositionsVarying[7] = mappedInputPosition + (vec2(2.0, 0.0) / textureSize);"
"inputPositionsVarying[8] = mappedInputPosition + (vec2(offsets[2], 0.0) / textureSize);" "inputPositionsVarying[8] = mappedInputPosition + (vec2(3.0, 0.0) / textureSize);"
"inputPositionsVarying[9] = mappedInputPosition + (vec2(offsets[1], 0.0) / textureSize);" "inputPositionsVarying[9] = mappedInputPosition + (vec2(4.0, 0.0) / textureSize);"
"inputPositionsVarying[10] = mappedInputPosition + (vec2(offsets[0], 0.0) / textureSize);" "inputPositionsVarying[10] = mappedInputPosition + (vec2(5.0, 0.0) / textureSize);"
"delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);" "delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);"
// setup phaseAndAmplitudeVarying.x as colour burst subcarrier phase, in radians; // 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); set_uniform("widthScalers", input_scaler, output_scaler);
} }
void IntermediateShader::set_vertical_offsets(float input, float output)
{
set_uniform("inputVerticalOffset", input);
set_uniform("outputVerticalOffset", output);
}

View File

@ -89,6 +89,11 @@ public:
*/ */
void set_width_scalers(float input_scaler, float output_scaler); 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: private:
static std::unique_ptr<IntermediateShader> make_shader(const char *fragment_shader, bool use_usampler, bool input_is_inputPosition); static std::unique_ptr<IntermediateShader> make_shader(const char *fragment_shader, bool use_usampler, bool input_is_inputPosition);
}; };