From 632d739db8f8276e5553df71876579d691378ce8 Mon Sep 17 00:00:00 2001 From: David Banks Date: Tue, 6 Nov 2018 12:45:12 +0000 Subject: [PATCH] Pi Firmware: Autocal optimization pass uses stored data, so much faster Change-Id: I1807b7872c7677d7e37fca32294464da30feed14 --- src/cpld_normal.c | 85 +++++++++++++++++++++++++++++------------------ src/rgb_to_hdmi.c | 20 +++++++++++ 2 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/cpld_normal.c b/src/cpld_normal.c index 706be83d..aaa424a6 100644 --- a/src/cpld_normal.c +++ b/src/cpld_normal.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "defs.h" #include "cpld.h" #include "osd.h" @@ -207,6 +208,10 @@ static int cpld_get_version() { return cpld_version; } +static int sum_channels(int *rgb) { + return rgb[CHAN_RED] + rgb[CHAN_GREEN] + rgb[CHAN_BLUE]; +} + static void cpld_calibrate(int elk, int chars_per_line) { int min_i = 0; int metric; // this is a point value (at one sample offset) @@ -219,6 +224,8 @@ static void cpld_calibrate(int elk, int chars_per_line) { int *metrics; int *errors; + int rgb_metrics[8][NUM_OFFSETS * NUM_CHANNELS]; + if (mode7) { log_info("Calibrating mode 7"); range = 8; @@ -231,33 +238,41 @@ static void cpld_calibrate(int elk, int chars_per_line) { errors = &errors_default; } + // Measure the error metrics at all possible offset values min_metric = INT_MAX; config->half_px_delay = 0; config->full_px_delay = 0; - for (int i = 0; i < range; i++) { - for (int j = 0; j < NUM_OFFSETS; j++) { - config->sp_offset[j] = i; + printf("INFO: "); + for (int i = 0; i < NUM_OFFSETS; i++) { + printf("%6c", 'A' + i); + } + printf(" total\r\n"); + for (int value = 0; value < range; value++) { + for (int i = 0; i < NUM_OFFSETS; i++) { + config->sp_offset[i] = value; } write_config(config); rgb_metric = diff_N_frames_by_sample(NUM_CAL_FRAMES, mode7, elk, chars_per_line); - metric = 0; - printf("INFO: offset = %d: metrics = ", i); - for (int j = 0; j < NUM_OFFSETS; j++) { + printf("INFO: value = %d: metrics = ", value); + for (int i = 0; i < NUM_OFFSETS; i++) { int per_sample = 0; - for (int c = 0; c < NUM_CHANNELS; c++) { - per_sample += rgb_metric[j * NUM_CHANNELS + c]; + for (int j = 0; j < NUM_CHANNELS; j++) { + per_sample += rgb_metric[i * NUM_CHANNELS + j]; } metric += per_sample; printf("%6d", per_sample); } - printf("; total = %7d\r\n", metric); - metrics[i] = metric; + printf("%8d\r\n", metric); + metrics[value] = metric; osd_sp(config, 1, metric); if (metric < min_metric) { min_metric = metric; } + // Save the metrics in case the second phase is needed + memcpy(rgb_metrics[value], rgb_metric, NUM_CHANNELS * NUM_OFFSETS * sizeof(int)); } + // Use a 3 sample window to find the minimum and maximum min_win_metric = INT_MAX; for (int i = 0; i < range; i++) { @@ -271,62 +286,66 @@ static void cpld_calibrate(int elk, int chars_per_line) { } } } + // If the min metric is at the limit, make use of the half pixel delay if (mode7 && min_metric > 0 && (min_i <= 1 || min_i >= 6)) { log_info("Enabling half pixel delay"); config->half_px_delay = 1; min_i ^= 4; + // Swap the metrics as well + for (int i = 0; i < range; i++) { + for (int j = 0; j < NUM_OFFSETS * NUM_CHANNELS; j++) { + int tmp = rgb_metrics[i][j]; + rgb_metrics[i][j] = rgb_metrics[i ^ 4][j]; + rgb_metrics[i ^ 4][j] = tmp; + } + } } + // In all modes, start with the min metric for (int i = 0; i < NUM_OFFSETS; i++) { config->sp_offset[i] = min_i; } + log_sp(config); // If the metric is non zero, there is scope for further optimization in mode7 if (mode7 && min_metric > 0) { - int ref = min_metric; log_info("Optimizing calibration"); - log_sp(config); - log_debug("ref = %d", ref); for (int i = 0; i < NUM_OFFSETS; i++) { + // Start with current value of the sample point i + int value = config->sp_offset[i]; + // Look up the current metric for this value + int ref = sum_channels(rgb_metrics[value] + i * NUM_CHANNELS); + // Loop up the metric if we decrease this by one int left = INT_MAX; + if (value > 0) { + left = sum_channels(rgb_metrics[value - 1] + i * NUM_CHANNELS); + } + // Look up the metric if we increase this by one int right = INT_MAX; - if (config->sp_offset[i] > 0) { - config->sp_offset[i]--; - write_config(config); - rgb_metric = diff_N_frames(NUM_CAL_FRAMES, mode7, elk, chars_per_line); - left = rgb_metric[CHAN_RED] + rgb_metric[CHAN_GREEN] + rgb_metric[CHAN_BLUE]; - osd_sp(config, 1, left); - config->sp_offset[i]++; - } - if (config->sp_offset[i] < 7) { - config->sp_offset[i]++; - write_config(config); - rgb_metric = diff_N_frames(NUM_CAL_FRAMES, mode7, elk, chars_per_line); - right = rgb_metric[CHAN_RED] + rgb_metric[CHAN_GREEN] + rgb_metric[CHAN_BLUE]; - osd_sp(config, 1, right); - config->sp_offset[i]--; + if (value < 7) { + right = sum_channels(rgb_metrics[value + 1] + i * NUM_CHANNELS); } + // Make the actual decision if (left < right && left < ref) { config->sp_offset[i]--; - ref = left; - log_info("nudged %d left, metric = %d", i, ref); } else if (right < left && right < ref) { config->sp_offset[i]++; - ref = right; - log_info("nudged %d right, metric = %d", i, ref); } } + log_sp(config); } + // Determine mode 7 alignment if (mode7 && supports_delay) { write_config(config); config->full_px_delay = analyze_mode7_alignment(); } + // Perform a final test of errors write_config(config); rgb_metric = diff_N_frames(NUM_CAL_FRAMES, mode7, elk, chars_per_line); - *errors = rgb_metric[CHAN_RED] + rgb_metric[CHAN_GREEN] + rgb_metric[CHAN_BLUE]; + *errors = sum_channels(rgb_metric); osd_sp(config, 1, *errors); log_info("Calibration complete"); log_sp(config); diff --git a/src/rgb_to_hdmi.c b/src/rgb_to_hdmi.c index 462ab5f6..9fd14e3f 100644 --- a/src/rgb_to_hdmi.c +++ b/src/rgb_to_hdmi.c @@ -633,6 +633,26 @@ int *diff_N_frames_by_sample(int n, int mode7, int elk, int chars_per_line) { #ifdef INSTRUMENT_CAL t_compare += _get_cycle_counter() - t; #endif + // At this point the diffs correspond to the sample points in + // an unusual order: A F C B E D + // + // This happens for three reasons: + // - the CPLD starts with sample point B, so you get B C D E F A + // - the firmware skips the first quad, so you get F A B C D E + // - the frame buffer swaps odd and even pixels, so you get A F C B E D + // + // Mutate the result to correctly order the sample points: + // A F C B E D => A B C D E F + // + // Then the downstream algorithms don't have to worry + for (int j = 0; j < NUM_CHANNELS; j++) { + int f = diff[j + 1 * NUM_CHANNELS]; + int b = diff[j + 3 * NUM_CHANNELS]; + int d = diff[j + 5 * NUM_CHANNELS]; + diff[j + 1 * NUM_CHANNELS] = b; + diff[j + 3 * NUM_CHANNELS] = d; + diff[j + 5 * NUM_CHANNELS] = f; + } // Accumulate the result for (int j = 0; j < NUM_CHANNELS * NUM_OFFSETS; j++) {