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) {
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,
// 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,
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);
}
}
}
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)
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.
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
}
}
+// 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());
// 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.