Make combine_two_samples() into a template instead of having manual rounding checks.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 21 Feb 2015 14:52:54 +0000 (15:52 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 21 Feb 2015 14:52:54 +0000 (15:52 +0100)
blur_effect.cpp
resample_effect.cpp
util.cpp
util.h

index 6e5e843..29e46b0 100644 (file)
@@ -199,7 +199,7 @@ void SingleBlurPassEffect::set_gl_state(GLuint glsl_program_num, const string &p
                float pos1 = base_pos / (float)size;
                float pos2 = (base_pos + 1) / (float)size;
                float pos, total_weight;
-               combine_two_samples(w1, w2, pos1, pos2, size, COMBINE_DO_NOT_ROUND, &pos, &total_weight, NULL);
+               combine_two_samples(w1, w2, pos1, pos2, size, &pos, &total_weight, NULL);
 
                samples[2 * i + 0] = pos;
                samples[2 * i + 1] = total_weight;
index 3ccb2fd..9838cd4 100644 (file)
@@ -55,13 +55,15 @@ unsigned gcd(unsigned a, unsigned b)
        return a;
 }
 
-unsigned combine_samples(Tap<float> *src, Tap<float> *dst, unsigned src_size, unsigned num_src_samples, unsigned max_samples_saved)
+template<class DestFloat>
+unsigned combine_samples(Tap<float> *src, Tap<DestFloat> *dst, unsigned src_size, unsigned num_src_samples, unsigned max_samples_saved)
 {
        unsigned num_samples_saved = 0;
        for (unsigned i = 0, j = 0; i < num_src_samples; ++i, ++j) {
                // Copy the sample directly; it will be overwritten later if we can combine.
                if (dst != NULL) {
-                       dst[j] = src[i];
+                       dst[j].weight = convert_float<float, DestFloat>(src[i].weight);
+                       dst[j].pos = convert_float<float, DestFloat>(src[i].pos);
                }
 
                if (i == num_src_samples - 1) {
@@ -85,8 +87,9 @@ unsigned combine_samples(Tap<float> *src, Tap<float> *dst, unsigned src_size, un
                float pos2 = src[i + 1].pos;
                assert(pos2 > pos1);
 
-               float pos, total_weight, sum_sq_error;
-               combine_two_samples(w1, w2, pos1, pos2, src_size, COMBINE_ROUND_TO_FP16, &pos, &total_weight, &sum_sq_error);
+               fp16_int_t pos, total_weight;
+               float sum_sq_error;
+               combine_two_samples(w1, w2, pos1, pos2, src_size, &pos, &total_weight, &sum_sq_error);
 
                // If the interpolation error is larger than that of about sqrt(2) of
                // a level at 8-bit precision, don't combine. (You'd think 1.0 was enough,
@@ -403,16 +406,14 @@ void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const str
        // The greedy strategy for combining samples is optimal.
        src_bilinear_samples = 0;
        for (unsigned y = 0; y < dst_samples; ++y) {
-               unsigned num_samples_saved = combine_samples(weights + y * src_samples, NULL, src_size, src_samples, UINT_MAX);
+               unsigned num_samples_saved = combine_samples<fp16_int_t>(weights + y * src_samples, NULL, src_size, src_samples, UINT_MAX);
                src_bilinear_samples = max<int>(src_bilinear_samples, src_samples - num_samples_saved);
        }
 
        // Now that we know the right width, actually combine the samples.
-       Tap<float> *bilinear_weights = new Tap<float>[dst_samples * src_bilinear_samples];
-       Tap<fp16_int_t> *bilinear_weights_fp16 = new Tap<fp16_int_t>[dst_samples * src_bilinear_samples];
+       Tap<fp16_int_t> *bilinear_weights = new Tap<fp16_int_t>[dst_samples * src_bilinear_samples];
        for (unsigned y = 0; y < dst_samples; ++y) {
-               Tap<float> *bilinear_weights_ptr = bilinear_weights + y * src_bilinear_samples;
-               Tap<fp16_int_t> *bilinear_weights_fp16_ptr = bilinear_weights_fp16 + y * src_bilinear_samples;
+               Tap<fp16_int_t> *bilinear_weights_ptr = bilinear_weights + y * src_bilinear_samples;
                unsigned num_samples_saved = combine_samples(
                        weights + y * src_samples,
                        bilinear_weights_ptr,
@@ -421,22 +422,16 @@ void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const str
                        src_samples - src_bilinear_samples);
                assert(int(src_samples) - int(num_samples_saved) == src_bilinear_samples);
 
-               // Convert to fp16.
-               for (int i = 0; i < src_bilinear_samples; ++i) {
-                       bilinear_weights_fp16_ptr[i].weight = fp64_to_fp16(bilinear_weights_ptr[i].weight);
-                       bilinear_weights_fp16_ptr[i].pos    = fp64_to_fp16(bilinear_weights_ptr[i].pos);
-               }
-
                // Normalize so that the sum becomes one. Note that we do it twice;
                // this sometimes helps a tiny little bit when we have many samples.
                for (int normalize_pass = 0; normalize_pass < 2; ++normalize_pass) {
                        double sum = 0.0;
                        for (int i = 0; i < src_bilinear_samples; ++i) {
-                               sum += fp16_to_fp64(bilinear_weights_fp16_ptr[i].weight);
+                               sum += fp16_to_fp64(bilinear_weights_ptr[i].weight);
                        }
                        for (int i = 0; i < src_bilinear_samples; ++i) {
-                               bilinear_weights_fp16_ptr[i].weight = fp64_to_fp16(
-                                       fp16_to_fp64(bilinear_weights_fp16_ptr[i].weight) / sum);
+                               bilinear_weights_ptr[i].weight = fp64_to_fp16(
+                                       fp16_to_fp64(bilinear_weights_ptr[i].weight) / sum);
                        }
                }
        }
@@ -452,12 +447,11 @@ void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const str
        check_error();
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        check_error();
-       glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, src_bilinear_samples, dst_samples, 0, GL_RG, GL_HALF_FLOAT, bilinear_weights_fp16);
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, src_bilinear_samples, dst_samples, 0, GL_RG, GL_HALF_FLOAT, bilinear_weights);
        check_error();
 
        delete[] weights;
        delete[] bilinear_weights;
-       delete[] bilinear_weights_fp16;
 }
 
 void SingleResamplePassEffect::set_gl_state(GLuint glsl_program_num, const string &prefix, unsigned *sampler_num)
index 7fbc67a..b12ee80 100644 (file)
--- a/util.cpp
+++ b/util.cpp
@@ -158,8 +158,9 @@ string output_glsl_mat3(const string &name, const Eigen::Matrix3d &m)
        return buf;
 }
 
-void combine_two_samples(float w1, float w2, float pos1, float pos2, unsigned size, CombineRoundingBehavior rounding_behavior,
-                         float *offset, float *total_weight, float *sum_sq_error)
+template<class DestFloat>
+void combine_two_samples(float w1, float w2, float pos1, float pos2, unsigned size,
+                         DestFloat *offset, DestFloat *total_weight, float *sum_sq_error)
 {
        assert(movit_initialized);
        assert(w1 * w2 >= 0.0f);  // Should not have differing signs.
@@ -170,14 +171,9 @@ void combine_two_samples(float w1, float w2, float pos1, float pos2, unsigned si
                z = w2 / (w1 + w2);
        }
 
-       *offset = pos1 + z * (pos2 - pos1);
-       if (rounding_behavior == COMBINE_ROUND_TO_FP16) {       
-               // Round to fp16. Note that this might take z outside the 0..1 range.
-               *offset = fp16_to_fp64(fp64_to_fp16(*offset));
-               z = (*offset - pos1) / (pos2 - pos1);
-       } else {
-               assert(rounding_behavior == COMBINE_DO_NOT_ROUND);
-       }
+       // Round to the desired precision. Note that this might take z outside the 0..1 range.
+       *offset = from_fp64<DestFloat>(pos1 + z * (pos2 - pos1));
+       z = (to_fp64(*offset) - pos1) / (pos2 - pos1);
 
        // Round to the minimum number of bits we have measured earlier.
        // The card will do this for us anyway, but if we know what the real z
@@ -208,6 +204,15 @@ void combine_two_samples(float w1, float w2, float pos1, float pos2, unsigned si
        }
 }
 
+// Explicit instantiations.
+template
+void combine_two_samples<float>(float w1, float w2, float pos1, float pos2, unsigned size,
+                                float *offset, float *total_weight, float *sum_sq_error);
+
+template
+void combine_two_samples<fp16_int_t>(float w1, float w2, float pos1, float pos2, unsigned size,
+                                     fp16_int_t *offset, fp16_int_t *total_weight, float *sum_sq_error);
+
 GLuint fill_vertex_attribute(GLuint glsl_program_num, const string &attribute_name, GLint size, GLenum type, GLsizeiptr data_size, const GLvoid *data)
 {
        int attrib = glGetAttribLocation(glsl_program_num, attribute_name.c_str());
diff --git a/util.h b/util.h
index a89d3a2..e4474d3 100644 (file)
--- a/util.h
+++ b/util.h
@@ -62,8 +62,9 @@ enum CombineRoundingBehavior {
 // is COMBINE_ROUND_TO_FP16, the coordinate is assumed to be stored as a
 // rounded fp16 value. This enables more precise calculation of total_weight
 // and sum_sq_error.
-void combine_two_samples(float w1, float w2, float pos1, float pos2, unsigned size, CombineRoundingBehavior rounding_behavior,
-                         float *offset, float *total_weight, float *sum_sq_error);
+template<class DestFloat>
+void combine_two_samples(float w1, float w2, float pos1, float pos2, unsigned size,
+                         DestFloat *offset, DestFloat *total_weight, float *sum_sq_error);
 
 // Create a VBO with the given data, and bind it to the vertex attribute
 // with name <attribute_name>. Returns the VBO number.