mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-10 12:29:01 +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)
|
void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||||
{
|
{
|
||||||
int c = 0;
|
int c = 0;
|
||||||
while((master_divider_&15) && c < number_of_samples)
|
while((master_divider_&7) && c < number_of_samples)
|
||||||
{
|
{
|
||||||
target[c] = output_volume_;
|
target[c] = output_volume_;
|
||||||
master_divider_++;
|
master_divider_++;
|
||||||
@ -131,7 +131,7 @@ void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
|
|||||||
|
|
||||||
evaluate_output_volume();
|
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_;
|
target[c] = output_volume_;
|
||||||
c++;
|
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()
|
void AY38910::evaluate_output_volume()
|
||||||
|
@ -71,7 +71,7 @@ void Machine::setup_output(float aspect_ratio)
|
|||||||
"uint y = c & 14u;"
|
"uint y = c & 14u;"
|
||||||
"uint iPhase = (c >> 4);"
|
"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);"
|
"return mix(float(y) / 14.0, step(1, iPhase) * cos(phase + phaseOffset), amplitude);"
|
||||||
"}");
|
"}");
|
||||||
speaker_->set_input_rate((float)(get_clock_rate() / 38.0));
|
speaker_->set_input_rate((float)(get_clock_rate() / 38.0));
|
||||||
@ -777,5 +777,6 @@ void Machine::update_audio()
|
|||||||
void Machine::synchronise()
|
void Machine::synchronise()
|
||||||
{
|
{
|
||||||
update_audio();
|
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."
|
// for horizontal retrace and 500 to 750 µs for vertical retrace in NTSC and PAL TV."
|
||||||
|
|
||||||
time_multiplier_ = IntermediateBufferWidth / cycles_per_line;
|
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;
|
phase_numerator_ = 0;
|
||||||
colour_cycle_numerator_ = colour_cycle_numerator * time_multiplier_;
|
colour_cycle_numerator_ = colour_cycle_numerator;
|
||||||
phase_alternates_ = should_alternate;
|
phase_alternates_ = should_alternate;
|
||||||
is_alernate_line_ &= phase_alternates_;
|
is_alernate_line_ &= phase_alternates_;
|
||||||
unsigned int multiplied_cycles_per_line = cycles_per_line * time_multiplier_;
|
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_output_position_x2() (*(uint16_t *)&next_run[SourceVertexOffsetOfEnds + 2])
|
||||||
#define source_phase() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 0]
|
#define source_phase() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 0]
|
||||||
#define source_amplitude() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 2]
|
#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)
|
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_output_position_x1() = (uint16_t)horizontal_flywheel_->get_current_output_position();
|
||||||
source_phase() = colour_burst_phase_;
|
source_phase() = colour_burst_phase_;
|
||||||
source_amplitude() = colour_burst_amplitude_;
|
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
|
// 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_++;
|
frames_since_last_delegate_call_++;
|
||||||
if(frames_since_last_delegate_call_ == 20)
|
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());
|
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;
|
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)
|
if(horizontal_flywheel_->get_current_time() < (horizontal_flywheel_->get_standard_period() * 12) >> 6)
|
||||||
{
|
{
|
||||||
colour_burst_time_ = (uint16_t)horizontal_flywheel_->get_current_time();
|
unsigned int position_phase = (horizontal_flywheel_->get_current_time() * colour_cycle_numerator_ * 256) / phase_denominator_;
|
||||||
colour_burst_phase_ = scan->phase;
|
colour_burst_phase_ = (position_phase + scan->phase) & 255;
|
||||||
colour_burst_amplitude_ = scan->amplitude;
|
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 {
|
Scan scan {
|
||||||
.type = Scan::Type::ColourBurst,
|
.type = Scan::Type::ColourBurst,
|
||||||
.number_of_cycles = number_of_cycles,
|
.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
|
.amplitude = 32
|
||||||
};
|
};
|
||||||
output_scan(&scan);
|
output_scan(&scan);
|
||||||
|
@ -60,7 +60,6 @@ class CRT {
|
|||||||
void output_scan(const Scan *scan);
|
void output_scan(const Scan *scan);
|
||||||
|
|
||||||
uint8_t colour_burst_phase_, colour_burst_amplitude_;
|
uint8_t colour_burst_phase_, colour_burst_amplitude_;
|
||||||
uint16_t colour_burst_time_;
|
|
||||||
bool is_writing_composite_run_;
|
bool is_writing_composite_run_;
|
||||||
|
|
||||||
unsigned int phase_denominator_, phase_numerator_, colour_cycle_numerator_;
|
unsigned int phase_denominator_, phase_numerator_, colour_cycle_numerator_;
|
||||||
|
@ -36,7 +36,7 @@ const GLsizei InputBufferBuilderWidth = 2048;
|
|||||||
const GLsizei InputBufferBuilderHeight = 512;
|
const GLsizei InputBufferBuilderHeight = 512;
|
||||||
|
|
||||||
// This is the size of the intermediate buffers used during composite to RGB conversion
|
// 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;
|
const GLsizei IntermediateBufferHeight = 512;
|
||||||
|
|
||||||
// Some internal buffer sizes
|
// Some internal buffer sizes
|
||||||
|
@ -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"
|
||||||
@ -16,12 +18,14 @@
|
|||||||
using namespace Outputs::CRT;
|
using namespace Outputs::CRT;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
static const GLenum composite_texture_unit = GL_TEXTURE0;
|
static const GLenum source_data_texture_unit = GL_TEXTURE0;
|
||||||
static const GLenum separated_texture_unit = GL_TEXTURE1;
|
static const GLenum pixel_accumulation_texture_unit = GL_TEXTURE1;
|
||||||
static const GLenum filtered_y_texture_unit = GL_TEXTURE2;
|
|
||||||
static const GLenum filtered_texture_unit = GL_TEXTURE3;
|
static const GLenum composite_texture_unit = GL_TEXTURE2;
|
||||||
static const GLenum source_data_texture_unit = GL_TEXTURE4;
|
static const GLenum separated_texture_unit = GL_TEXTURE3;
|
||||||
static const GLenum pixel_accumulation_texture_unit = GL_TEXTURE5;
|
static const GLenum filtered_texture_unit = GL_TEXTURE4;
|
||||||
|
|
||||||
|
static const GLenum work_texture_unit = GL_TEXTURE2;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGLOutputBuilder::OpenGLOutputBuilder(size_t bytes_per_pixel) :
|
OpenGLOutputBuilder::OpenGLOutputBuilder(size_t bytes_per_pixel) :
|
||||||
@ -33,11 +37,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_y_texture_(IntermediateBufferWidth, IntermediateBufferHeight, filtered_y_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);
|
||||||
@ -47,6 +47,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, 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()
|
OpenGLOutputBuilder::~OpenGLOutputBuilder()
|
||||||
@ -57,6 +83,11 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder()
|
|||||||
free(rgb_shader_);
|
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)
|
void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty)
|
||||||
{
|
{
|
||||||
// lock down any other draw_frames
|
// 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
|
// make sure there's a target to draw to
|
||||||
if(!framebuffer_ || framebuffer_->get_height() != output_height || framebuffer_->get_width() != output_width)
|
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_)
|
if(framebuffer_)
|
||||||
{
|
{
|
||||||
new_framebuffer->bind_framebuffer();
|
new_framebuffer->bind_framebuffer();
|
||||||
@ -123,30 +154,29 @@ 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}
|
||||||
};
|
};
|
||||||
|
|
||||||
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)
|
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_);
|
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_TRIANGLE_STRIP, 0, 4, (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
|
||||||
@ -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);
|
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);
|
||||||
@ -211,7 +260,6 @@ void OpenGLOutputBuilder::reset_all_OpenGL_state()
|
|||||||
{
|
{
|
||||||
composite_input_shader_program_ = nullptr;
|
composite_input_shader_program_ = nullptr;
|
||||||
composite_separation_filter_program_ = nullptr;
|
composite_separation_filter_program_ = nullptr;
|
||||||
composite_y_filter_shader_program_ = nullptr;
|
|
||||||
composite_chrominance_filter_shader_program_ = nullptr;
|
composite_chrominance_filter_shader_program_ = nullptr;
|
||||||
rgb_input_shader_program_ = nullptr;
|
rgb_input_shader_program_ = nullptr;
|
||||||
rgb_filter_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)
|
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);
|
composite_shader_ = strdup(shader);
|
||||||
reset_all_OpenGL_state();
|
reset_all_OpenGL_state();
|
||||||
output_mutex_.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLOutputBuilder::set_rgb_sampling_function(const char *shader)
|
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);
|
rgb_shader_ = strdup(shader);
|
||||||
reset_all_OpenGL_state();
|
reset_all_OpenGL_state();
|
||||||
output_mutex_.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Program compilation
|
#pragma mark - Program compilation
|
||||||
@ -252,16 +298,25 @@ void OpenGLOutputBuilder::prepare_composite_input_shaders()
|
|||||||
composite_input_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
composite_input_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
||||||
|
|
||||||
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_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(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);
|
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()
|
void OpenGLOutputBuilder::prepare_rgb_input_shaders()
|
||||||
@ -295,7 +350,9 @@ 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);
|
||||||
|
// 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()
|
void OpenGLOutputBuilder::prepare_output_vertex_array()
|
||||||
@ -319,6 +376,7 @@ void OpenGLOutputBuilder::set_output_device(OutputDevice output_device)
|
|||||||
composite_src_output_y_ = 0;
|
composite_src_output_y_ = 0;
|
||||||
last_output_width_ = 0;
|
last_output_width_ = 0;
|
||||||
last_output_height_ = 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_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);
|
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()
|
void OpenGLOutputBuilder::set_timing_uniforms()
|
||||||
{
|
{
|
||||||
OpenGL::IntermediateShader *intermediate_shaders[] = {
|
const float colour_subcarrier_frequency = (float)colour_cycle_numerator_ / (float)colour_cycle_denominator_;
|
||||||
composite_input_shader_program_.get(),
|
const float output_width = get_composite_output_width();
|
||||||
composite_separation_filter_program_.get(),
|
const float sample_cycles_per_line = cycles_per_line_ / output_width;
|
||||||
composite_y_filter_shader_program_.get(),
|
|
||||||
composite_chrominance_filter_shader_program_.get()
|
if(composite_separation_filter_program_)
|
||||||
};
|
|
||||||
bool extends = false;
|
|
||||||
float phaseCyclesPerTick = (float)colour_cycle_numerator_ / (float)(colour_cycle_denominator_ * cycles_per_line_);
|
|
||||||
for(int c = 0; c < 3; c++)
|
|
||||||
{
|
{
|
||||||
if(intermediate_shaders[c]) intermediate_shaders[c]->set_phase_cycles_per_sample(phaseCyclesPerTick, extends);
|
composite_separation_filter_program_->set_width_scalers(output_width, output_width);
|
||||||
extends = true;
|
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_;
|
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_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
|
std::unique_ptr<OpenGL::IntermediateShader> composite_input_shader_program_;
|
||||||
OpenGL::TextureTarget separated_texture_; // receives unfiltered 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_y_texture_; // receives filtered Y in the R channel plus unfiltered chrominance in G and B
|
std::unique_ptr<OpenGL::IntermediateShader> composite_chrominance_filter_shader_program_;
|
||||||
OpenGL::TextureTarget filtered_texture_; // receives filtered YIQ or YUV
|
|
||||||
|
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
|
||||||
|
|
||||||
@ -88,6 +94,9 @@ class OpenGLOutputBuilder {
|
|||||||
void reset_all_OpenGL_state();
|
void reset_all_OpenGL_state();
|
||||||
|
|
||||||
GLsync fence_;
|
GLsync fence_;
|
||||||
|
float get_composite_output_width() const;
|
||||||
|
void set_output_shader_width();
|
||||||
|
bool get_is_television_output();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// These two are protected by output_mutex_.
|
// These two are protected by output_mutex_.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -40,11 +40,14 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
|
|||||||
"in vec2 ends;"
|
"in vec2 ends;"
|
||||||
"in vec3 phaseTimeAndAmplitude;"
|
"in vec3 phaseTimeAndAmplitude;"
|
||||||
|
|
||||||
"uniform float phaseCyclesPerTick;"
|
|
||||||
"uniform ivec2 outputTextureSize;"
|
"uniform ivec2 outputTextureSize;"
|
||||||
"uniform float extension;"
|
"uniform float extension;"
|
||||||
"uniform %s texID;"
|
"uniform %s texID;"
|
||||||
"uniform float offsets[5];"
|
"uniform float offsets[5];"
|
||||||
|
"uniform vec2 widthScalers;"
|
||||||
|
"uniform float inputVerticalOffset;"
|
||||||
|
"uniform float outputVerticalOffset;"
|
||||||
|
"uniform float textureHeightDivisor;"
|
||||||
|
|
||||||
"out vec2 phaseAndAmplitudeVarying;"
|
"out vec2 phaseAndAmplitudeVarying;"
|
||||||
"out vec2 inputPositionsVarying[11];"
|
"out vec2 inputPositionsVarying[11];"
|
||||||
@ -53,35 +56,51 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const char *
|
|||||||
|
|
||||||
"void main(void)"
|
"void main(void)"
|
||||||
"{"
|
"{"
|
||||||
|
// odd vertices are on the left, even on the right
|
||||||
"float extent = float(gl_VertexID & 1);"
|
"float extent = float(gl_VertexID & 1);"
|
||||||
|
"float longitudinal = float((gl_VertexID & 2) >> 1);"
|
||||||
|
|
||||||
"vec2 inputPosition = vec2(mix(inputStart.x, ends.x, extent), inputStart.y);"
|
// inputPosition.x is either inputStart.x or ends.x, depending on whether it is on the left or the right;
|
||||||
"vec2 outputPosition = vec2(mix(outputStart.x, ends.y, extent), outputStart.y);"
|
// 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);"
|
"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 extendedInputPosition = %s + extensionVector;"
|
||||||
"vec2 extendedOutputPosition = outputPosition + extensionVector;"
|
"vec2 extendedOutputPosition = outputPosition + extensionVector;"
|
||||||
|
|
||||||
|
// keep iInputPositionVarying in whole source pixels, scale mappedInputPosition to the ordinary normalised range
|
||||||
"vec2 textureSize = vec2(textureSize(texID, 0));"
|
"vec2 textureSize = vec2(textureSize(texID, 0));"
|
||||||
"iInputPositionVarying = extendedInputPosition;"
|
"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);"
|
// setup input positions spaced as per the supplied offsets; these are for filtering where required
|
||||||
"inputPositionsVarying[1] = mappedInputPosition - (vec2(offsets[1], 0.0) / textureSize);"
|
"inputPositionsVarying[0] = mappedInputPosition - (vec2(5.0, 0.0) / textureSize);"
|
||||||
"inputPositionsVarying[2] = mappedInputPosition - (vec2(offsets[2], 0.0) / textureSize);"
|
"inputPositionsVarying[1] = mappedInputPosition - (vec2(4.0, 0.0) / textureSize);"
|
||||||
"inputPositionsVarying[3] = mappedInputPosition - (vec2(offsets[3], 0.0) / textureSize);"
|
"inputPositionsVarying[2] = mappedInputPosition - (vec2(3.0, 0.0) / textureSize);"
|
||||||
"inputPositionsVarying[4] = mappedInputPosition - (vec2(offsets[4], 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[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);"
|
||||||
|
|
||||||
"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)
|
"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;"
|
"vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(1.0)/outputTextureSize;"
|
||||||
"gl_Position = vec4(eyePosition, 0.0, 1.0);"
|
"gl_Position = vec4(eyePosition, 0.0, 1.0);"
|
||||||
"}", sampler_type, input_variable);
|
"}", sampler_type, input_variable);
|
||||||
@ -172,7 +191,6 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_luma_separat
|
|||||||
|
|
||||||
"in vec2 phaseAndAmplitudeVarying;"
|
"in vec2 phaseAndAmplitudeVarying;"
|
||||||
"in vec2 inputPositionsVarying[11];"
|
"in vec2 inputPositionsVarying[11];"
|
||||||
"uniform vec4 weights[3];"
|
|
||||||
|
|
||||||
"out vec3 fragColour;"
|
"out vec3 fragColour;"
|
||||||
|
|
||||||
@ -180,37 +198,20 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_luma_separat
|
|||||||
|
|
||||||
"void main(void)"
|
"void main(void)"
|
||||||
"{"
|
"{"
|
||||||
"vec4 samples[3] = vec4[]("
|
"vec4 samples = vec4("
|
||||||
"vec4("
|
"texture(texID, inputPositionsVarying[3]).r,"
|
||||||
"texture(texID, inputPositionsVarying[0]).r,"
|
"texture(texID, inputPositionsVarying[4]).r,"
|
||||||
"texture(texID, inputPositionsVarying[1]).r,"
|
"texture(texID, inputPositionsVarying[5]).r,"
|
||||||
"texture(texID, inputPositionsVarying[2]).r,"
|
"texture(texID, inputPositionsVarying[6]).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"
|
|
||||||
")"
|
|
||||||
");"
|
");"
|
||||||
|
"float luminance = dot(samples, vec4(0.25));"
|
||||||
|
|
||||||
"float luminance = "
|
// define chroma to be whatever was here, minus luma
|
||||||
"dot(vec3("
|
"float chrominance = 0.5 * (samples.z - luminance) / phaseAndAmplitudeVarying.y;"
|
||||||
"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;"
|
|
||||||
"luminance /= (1.0 - 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));"
|
"vec2 quadrature = vec2(cos(phaseAndAmplitudeVarying.x), -sin(phaseAndAmplitudeVarying.x));"
|
||||||
"fragColour = vec3(luminance, vec2(0.5) + (chrominance * quadrature));"
|
"fragColour = vec3(luminance, vec2(0.5) + (chrominance * quadrature));"
|
||||||
"}",false, false);
|
"}",false, false);
|
||||||
@ -232,41 +233,18 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_filter_shade
|
|||||||
"void main(void)"
|
"void main(void)"
|
||||||
"{"
|
"{"
|
||||||
"vec3 samples[] = vec3[]("
|
"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[3]).rgb,"
|
||||||
"texture(texID, inputPositionsVarying[4]).rgb,"
|
"texture(texID, inputPositionsVarying[4]).rgb,"
|
||||||
"texture(texID, inputPositionsVarying[5]).rgb,"
|
"texture(texID, inputPositionsVarying[5]).rgb,"
|
||||||
"texture(texID, inputPositionsVarying[6]).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 chromaChannel1[] = vec4[]("
|
"vec4 chromaChannel1 = vec4(samples[0].g, samples[1].g, samples[2].g, samples[3].g);"
|
||||||
"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);"
|
||||||
"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)"
|
|
||||||
");"
|
|
||||||
|
|
||||||
"vec3 lumaChromaColour = vec3(samples[5].r,"
|
"vec3 lumaChromaColour = vec3(samples[2].r,"
|
||||||
"dot(vec3("
|
"dot(chromaChannel1, vec4(0.25)),"
|
||||||
"dot(chromaChannel1[0], weights[0]),"
|
"dot(chromaChannel2, vec4(0.25))"
|
||||||
"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 lumaChromaColourInRange = (lumaChromaColour - vec3(0.0, 0.5, 0.5)) * vec3(1.0, 2.0, 2.0);"
|
"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);
|
"}", 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()
|
std::unique_ptr<IntermediateShader> IntermediateShader::make_rgb_filter_shader()
|
||||||
{
|
{
|
||||||
return make_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.
|
// Perform a linear search for the highest number of taps we can use with 11 samples.
|
||||||
GLfloat weights[12];
|
GLfloat weights[12];
|
||||||
GLfloat offsets[5];
|
GLfloat offsets[5];
|
||||||
unsigned int taps = 21;
|
unsigned int taps = 11;
|
||||||
|
// unsigned int taps = 21;
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
float coefficients[21];
|
float coefficients[21];
|
||||||
SignalProcessing::FIRFilter luminance_filter(taps, sampling_rate, 0.0f, cutoff_frequency, SignalProcessing::FIRFilter::DefaultAttenuation);
|
SignalProcessing::FIRFilter luminance_filter(taps, sampling_rate, 0.0f, cutoff_frequency, SignalProcessing::FIRFilter::DefaultAttenuation);
|
||||||
luminance_filter.get_coefficients(coefficients);
|
luminance_filter.get_coefficients(coefficients);
|
||||||
|
|
||||||
int sample = 0;
|
// int sample = 0;
|
||||||
int c = 0;
|
// int c = 0;
|
||||||
memset(weights, 0, sizeof(float)*12);
|
memset(weights, 0, sizeof(float)*12);
|
||||||
memset(offsets, 0, sizeof(float)*5);
|
memset(offsets, 0, sizeof(float)*5);
|
||||||
|
|
||||||
int halfSize = (taps >> 1);
|
int halfSize = (taps >> 1);
|
||||||
while(c < halfSize && sample < 5)
|
for(int c = 0; c < taps; c++)
|
||||||
{
|
{
|
||||||
offsets[sample] = (float)(halfSize - c);
|
if(c < 5) offsets[c] = (halfSize - c);
|
||||||
if((coefficients[c] < 0.0f) == (coefficients[c+1] < 0.0f) && c+1 < (taps >> 1))
|
weights[c] = coefficients[c];
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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;
|
taps -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,10 +395,9 @@ void IntermediateShader::set_separation_frequency(float sampling_rate, float col
|
|||||||
set_filter_coefficients(sampling_rate, colour_burst_frequency);
|
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", extension);
|
||||||
set_uniform("extension", extend_runs_to_full_cycle ? ceilf(1.0f / phase_cycles_per_sample) : 0.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntermediateShader::set_colour_conversion_matrices(float *fromRGB, float *toRGB)
|
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("lumaChromaToRGB", 3, false, toRGB);
|
||||||
set_uniform_matrix("rgbToLumaChroma", 3, false, fromRGB);
|
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();
|
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.
|
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,
|
geometry should be extended so that a complete colour cycle is included at both the beginning and end,
|
||||||
to occur upon the next `bind`.
|
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`.
|
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);
|
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:
|
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);
|
||||||
};
|
};
|
||||||
|
@ -38,6 +38,8 @@ std::unique_ptr<OutputShader> OutputShader::make_shader(const char *fragment_met
|
|||||||
"uniform vec2 positionConversion;"
|
"uniform vec2 positionConversion;"
|
||||||
"uniform vec2 scanNormal;"
|
"uniform vec2 scanNormal;"
|
||||||
"uniform %s texID;"
|
"uniform %s texID;"
|
||||||
|
"uniform float inputScaler;"
|
||||||
|
"uniform int textureHeightDivisor;"
|
||||||
|
|
||||||
"out float lateralVarying;"
|
"out float lateralVarying;"
|
||||||
"out vec2 srcCoordinatesVarying;"
|
"out vec2 srcCoordinatesVarying;"
|
||||||
@ -52,9 +54,10 @@ std::unique_ptr<OutputShader> OutputShader::make_shader(const char *fragment_met
|
|||||||
"lateralVarying = lateral - 0.5;"
|
"lateralVarying = lateral - 0.5;"
|
||||||
|
|
||||||
"vec2 vSrcCoordinates = vec2(x, vertical.y);"
|
"vec2 vSrcCoordinates = vec2(x, vertical.y);"
|
||||||
"ivec2 textureSize = textureSize(texID, 0);"
|
"ivec2 textureSize = textureSize(texID, 0) * ivec2(1, textureHeightDivisor);"
|
||||||
"iSrcCoordinatesVarying = vSrcCoordinates;"
|
"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 vPosition = vec2(x, vertical.x);"
|
||||||
"vec2 floatingPosition = (vPosition / positionConversion) + lateral * scanNormal;"
|
"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("scanNormal", scan_normal[0], scan_normal[1]);
|
||||||
set_uniform("positionConversion", (GLfloat)horizontal_scan_period, (GLfloat)vertical_scan_period / (GLfloat)vertical_period_divider);
|
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`.
|
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_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)
|
void Shader::set_uniform(const std::string &name, GLfloat value1, GLfloat value2)
|
||||||
{
|
{
|
||||||
enqueue_function([name, value1, value2, this] {
|
enqueue_function([name, value1, value2, this] {
|
||||||
glUniform2f(location(), value1, value2);
|
GLint location = location();
|
||||||
|
glUniform2f(location, value1, value2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
using namespace OpenGL;
|
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),
|
_width(width),
|
||||||
_height(height),
|
_height(height),
|
||||||
_pixel_shader(nullptr),
|
_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);
|
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);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)_expanded_width, (GLsizei)_expanded_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, blank_buffer);
|
||||||
free(blank_buffer);
|
free(blank_buffer);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
|
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 height The height of target to create.
|
||||||
@param texture_unit A texture unit on which to bind the texture.
|
@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();
|
~TextureTarget();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
Loading…
Reference in New Issue
Block a user