From 61f2d34b4b34791d346f3a9954975e4e67229f05 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sat, 27 Oct 2012 19:57:18 +0200 Subject: [PATCH] Implement wrapping of the resampling parameter texture. Effectively a no-op frame rate-wise on my machine, but it saves some texture RAM. --- resample_effect.cpp | 34 +++++++++++++++++++++++++++------- resample_effect.frag | 10 ++++++---- resample_effect.h | 3 ++- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/resample_effect.cpp b/resample_effect.cpp index c0e82f1..5b01686 100644 --- a/resample_effect.cpp +++ b/resample_effect.cpp @@ -29,6 +29,17 @@ float lanczos_weight(float x, float a) } } +// Euclid's algorithm, from Wikipedia. +unsigned gcd(unsigned a, unsigned b) +{ + while (b != 0) { + unsigned t = b; + b = a % b; + a = t; + } + return a; +} + } // namespace ResampleEffect::ResampleEffect() @@ -143,7 +154,6 @@ std::string SingleResamplePassEffect::output_fragment_shader() // For horizontal scaling, we fill in the exact same texture; // the shader just interprets is differently. // -// TODO: Support optimization of wrapping the sample texture. // TODO: Support optimization using free linear sampling, like in BlurEffect. void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) { @@ -160,6 +170,16 @@ void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const std assert(false); } + + // For many resamplings (e.g. 640 -> 1280), we will end up with the same + // set of samples over and over again in a loop. Thus, we can compute only + // the first such loop, and then ask the card to repeat the texture for us. + // This is both easier on the texture cache and lowers our CPU cost for + // generating the kernel somewhat. + num_loops = gcd(src_size, dst_size); + slice_height = 1.0f / num_loops; + unsigned dst_samples = dst_size / num_loops; + // Sample the kernel in the right place. A diagram with a triangular kernel // (corresponding to linear filtering, and obviously with radius 1) // for easier ASCII art drawing: @@ -212,8 +232,8 @@ void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const std float radius_scaling_factor = std::min(float(dst_size) / float(src_size), 1.0f); int int_radius = lrintf(LANCZOS_RADIUS / radius_scaling_factor); src_samples = int_radius * 2 + 1; - float *weights = new float[dst_size * src_samples * 2]; - for (unsigned y = 0; y < dst_size; ++y) { + float *weights = new float[dst_samples * src_samples * 2]; + for (unsigned y = 0; y < dst_samples; ++y) { // Find the point around which we want to sample the source image, // compensating for differing pixel centers as the scale changes. float center_src_y = (y + 0.5f) * float(src_size) / float(dst_size) - 0.5f; @@ -228,8 +248,7 @@ void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const std } } - // Encode as a two-component texture. Note the GL_REPEAT, which is not relevant - // right now, but will be later. + // Encode as a two-component texture. Note the GL_REPEAT. glActiveTexture(GL_TEXTURE0 + *sampler_num); check_error(); glBindTexture(GL_TEXTURE_2D, texnum); @@ -240,11 +259,10 @@ void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const std check_error(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); check_error(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, src_samples, dst_size, 0, GL_RG, GL_FLOAT, weights); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, src_samples, dst_samples, 0, GL_RG, GL_FLOAT, weights); check_error(); delete[] weights; - } void SingleResamplePassEffect::set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) @@ -270,6 +288,8 @@ void SingleResamplePassEffect::set_gl_state(GLuint glsl_program_num, const std:: set_uniform_int(glsl_program_num, prefix, "sample_tex", *sampler_num); ++sampler_num; set_uniform_int(glsl_program_num, prefix, "num_samples", src_samples); + set_uniform_float(glsl_program_num, prefix, "num_loops", num_loops); + set_uniform_float(glsl_program_num, prefix, "slice_height", slice_height); // Instructions for how to convert integer sample numbers to positions in the weight texture. set_uniform_float(glsl_program_num, prefix, "sample_x_scale", 1.0f / src_samples); diff --git a/resample_effect.frag b/resample_effect.frag index 6aac29d..a31df17 100644 --- a/resample_effect.frag +++ b/resample_effect.frag @@ -3,8 +3,10 @@ uniform sampler2D PREFIX(sample_tex); uniform int PREFIX(num_samples); +uniform float PREFIX(num_loops); uniform float PREFIX(sample_x_scale); uniform float PREFIX(sample_x_offset); +uniform float PREFIX(slice_height); // Sample a single weight. First fetch information about where to sample // and the weight from sample_tex, and then read the pixel itself. @@ -13,16 +15,16 @@ vec4 PREFIX(do_sample)(vec2 tc, int i) vec2 sample_tc; sample_tc.x = float(i) * PREFIX(sample_x_scale) + PREFIX(sample_x_offset); #if DIRECTION_VERTICAL - sample_tc.y = tc.y; + sample_tc.y = tc.y * PREFIX(num_loops); #else - sample_tc.y = tc.x; + sample_tc.y = tc.x * PREFIX(num_loops); #endif vec2 sample = texture2D(PREFIX(sample_tex), sample_tc).rg; #if DIRECTION_VERTICAL - tc.y = sample.g; + tc.y = sample.g + floor(sample_tc.y) * PREFIX(slice_height); #else - tc.x = sample.g; + tc.x = sample.g + floor(sample_tc.y) * PREFIX(slice_height); #endif return vec4(sample.r) * INPUT(tc); } diff --git a/resample_effect.h b/resample_effect.h index fbe5fc5..1424bbf 100644 --- a/resample_effect.h +++ b/resample_effect.h @@ -87,7 +87,8 @@ private: GLuint texnum; int input_width, input_height, output_width, output_height; int last_input_width, last_input_height, last_output_width, last_output_height; - int src_samples; + int src_samples, num_loops; + float slice_height; }; #endif // !defined(_RESAMPLE_EFFECT_H) -- 2.39.2