1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 23:52:26 +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 <stdlib.h>
#include <math.h>
#include <cstdlib>
#include <cstring>
#include <cmath>
#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()

View File

@ -66,12 +66,19 @@ class OpenGLOutputBuilder {
GLsizei composite_src_output_y_;
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
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<OpenGL::IntermediateShader> composite_input_shader_program_;
std::unique_ptr<OpenGL::IntermediateShader> composite_separation_filter_program_;
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

View File

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

View File

@ -46,6 +46,8 @@ std::unique_ptr<IntermediateShader> 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> 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> 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);
}

View File

@ -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<IntermediateShader> make_shader(const char *fragment_shader, bool use_usampler, bool input_is_inputPosition);
};