diff --git a/Components/AY38910/AY38910.cpp b/Components/AY38910/AY38910.cpp index 93534d872..2ac9d3177 100644 --- a/Components/AY38910/AY38910.cpp +++ b/Components/AY38910/AY38910.cpp @@ -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() diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 47df984ea..ea96ea83c 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -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(); } diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 8e7694761..f1d0c3f88 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -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); diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 0b07910f9..af06af273 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -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_; diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index b7fefe8b9..54f14edbe 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -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 diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 6296b3e61..2b471fa3d 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -6,8 +6,10 @@ // #include "CRT.hpp" -#include -#include + +#include +#include +#include #include "CRTOpenGL.hpp" #include "../../../SignalProcessing/FIRFilter.hpp" @@ -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 new_framebuffer(new OpenGL::TextureTarget((GLsizei)output_width, (GLsizei)output_height, pixel_accumulation_texture_unit)); + std::unique_ptr 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 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 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); } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 2c8718eac..b14d49e28 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -66,13 +66,19 @@ class OpenGLOutputBuilder { GLsizei composite_src_output_y_; std::unique_ptr output_shader_program_; - std::unique_ptr composite_input_shader_program_, composite_separation_filter_program_, composite_y_filter_shader_program_, composite_chrominance_filter_shader_program_; - std::unique_ptr rgb_input_shader_program_, rgb_filter_shader_program_; - OpenGL::TextureTarget composite_texture_; // receives raw composite levels - OpenGL::TextureTarget separated_texture_; // receives 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 composite_input_shader_program_; + std::unique_ptr composite_separation_filter_program_; + std::unique_ptr composite_chrominance_filter_shader_program_; + + std::unique_ptr rgb_input_shader_program_; + std::unique_ptr rgb_filter_shader_program_; + + std::unique_ptr composite_texture_; // receives raw composite levels + std::unique_ptr separated_texture_; // receives filtered Y in the R channel plus unfiltered but demodulated chrominance in G and B + std::unique_ptr filtered_texture_; // receives filtered YIQ or YUV + + std::unique_ptr work_texture_; // used for all intermediate rendering if texture fences are supported std::unique_ptr framebuffer_; // the current pixel output @@ -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_. diff --git a/Outputs/CRT/Internals/OpenGL.hpp b/Outputs/CRT/Internals/OpenGL.hpp index 4d373546b..629ebd501 100644 --- a/Outputs/CRT/Internals/OpenGL.hpp +++ b/Outputs/CRT/Internals/OpenGL.hpp @@ -15,6 +15,7 @@ #else #include #include + #include #endif #endif diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp index b3ad6f5a8..870eef24f 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp @@ -40,11 +40,14 @@ std::unique_ptr 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::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::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::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::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::make_chroma_filter_shade "}", false, false); } -std::unique_ptr 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::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); +} diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp index a419da8be..13dedc2c4 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp @@ -44,11 +44,6 @@ public: */ static std::unique_ptr 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 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 make_shader(const char *fragment_shader, bool use_usampler, bool input_is_inputPosition); }; diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.cpp b/Outputs/CRT/Internals/Shaders/OutputShader.cpp index e139c954d..ea31acebd 100644 --- a/Outputs/CRT/Internals/Shaders/OutputShader.cpp +++ b/Outputs/CRT/Internals/Shaders/OutputShader.cpp @@ -38,6 +38,8 @@ std::unique_ptr 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::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); +} diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.hpp b/Outputs/CRT/Internals/Shaders/OutputShader.hpp index 9b4b7db39..0df8143f4 100644 --- a/Outputs/CRT/Internals/Shaders/OutputShader.hpp +++ b/Outputs/CRT/Internals/Shaders/OutputShader.hpp @@ -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); }; } diff --git a/Outputs/CRT/Internals/Shaders/Shader.cpp b/Outputs/CRT/Internals/Shaders/Shader.cpp index df96c14ab..15b8bb43c 100644 --- a/Outputs/CRT/Internals/Shaders/Shader.cpp +++ b/Outputs/CRT/Internals/Shaders/Shader.cpp @@ -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); }); } diff --git a/Outputs/CRT/Internals/TextureTarget.cpp b/Outputs/CRT/Internals/TextureTarget.cpp index 6a6e5f426..54d2ede35 100644 --- a/Outputs/CRT/Internals/TextureTarget.cpp +++ b/Outputs/CRT/Internals/TextureTarget.cpp @@ -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); diff --git a/Outputs/CRT/Internals/TextureTarget.hpp b/Outputs/CRT/Internals/TextureTarget.hpp index 7e4b9dfef..aab1cc203 100644 --- a/Outputs/CRT/Internals/TextureTarget.hpp +++ b/Outputs/CRT/Internals/TextureTarget.hpp @@ -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(); /*!