mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-18 04:29:46 +00:00
Merge pull request #96 from TomHarte/PhaseAlignedSampling
Optimises existing composite flow
This commit is contained in:
commit
92f928ca42
@ -84,7 +84,7 @@ void AY38910::set_clock_rate(double clock_rate)
|
||||
void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||
{
|
||||
int c = 0;
|
||||
while((master_divider_&15) && c < number_of_samples)
|
||||
while((master_divider_&7) && c < number_of_samples)
|
||||
{
|
||||
target[c] = output_volume_;
|
||||
master_divider_++;
|
||||
@ -131,7 +131,7 @@ void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||
|
||||
evaluate_output_volume();
|
||||
|
||||
for(int ic = 0; ic < 16 && c < number_of_samples; ic++)
|
||||
for(int ic = 0; ic < 8 && c < number_of_samples; ic++)
|
||||
{
|
||||
target[c] = output_volume_;
|
||||
c++;
|
||||
@ -139,7 +139,7 @@ void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||
}
|
||||
}
|
||||
|
||||
master_divider_ &= 15;
|
||||
master_divider_ &= 7;
|
||||
}
|
||||
|
||||
void AY38910::evaluate_output_volume()
|
||||
|
@ -71,7 +71,7 @@ void Machine::setup_output(float aspect_ratio)
|
||||
"uint y = c & 14u;"
|
||||
"uint iPhase = (c >> 4);"
|
||||
|
||||
"float phaseOffset = 6.283185308 * float(iPhase - 1u) / 13.0;"
|
||||
"float phaseOffset = 6.283185308 * float(iPhase) / 13.0 + 5.074880441076923;"
|
||||
"return mix(float(y) / 14.0, step(1, iPhase) * cos(phase + phaseOffset), amplitude);"
|
||||
"}");
|
||||
speaker_->set_input_rate((float)(get_clock_rate() / 38.0));
|
||||
@ -777,5 +777,6 @@ void Machine::update_audio()
|
||||
void Machine::synchronise()
|
||||
{
|
||||
update_audio();
|
||||
speaker_->flush();
|
||||
}
|
||||
|
||||
|
@ -29,9 +29,9 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di
|
||||
// for horizontal retrace and 500 to 750 µs for vertical retrace in NTSC and PAL TV."
|
||||
|
||||
time_multiplier_ = IntermediateBufferWidth / cycles_per_line;
|
||||
phase_denominator_ = cycles_per_line * colour_cycle_denominator;
|
||||
phase_denominator_ = cycles_per_line * colour_cycle_denominator * time_multiplier_;
|
||||
phase_numerator_ = 0;
|
||||
colour_cycle_numerator_ = colour_cycle_numerator * time_multiplier_;
|
||||
colour_cycle_numerator_ = colour_cycle_numerator;
|
||||
phase_alternates_ = should_alternate;
|
||||
is_alernate_line_ &= phase_alternates_;
|
||||
unsigned int multiplied_cycles_per_line = cycles_per_line * time_multiplier_;
|
||||
@ -112,7 +112,6 @@ Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested,
|
||||
#define source_output_position_x2() (*(uint16_t *)&next_run[SourceVertexOffsetOfEnds + 2])
|
||||
#define source_phase() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 0]
|
||||
#define source_amplitude() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 2]
|
||||
#define source_phase_time() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 1]
|
||||
|
||||
void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Scan::Type type)
|
||||
{
|
||||
@ -149,7 +148,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo
|
||||
source_output_position_x1() = (uint16_t)horizontal_flywheel_->get_current_output_position();
|
||||
source_phase() = colour_burst_phase_;
|
||||
source_amplitude() = colour_burst_amplitude_;
|
||||
source_phase_time() = (uint8_t)colour_burst_time_; // assumption: burst was within the first 1/16 of the line
|
||||
}
|
||||
|
||||
// decrement the number of cycles left to run for and increment the
|
||||
@ -242,7 +240,9 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo
|
||||
frames_since_last_delegate_call_++;
|
||||
if(frames_since_last_delegate_call_ == 20)
|
||||
{
|
||||
output_lock.unlock();
|
||||
delegate_->crt_did_end_batch_of_frames(this, frames_since_last_delegate_call_, vertical_flywheel_->get_and_reset_number_of_surprises());
|
||||
output_lock.lock();
|
||||
frames_since_last_delegate_call_ = 0;
|
||||
}
|
||||
}
|
||||
@ -286,9 +286,11 @@ void CRT::output_scan(const Scan *const scan)
|
||||
{
|
||||
if(horizontal_flywheel_->get_current_time() < (horizontal_flywheel_->get_standard_period() * 12) >> 6)
|
||||
{
|
||||
colour_burst_time_ = (uint16_t)horizontal_flywheel_->get_current_time();
|
||||
colour_burst_phase_ = scan->phase;
|
||||
unsigned int position_phase = (horizontal_flywheel_->get_current_time() * colour_cycle_numerator_ * 256) / phase_denominator_;
|
||||
colour_burst_phase_ = (position_phase + scan->phase) & 255;
|
||||
colour_burst_amplitude_ = scan->amplitude;
|
||||
|
||||
colour_burst_phase_ = (colour_burst_phase_ & ~63) + 32;
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,7 +346,7 @@ void CRT::output_default_colour_burst(unsigned int number_of_cycles)
|
||||
Scan scan {
|
||||
.type = Scan::Type::ColourBurst,
|
||||
.number_of_cycles = number_of_cycles,
|
||||
.phase = (uint8_t)((phase_numerator_ * 255) / phase_denominator_ + (is_alernate_line_ ? 128 : 0)),
|
||||
.phase = (uint8_t)((phase_numerator_ * 256) / phase_denominator_ + (is_alernate_line_ ? 128 : 0)),
|
||||
.amplitude = 32
|
||||
};
|
||||
output_scan(&scan);
|
||||
|
@ -60,7 +60,6 @@ class CRT {
|
||||
void output_scan(const Scan *scan);
|
||||
|
||||
uint8_t colour_burst_phase_, colour_burst_amplitude_;
|
||||
uint16_t colour_burst_time_;
|
||||
bool is_writing_composite_run_;
|
||||
|
||||
unsigned int phase_denominator_, phase_numerator_, colour_cycle_numerator_;
|
||||
|
@ -36,7 +36,7 @@ const GLsizei InputBufferBuilderWidth = 2048;
|
||||
const GLsizei InputBufferBuilderHeight = 512;
|
||||
|
||||
// This is the size of the intermediate buffers used during composite to RGB conversion
|
||||
const GLsizei IntermediateBufferWidth = 4096;
|
||||
const GLsizei IntermediateBufferWidth = 2048;
|
||||
const GLsizei IntermediateBufferHeight = 512;
|
||||
|
||||
// Some internal buffer sizes
|
||||
|
@ -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"
|
||||
@ -16,12 +18,14 @@
|
||||
using namespace Outputs::CRT;
|
||||
|
||||
namespace {
|
||||
static const GLenum composite_texture_unit = GL_TEXTURE0;
|
||||
static const GLenum separated_texture_unit = GL_TEXTURE1;
|
||||
static const GLenum filtered_y_texture_unit = GL_TEXTURE2;
|
||||
static const GLenum filtered_texture_unit = GL_TEXTURE3;
|
||||
static const GLenum source_data_texture_unit = GL_TEXTURE4;
|
||||
static const GLenum pixel_accumulation_texture_unit = GL_TEXTURE5;
|
||||
static const GLenum source_data_texture_unit = GL_TEXTURE0;
|
||||
static const GLenum pixel_accumulation_texture_unit = GL_TEXTURE1;
|
||||
|
||||
static const GLenum composite_texture_unit = GL_TEXTURE2;
|
||||
static const GLenum separated_texture_unit = GL_TEXTURE3;
|
||||
static const GLenum filtered_texture_unit = GL_TEXTURE4;
|
||||
|
||||
static const GLenum work_texture_unit = GL_TEXTURE2;
|
||||
}
|
||||
|
||||
OpenGLOutputBuilder::OpenGLOutputBuilder(size_t bytes_per_pixel) :
|
||||
@ -33,11 +37,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_y_texture_(IntermediateBufferWidth, IntermediateBufferHeight, filtered_y_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);
|
||||
@ -47,6 +47,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, GL_NEAREST));
|
||||
separated_texture_.reset(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, separated_texture_unit, GL_NEAREST));
|
||||
filtered_texture_.reset(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, filtered_texture_unit, GL_LINEAR));
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLOutputBuilder::~OpenGLOutputBuilder()
|
||||
@ -57,6 +83,11 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder()
|
||||
free(rgb_shader_);
|
||||
}
|
||||
|
||||
bool OpenGLOutputBuilder::get_is_television_output()
|
||||
{
|
||||
return output_device_ == Television || !rgb_input_shader_program_;
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty)
|
||||
{
|
||||
// lock down any other draw_frames
|
||||
@ -91,7 +122,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
||||
// make sure there's a target to draw to
|
||||
if(!framebuffer_ || framebuffer_->get_height() != output_height || framebuffer_->get_width() != output_width)
|
||||
{
|
||||
std::unique_ptr<OpenGL::TextureTarget> new_framebuffer(new OpenGL::TextureTarget((GLsizei)output_width, (GLsizei)output_height, pixel_accumulation_texture_unit));
|
||||
std::unique_ptr<OpenGL::TextureTarget> new_framebuffer(new OpenGL::TextureTarget((GLsizei)output_width, (GLsizei)output_height, pixel_accumulation_texture_unit, GL_LINEAR));
|
||||
if(framebuffer_)
|
||||
{
|
||||
new_framebuffer->bind_framebuffer();
|
||||
@ -123,30 +154,29 @@ 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}
|
||||
};
|
||||
|
||||
RenderStage *active_pipeline = (output_device_ == Television || !rgb_input_shader_program_) ? composite_render_stages : rgb_render_stages;
|
||||
RenderStage *active_pipeline = get_is_television_output() ? composite_render_stages : rgb_render_stages;
|
||||
|
||||
if(array_submission.input_size || array_submission.output_size)
|
||||
{
|
||||
@ -154,24 +184,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);
|
||||
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, (GLsizei)array_submission.input_size / SourceVertexSize);
|
||||
|
||||
active_pipeline++;
|
||||
#ifdef GL_NV_texture_barrier
|
||||
glTextureBarrierNV();
|
||||
#endif
|
||||
}
|
||||
|
||||
// prepare to transfer to framebuffer
|
||||
@ -194,6 +239,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);
|
||||
@ -211,7 +260,6 @@ void OpenGLOutputBuilder::reset_all_OpenGL_state()
|
||||
{
|
||||
composite_input_shader_program_ = nullptr;
|
||||
composite_separation_filter_program_ = nullptr;
|
||||
composite_y_filter_shader_program_ = nullptr;
|
||||
composite_chrominance_filter_shader_program_ = nullptr;
|
||||
rgb_input_shader_program_ = nullptr;
|
||||
rgb_filter_shader_program_ = nullptr;
|
||||
@ -229,18 +277,16 @@ void OpenGLOutputBuilder::set_openGL_context_will_change(bool should_delete_reso
|
||||
|
||||
void OpenGLOutputBuilder::set_composite_sampling_function(const char *shader)
|
||||
{
|
||||
output_mutex_.lock();
|
||||
std::lock_guard<std::mutex> lock_guard(output_mutex_);
|
||||
composite_shader_ = strdup(shader);
|
||||
reset_all_OpenGL_state();
|
||||
output_mutex_.unlock();
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::set_rgb_sampling_function(const char *shader)
|
||||
{
|
||||
output_mutex_.lock();
|
||||
std::lock_guard<std::mutex> lock_guard(output_mutex_);
|
||||
rgb_shader_ = strdup(shader);
|
||||
reset_all_OpenGL_state();
|
||||
output_mutex_.unlock();
|
||||
}
|
||||
|
||||
#pragma mark - Program compilation
|
||||
@ -252,16 +298,25 @@ void OpenGLOutputBuilder::prepare_composite_input_shaders()
|
||||
composite_input_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
||||
|
||||
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_chrominance_filter_shader_program_ = OpenGL::IntermediateShader::make_chroma_filter_shader();
|
||||
composite_chrominance_filter_shader_program_->set_source_texture_unit(filtered_y_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);
|
||||
|
||||
if(work_texture_)
|
||||
{
|
||||
composite_input_shader_program_->set_is_double_height(true, 0.0f, 0.0f);
|
||||
composite_separation_filter_program_->set_is_double_height(true, 0.0f, 0.5f);
|
||||
composite_chrominance_filter_shader_program_->set_is_double_height(true, 0.5f, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
composite_input_shader_program_->set_is_double_height(false);
|
||||
composite_separation_filter_program_->set_is_double_height(false);
|
||||
composite_chrominance_filter_shader_program_->set_is_double_height(false);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::prepare_rgb_input_shaders()
|
||||
@ -295,7 +350,9 @@ 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);
|
||||
// output_shader_program_->set_source_texture_unit(composite_texture_unit);
|
||||
output_shader_program_->set_origin_is_double_height(!!work_texture_);
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::prepare_output_vertex_array()
|
||||
@ -319,6 +376,7 @@ void OpenGLOutputBuilder::set_output_device(OutputDevice output_device)
|
||||
composite_src_output_y_ = 0;
|
||||
last_output_width_ = 0;
|
||||
last_output_height_ = 0;
|
||||
set_output_shader_width();
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,30 +420,58 @@ void OpenGLOutputBuilder::set_colour_space_uniforms()
|
||||
}
|
||||
|
||||
if(composite_input_shader_program_) composite_input_shader_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||
if(composite_separation_filter_program_) composite_separation_filter_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||
if(composite_chrominance_filter_shader_program_) composite_chrominance_filter_shader_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||
}
|
||||
|
||||
float OpenGLOutputBuilder::get_composite_output_width() const
|
||||
{
|
||||
return ((float)colour_cycle_numerator_ * 4.0f) / (float)(colour_cycle_denominator_ * IntermediateBufferWidth);
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::set_output_shader_width()
|
||||
{
|
||||
if(output_shader_program_)
|
||||
{
|
||||
const float width = get_is_television_output() ? get_composite_output_width() : 1.0f;
|
||||
output_shader_program_->set_input_width_scaler(width);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::set_timing_uniforms()
|
||||
{
|
||||
OpenGL::IntermediateShader *intermediate_shaders[] = {
|
||||
composite_input_shader_program_.get(),
|
||||
composite_separation_filter_program_.get(),
|
||||
composite_y_filter_shader_program_.get(),
|
||||
composite_chrominance_filter_shader_program_.get()
|
||||
};
|
||||
bool extends = false;
|
||||
float phaseCyclesPerTick = (float)colour_cycle_numerator_ / (float)(colour_cycle_denominator_ * cycles_per_line_);
|
||||
for(int c = 0; c < 3; c++)
|
||||
const float colour_subcarrier_frequency = (float)colour_cycle_numerator_ / (float)colour_cycle_denominator_;
|
||||
const float output_width = get_composite_output_width();
|
||||
const float sample_cycles_per_line = cycles_per_line_ / output_width;
|
||||
|
||||
if(composite_separation_filter_program_)
|
||||
{
|
||||
if(intermediate_shaders[c]) intermediate_shaders[c]->set_phase_cycles_per_sample(phaseCyclesPerTick, extends);
|
||||
extends = true;
|
||||
composite_separation_filter_program_->set_width_scalers(output_width, output_width);
|
||||
composite_separation_filter_program_->set_separation_frequency(sample_cycles_per_line, colour_subcarrier_frequency);
|
||||
composite_separation_filter_program_->set_extension(6.0f);
|
||||
}
|
||||
if(composite_chrominance_filter_shader_program_)
|
||||
{
|
||||
composite_chrominance_filter_shader_program_->set_width_scalers(output_width, output_width);
|
||||
composite_chrominance_filter_shader_program_->set_extension(5.0f);
|
||||
}
|
||||
if(rgb_filter_shader_program_)
|
||||
{
|
||||
rgb_filter_shader_program_->set_width_scalers(1.0f, 1.0f);
|
||||
rgb_filter_shader_program_->set_filter_coefficients(sample_cycles_per_line, (float)input_frequency_ * 0.5f);
|
||||
}
|
||||
if(output_shader_program_)
|
||||
{
|
||||
set_output_shader_width();
|
||||
output_shader_program_->set_timing(height_of_display_, cycles_per_line_, horizontal_scan_period_, vertical_scan_period_, vertical_period_divider_);
|
||||
}
|
||||
if(composite_input_shader_program_)
|
||||
{
|
||||
composite_input_shader_program_->set_width_scalers(1.0f, output_width);
|
||||
composite_input_shader_program_->set_extension(0.0f);
|
||||
}
|
||||
if(rgb_input_shader_program_)
|
||||
{
|
||||
rgb_input_shader_program_->set_width_scalers(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
if(output_shader_program_) output_shader_program_->set_timing(height_of_display_, cycles_per_line_, horizontal_scan_period_, vertical_scan_period_, vertical_period_divider_);
|
||||
|
||||
float colour_subcarrier_frequency = (float)colour_cycle_numerator_ / (float)colour_cycle_denominator_;
|
||||
if(composite_separation_filter_program_) composite_separation_filter_program_->set_separation_frequency(cycles_per_line_, colour_subcarrier_frequency);
|
||||
if(composite_y_filter_shader_program_) composite_y_filter_shader_program_->set_filter_coefficients(cycles_per_line_, colour_subcarrier_frequency * 0.25f);
|
||||
if(composite_chrominance_filter_shader_program_) composite_chrominance_filter_shader_program_->set_filter_coefficients(cycles_per_line_, colour_subcarrier_frequency * 0.5f);
|
||||
if(rgb_filter_shader_program_) rgb_filter_shader_program_->set_filter_coefficients(cycles_per_line_, (float)input_frequency_ * 0.5f);
|
||||
}
|
||||
|
@ -66,13 +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_y_filter_shader_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 unfiltered Y in the R channel plus unfiltered but demodulated chrominance in G and B
|
||||
OpenGL::TextureTarget filtered_y_texture_; // receives filtered Y in the R channel plus unfiltered 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
|
||||
|
||||
@ -88,6 +94,9 @@ class OpenGLOutputBuilder {
|
||||
void reset_all_OpenGL_state();
|
||||
|
||||
GLsync fence_;
|
||||
float get_composite_output_width() const;
|
||||
void set_output_shader_width();
|
||||
bool get_is_television_output();
|
||||
|
||||
public:
|
||||
// These two are protected by output_mutex_.
|
||||
|
@ -15,6 +15,7 @@
|
||||
#else
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#include <OpenGL/gl3.h>
|
||||
#include <OpenGL/gl3ext.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -40,11 +40,14 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
|
||||
"in vec2 ends;"
|
||||
"in vec3 phaseTimeAndAmplitude;"
|
||||
|
||||
"uniform float phaseCyclesPerTick;"
|
||||
"uniform ivec2 outputTextureSize;"
|
||||
"uniform float extension;"
|
||||
"uniform %s texID;"
|
||||
"uniform float offsets[5];"
|
||||
"uniform vec2 widthScalers;"
|
||||
"uniform float inputVerticalOffset;"
|
||||
"uniform float outputVerticalOffset;"
|
||||
"uniform float textureHeightDivisor;"
|
||||
|
||||
"out vec2 phaseAndAmplitudeVarying;"
|
||||
"out vec2 inputPositionsVarying[11];"
|
||||
@ -53,35 +56,51 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
|
||||
|
||||
"void main(void)"
|
||||
"{"
|
||||
// odd vertices are on the left, even on the right
|
||||
"float extent = float(gl_VertexID & 1);"
|
||||
"float longitudinal = float((gl_VertexID & 2) >> 1);"
|
||||
|
||||
"vec2 inputPosition = vec2(mix(inputStart.x, ends.x, extent), inputStart.y);"
|
||||
"vec2 outputPosition = vec2(mix(outputStart.x, ends.y, extent), outputStart.y);"
|
||||
// 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 + inputVerticalOffset);"
|
||||
"vec2 outputPosition = vec2(mix(outputStart.x, ends.y, extent)*widthScalers[1], outputStart.y + outputVerticalOffset);"
|
||||
|
||||
"inputPosition.y += longitudinal;"
|
||||
"outputPosition.y += longitudinal;"
|
||||
|
||||
// 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);"
|
||||
|
||||
// extended[Input/Output]Position are [input/output]Position with the necessary applied extension
|
||||
"vec2 extendedInputPosition = %s + extensionVector;"
|
||||
"vec2 extendedOutputPosition = outputPosition + extensionVector;"
|
||||
|
||||
// keep iInputPositionVarying in whole source pixels, scale mappedInputPosition to the ordinary normalised range
|
||||
"vec2 textureSize = vec2(textureSize(texID, 0));"
|
||||
"iInputPositionVarying = extendedInputPosition;"
|
||||
"vec2 mappedInputPosition = (extendedInputPosition + vec2(0.0, 0.5)) / textureSize;"
|
||||
"vec2 mappedInputPosition = extendedInputPosition / textureSize;" // + vec2(0.0, 0.5)
|
||||
|
||||
"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);"
|
||||
// setup input positions spaced as per the supplied offsets; these are for filtering where required
|
||||
"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);"
|
||||
|
||||
"phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTimeAndAmplitude.y) + (phaseTimeAndAmplitude.x / 256.0)) * 2.0 * 3.141592654;"
|
||||
// setup phaseAndAmplitudeVarying.x as colour burst subcarrier phase, in radians;
|
||||
// setup phaseAndAmplitudeVarying.x as colour burst amplitude
|
||||
"phaseAndAmplitudeVarying.x = (extendedOutputPosition.x + (phaseTimeAndAmplitude.x / 64.0)) * 0.5 * 3.141592654;"
|
||||
"phaseAndAmplitudeVarying.y = 0.33;" // TODO: reinstate connection with (phaseTimeAndAmplitude.y/256.0)
|
||||
|
||||
// determine output position by scaling the output position according to the texture size
|
||||
"vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(1.0)/outputTextureSize;"
|
||||
"gl_Position = vec4(eyePosition, 0.0, 1.0);"
|
||||
"}", sampler_type, input_variable);
|
||||
@ -172,7 +191,6 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_luma_separat
|
||||
|
||||
"in vec2 phaseAndAmplitudeVarying;"
|
||||
"in vec2 inputPositionsVarying[11];"
|
||||
"uniform vec4 weights[3];"
|
||||
|
||||
"out vec3 fragColour;"
|
||||
|
||||
@ -180,37 +198,20 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_luma_separat
|
||||
|
||||
"void main(void)"
|
||||
"{"
|
||||
"vec4 samples[3] = vec4[]("
|
||||
"vec4("
|
||||
"texture(texID, inputPositionsVarying[0]).r,"
|
||||
"texture(texID, inputPositionsVarying[1]).r,"
|
||||
"texture(texID, inputPositionsVarying[2]).r,"
|
||||
"texture(texID, inputPositionsVarying[3]).r"
|
||||
"),"
|
||||
"vec4("
|
||||
"texture(texID, inputPositionsVarying[4]).r,"
|
||||
"texture(texID, inputPositionsVarying[5]).r,"
|
||||
"texture(texID, inputPositionsVarying[6]).r,"
|
||||
"texture(texID, inputPositionsVarying[7]).r"
|
||||
"),"
|
||||
"vec4("
|
||||
"texture(texID, inputPositionsVarying[8]).r,"
|
||||
"texture(texID, inputPositionsVarying[9]).r,"
|
||||
"texture(texID, inputPositionsVarying[10]).r,"
|
||||
"0.0"
|
||||
")"
|
||||
"vec4 samples = vec4("
|
||||
"texture(texID, inputPositionsVarying[3]).r,"
|
||||
"texture(texID, inputPositionsVarying[4]).r,"
|
||||
"texture(texID, inputPositionsVarying[5]).r,"
|
||||
"texture(texID, inputPositionsVarying[6]).r"
|
||||
");"
|
||||
"float luminance = dot(samples, vec4(0.25));"
|
||||
|
||||
"float luminance = "
|
||||
"dot(vec3("
|
||||
"dot(samples[0], weights[0]),"
|
||||
"dot(samples[1], weights[1]),"
|
||||
"dot(samples[2], weights[2])"
|
||||
"), vec3(1.0));"
|
||||
|
||||
"float chrominance = 0.5 * (samples[1].y - luminance) / phaseAndAmplitudeVarying.y;"
|
||||
// define chroma to be whatever was here, minus luma
|
||||
"float chrominance = 0.5 * (samples.z - luminance) / phaseAndAmplitudeVarying.y;"
|
||||
"luminance /= (1.0 - phaseAndAmplitudeVarying.y);"
|
||||
|
||||
// split choma colours here, as the most direct place, writing out
|
||||
// RGB = (luma, chroma.x, chroma.y)
|
||||
"vec2 quadrature = vec2(cos(phaseAndAmplitudeVarying.x), -sin(phaseAndAmplitudeVarying.x));"
|
||||
"fragColour = vec3(luminance, vec2(0.5) + (chrominance * quadrature));"
|
||||
"}",false, false);
|
||||
@ -232,41 +233,18 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_filter_shade
|
||||
"void main(void)"
|
||||
"{"
|
||||
"vec3 samples[] = vec3[]("
|
||||
"texture(texID, inputPositionsVarying[0]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[1]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[2]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[3]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[4]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[5]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[6]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[7]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[8]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[9]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[10]).rgb"
|
||||
"texture(texID, inputPositionsVarying[6]).rgb"
|
||||
");"
|
||||
|
||||
"vec4 chromaChannel1[] = vec4[]("
|
||||
"vec4(samples[0].g, samples[1].g, samples[2].g, samples[3].g),"
|
||||
"vec4(samples[4].g, samples[5].g, samples[6].g, samples[7].g),"
|
||||
"vec4(samples[8].g, samples[9].g, samples[10].g, 0.0)"
|
||||
");"
|
||||
"vec4 chromaChannel2[] = vec4[]("
|
||||
"vec4(samples[0].b, samples[1].b, samples[2].b, samples[3].b),"
|
||||
"vec4(samples[4].b, samples[5].b, samples[6].b, samples[7].b),"
|
||||
"vec4(samples[8].b, samples[9].b, samples[10].b, 0.0)"
|
||||
");"
|
||||
"vec4 chromaChannel1 = vec4(samples[0].g, samples[1].g, samples[2].g, samples[3].g);"
|
||||
"vec4 chromaChannel2 = vec4(samples[0].b, samples[1].b, samples[2].b, samples[3].b);"
|
||||
|
||||
"vec3 lumaChromaColour = vec3(samples[5].r,"
|
||||
"dot(vec3("
|
||||
"dot(chromaChannel1[0], weights[0]),"
|
||||
"dot(chromaChannel1[1], weights[1]),"
|
||||
"dot(chromaChannel1[2], weights[2])"
|
||||
"), vec3(1.0)),"
|
||||
"dot(vec3("
|
||||
"dot(chromaChannel2[0], weights[0]),"
|
||||
"dot(chromaChannel2[1], weights[1]),"
|
||||
"dot(chromaChannel2[2], weights[2])"
|
||||
"), vec3(1.0))"
|
||||
"vec3 lumaChromaColour = vec3(samples[2].r,"
|
||||
"dot(chromaChannel1, vec4(0.25)),"
|
||||
"dot(chromaChannel2, vec4(0.25))"
|
||||
");"
|
||||
|
||||
"vec3 lumaChromaColourInRange = (lumaChromaColour - vec3(0.0, 0.5, 0.5)) * vec3(1.0, 2.0, 2.0);"
|
||||
@ -274,52 +252,6 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_filter_shade
|
||||
"}", false, false);
|
||||
}
|
||||
|
||||
std::unique_ptr<IntermediateShader> IntermediateShader::make_luma_filter_shader()
|
||||
{
|
||||
return make_shader(
|
||||
"#version 150\n"
|
||||
|
||||
"in vec2 inputPositionsVarying[11];"
|
||||
"uniform vec4 weights[3];"
|
||||
|
||||
"out vec3 fragColour;"
|
||||
|
||||
"uniform sampler2D texID;"
|
||||
"uniform mat3 lumaChromaToRGB;"
|
||||
|
||||
"void main(void)"
|
||||
"{"
|
||||
"vec3 samples[] = vec3[]("
|
||||
"texture(texID, inputPositionsVarying[0]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[1]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[2]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[3]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[4]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[5]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[6]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[7]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[8]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[9]).rgb,"
|
||||
"texture(texID, inputPositionsVarying[10]).rgb"
|
||||
");"
|
||||
|
||||
"vec4 luminance[] = vec4[]("
|
||||
"vec4(samples[0].r, samples[1].r, samples[2].r, samples[3].r),"
|
||||
"vec4(samples[4].r, samples[5].r, samples[6].r, samples[7].r),"
|
||||
"vec4(samples[8].r, samples[9].r, samples[10].r, 0.0)"
|
||||
");"
|
||||
|
||||
"fragColour = vec3("
|
||||
"dot(vec3("
|
||||
"dot(luminance[0], weights[0]),"
|
||||
"dot(luminance[1], weights[1]),"
|
||||
"dot(luminance[2], weights[2])"
|
||||
"), vec3(1.0)),"
|
||||
"samples[5].gb"
|
||||
");"
|
||||
"}", false, false);
|
||||
}
|
||||
|
||||
std::unique_ptr<IntermediateShader> IntermediateShader::make_rgb_filter_shader()
|
||||
{
|
||||
return make_shader(
|
||||
@ -404,44 +336,53 @@ void IntermediateShader::set_filter_coefficients(float sampling_rate, float cuto
|
||||
// Perform a linear search for the highest number of taps we can use with 11 samples.
|
||||
GLfloat weights[12];
|
||||
GLfloat offsets[5];
|
||||
unsigned int taps = 21;
|
||||
unsigned int taps = 11;
|
||||
// unsigned int taps = 21;
|
||||
while(1)
|
||||
{
|
||||
float coefficients[21];
|
||||
SignalProcessing::FIRFilter luminance_filter(taps, sampling_rate, 0.0f, cutoff_frequency, SignalProcessing::FIRFilter::DefaultAttenuation);
|
||||
luminance_filter.get_coefficients(coefficients);
|
||||
|
||||
int sample = 0;
|
||||
int c = 0;
|
||||
// int sample = 0;
|
||||
// int c = 0;
|
||||
memset(weights, 0, sizeof(float)*12);
|
||||
memset(offsets, 0, sizeof(float)*5);
|
||||
|
||||
int halfSize = (taps >> 1);
|
||||
while(c < halfSize && sample < 5)
|
||||
for(int c = 0; c < taps; c++)
|
||||
{
|
||||
offsets[sample] = (float)(halfSize - c);
|
||||
if((coefficients[c] < 0.0f) == (coefficients[c+1] < 0.0f) && c+1 < (taps >> 1))
|
||||
{
|
||||
weights[sample] = coefficients[c] + coefficients[c+1];
|
||||
offsets[sample] -= (coefficients[c+1] / weights[sample]);
|
||||
c += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
weights[sample] = coefficients[c];
|
||||
c++;
|
||||
}
|
||||
sample ++;
|
||||
}
|
||||
if(c == halfSize) // i.e. we finished combining inputs before we ran out of space
|
||||
{
|
||||
weights[sample] = coefficients[c];
|
||||
for(int c = 0; c < sample; c++)
|
||||
{
|
||||
weights[sample+c+1] = weights[sample-c-1];
|
||||
}
|
||||
break;
|
||||
if(c < 5) offsets[c] = (halfSize - c);
|
||||
weights[c] = coefficients[c];
|
||||
}
|
||||
break;
|
||||
|
||||
// int halfSize = (taps >> 1);
|
||||
// while(c < halfSize && sample < 5)
|
||||
// {
|
||||
// offsets[sample] = (float)(halfSize - c);
|
||||
// if((coefficients[c] < 0.0f) == (coefficients[c+1] < 0.0f) && c+1 < (taps >> 1))
|
||||
// {
|
||||
// weights[sample] = coefficients[c] + coefficients[c+1];
|
||||
// offsets[sample] -= (coefficients[c+1] / weights[sample]);
|
||||
// c += 2;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// weights[sample] = coefficients[c];
|
||||
// c++;
|
||||
// }
|
||||
// sample ++;
|
||||
// }
|
||||
// if(c == halfSize) // i.e. we finished combining inputs before we ran out of space
|
||||
// {
|
||||
// weights[sample] = coefficients[c];
|
||||
// for(int c = 0; c < sample; c++)
|
||||
// {
|
||||
// weights[sample+c+1] = weights[sample-c-1];
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
taps -= 2;
|
||||
}
|
||||
|
||||
@ -454,10 +395,9 @@ void IntermediateShader::set_separation_frequency(float sampling_rate, float col
|
||||
set_filter_coefficients(sampling_rate, colour_burst_frequency);
|
||||
}
|
||||
|
||||
void IntermediateShader::set_phase_cycles_per_sample(float phase_cycles_per_sample, bool extend_runs_to_full_cycle)
|
||||
void IntermediateShader::set_extension(float extension)
|
||||
{
|
||||
set_uniform("phaseCyclesPerTick", (GLfloat)phase_cycles_per_sample);
|
||||
set_uniform("extension", extend_runs_to_full_cycle ? ceilf(1.0f / phase_cycles_per_sample) : 0.0f);
|
||||
set_uniform("extension", extension);
|
||||
}
|
||||
|
||||
void IntermediateShader::set_colour_conversion_matrices(float *fromRGB, float *toRGB)
|
||||
@ -465,3 +405,15 @@ void IntermediateShader::set_colour_conversion_matrices(float *fromRGB, float *t
|
||||
set_uniform_matrix("lumaChromaToRGB", 3, false, toRGB);
|
||||
set_uniform_matrix("rgbToLumaChroma", 3, false, fromRGB);
|
||||
}
|
||||
|
||||
void IntermediateShader::set_width_scalers(float input_scaler, float output_scaler)
|
||||
{
|
||||
set_uniform("widthScalers", input_scaler, output_scaler);
|
||||
}
|
||||
|
||||
void IntermediateShader::set_is_double_height(bool is_double_height, float input_offset, float output_offset)
|
||||
{
|
||||
set_uniform("textureHeightDivisor", is_double_height ? 2.0f : 1.0f);
|
||||
set_uniform("inputVerticalOffset", input_offset);
|
||||
set_uniform("outputVerticalOffset", output_offset);
|
||||
}
|
||||
|
@ -44,11 +44,6 @@ public:
|
||||
*/
|
||||
static std::unique_ptr<IntermediateShader> make_chroma_filter_shader();
|
||||
|
||||
/*!
|
||||
Constructs and returns an intermediate shader that will filter R while passing through G and B unchanged.
|
||||
*/
|
||||
static std::unique_ptr<IntermediateShader> make_luma_filter_shader();
|
||||
|
||||
/*!
|
||||
Constructs and returns an intermediate shader that will filter R, G and B.
|
||||
*/
|
||||
@ -81,13 +76,24 @@ public:
|
||||
geometry should be extended so that a complete colour cycle is included at both the beginning and end,
|
||||
to occur upon the next `bind`.
|
||||
*/
|
||||
void set_phase_cycles_per_sample(float phase_cycles_per_sample, bool extend_runs_to_full_cycle);
|
||||
void set_extension(float extension);
|
||||
|
||||
/*!
|
||||
Queues setting the matrices that convert between RGB and chrominance/luminance to occur on the next `bind`.
|
||||
*/
|
||||
void set_colour_conversion_matrices(float *fromRGB, float *toRGB);
|
||||
|
||||
/*!
|
||||
Sets the proportions of the input and output areas that should be considered the whole width — 1.0 means use all available
|
||||
space, 0.5 means use half, etc.
|
||||
*/
|
||||
void set_width_scalers(float input_scaler, float output_scaler);
|
||||
|
||||
/*!
|
||||
Sets source and target vertical offsets.
|
||||
*/
|
||||
void set_is_double_height(bool is_double_height, float input_offset = 0.0f, float output_offset = 0.0f);
|
||||
|
||||
private:
|
||||
static std::unique_ptr<IntermediateShader> make_shader(const char *fragment_shader, bool use_usampler, bool input_is_inputPosition);
|
||||
};
|
||||
|
@ -38,6 +38,8 @@ std::unique_ptr<OutputShader> OutputShader::make_shader(const char *fragment_met
|
||||
"uniform vec2 positionConversion;"
|
||||
"uniform vec2 scanNormal;"
|
||||
"uniform %s texID;"
|
||||
"uniform float inputScaler;"
|
||||
"uniform int textureHeightDivisor;"
|
||||
|
||||
"out float lateralVarying;"
|
||||
"out vec2 srcCoordinatesVarying;"
|
||||
@ -52,9 +54,10 @@ std::unique_ptr<OutputShader> OutputShader::make_shader(const char *fragment_met
|
||||
"lateralVarying = lateral - 0.5;"
|
||||
|
||||
"vec2 vSrcCoordinates = vec2(x, vertical.y);"
|
||||
"ivec2 textureSize = textureSize(texID, 0);"
|
||||
"ivec2 textureSize = textureSize(texID, 0) * ivec2(1, textureHeightDivisor);"
|
||||
"iSrcCoordinatesVarying = vSrcCoordinates;"
|
||||
"srcCoordinatesVarying = vec2(vSrcCoordinates.x / textureSize.x, (vSrcCoordinates.y + 0.5) / textureSize.y);"
|
||||
"srcCoordinatesVarying = vec2(inputScaler * vSrcCoordinates.x / textureSize.x, (vSrcCoordinates.y + 0.5) / textureSize.y);"
|
||||
"srcCoordinatesVarying.x = srcCoordinatesVarying.x - mod(srcCoordinatesVarying.x, 1.0 / textureSize.x);"
|
||||
|
||||
"vec2 vPosition = vec2(x, vertical.x);"
|
||||
"vec2 floatingPosition = (vPosition / positionConversion) + lateral * scanNormal;"
|
||||
@ -117,3 +120,13 @@ void OutputShader::set_timing(unsigned int height_of_display, unsigned int cycle
|
||||
set_uniform("scanNormal", scan_normal[0], scan_normal[1]);
|
||||
set_uniform("positionConversion", (GLfloat)horizontal_scan_period, (GLfloat)vertical_scan_period / (GLfloat)vertical_period_divider);
|
||||
}
|
||||
|
||||
void OutputShader::set_input_width_scaler(float input_scaler)
|
||||
{
|
||||
set_uniform("inputScaler", input_scaler);
|
||||
}
|
||||
|
||||
void OutputShader::set_origin_is_double_height(bool is_double_height)
|
||||
{
|
||||
set_uniform("textureHeightDivisor", is_double_height ? 2 : 1);
|
||||
}
|
||||
|
@ -58,6 +58,16 @@ public:
|
||||
to occur upon the next `bind`.
|
||||
*/
|
||||
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);
|
||||
|
||||
/*!
|
||||
*/
|
||||
void set_origin_is_double_height(bool is_double_height);
|
||||
|
||||
/*!
|
||||
Sets the proportion of the input area that should be considered the whole width — 1.0 means use all available
|
||||
space, 0.5 means use half, etc.
|
||||
*/
|
||||
void set_input_width_scaler(float input_scaler);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -156,7 +156,8 @@ void Shader::set_uniform(const std::string &name, GLint value1, GLint value2)
|
||||
void Shader::set_uniform(const std::string &name, GLfloat value1, GLfloat value2)
|
||||
{
|
||||
enqueue_function([name, value1, value2, this] {
|
||||
glUniform2f(location(), value1, value2);
|
||||
GLint location = location();
|
||||
glUniform2f(location, value1, value2);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
using namespace OpenGL;
|
||||
|
||||
TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit) :
|
||||
TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit, GLint mag_filter) :
|
||||
_width(width),
|
||||
_height(height),
|
||||
_pixel_shader(nullptr),
|
||||
@ -33,8 +33,8 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit)
|
||||
uint8_t *blank_buffer = (uint8_t *)calloc((size_t)(_expanded_width * _expanded_height), 4);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)_expanded_width, (GLsizei)_expanded_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, blank_buffer);
|
||||
free(blank_buffer);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
|
||||
|
||||
|
@ -30,7 +30,7 @@ class TextureTarget {
|
||||
@param height The height of target to create.
|
||||
@param texture_unit A texture unit on which to bind the texture.
|
||||
*/
|
||||
TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit);
|
||||
TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit, GLint mag_filter);
|
||||
~TextureTarget();
|
||||
|
||||
/*!
|
||||
|
Loading…
Reference in New Issue
Block a user