From a910bca9e0f4f28b5972c41ce879f5a373c5d6d6 Mon Sep 17 00:00:00 2001 From: asintes Date: Tue, 5 Sep 2023 12:08:54 +0200 Subject: [PATCH] initial ultrafast commit --- modules/mlv_lite/mlv_lite.c | 239 ++++++++++++++--- modules/mlv_play/mlv_play.c | 4 +- src/raw.c | 517 ++++++++++++++++++++++++++++++++++-- src/raw.h | 48 +++- 4 files changed, 751 insertions(+), 57 deletions(-) diff --git a/modules/mlv_lite/mlv_lite.c b/modules/mlv_lite/mlv_lite.c index b5d00edf..f0938564 100755 --- a/modules/mlv_lite/mlv_lite.c +++ b/modules/mlv_lite/mlv_lite.c @@ -181,6 +181,14 @@ static CONFIG_INT("raw.preview", preview_mode, 2); #define PREVIEW_ML (preview_mode == 2) #define PREVIEW_HACKED (preview_mode == 3) +static CONFIG_INT( "raw.framed-preview-engine", framed_preview_engine, FRAMED_PREVIEW_PARAM__ENGINE__LEGACY ); +static CONFIG_INT( "raw.framed-preview-idle-style", framed_preview_idle_style, FRAMED_PREVIEW_PARAM__STYLE__COLORED ); +static CONFIG_INT( "raw.framed-preview-idle-resolution", framed_preview_idle_resolution, FRAMED_PREVIEW_PARAM__RESOLUTION_HALF ); +static CONFIG_INT( "raw.framed-preview-recording-style", framed_preview_recording_style, FRAMED_PREVIEW_PARAM__STYLE__GRAYSCALED ); +static CONFIG_INT( "raw.framed-preview-recording-resolution", framed_preview_recording_resolution, FRAMED_PREVIEW_PARAM__RESOLUTION_QUARTER ); +static CONFIG_INT( "raw.framed-preview-timing", framed_preview_timing, FRAMED_PREVIEW_PARAM__TIMING__LEGACY ); +static CONFIG_INT( "raw.framed-preview-statistics", framed_preview_statistics, FRAMED_PREVIEW_PARAM__STATISTICS_OFF ); + static CONFIG_INT("raw.warm.up", warm_up, 0); static CONFIG_INT("raw.use.srm.memory", use_srm_memory, 1); static CONFIG_INT("raw.small.hacks", small_hacks, 1); @@ -983,7 +991,7 @@ static void measure_compression_ratio() if (valid_slot_count == 0) { /* no valid buffers yet? */ - return; + return; } ASSERT(slots[0].ptr); @@ -1011,9 +1019,9 @@ static EXCLUDES(settings_sem) void refresh_raw_settings(int force) { - /* if (!lv) return; */ + /* if (!lv) return; */ - if (!RAW_IS_IDLE) return; + if (!RAW_IS_IDLE) return; take_semaphore(settings_sem, 0); @@ -1037,7 +1045,7 @@ void refresh_raw_settings(int force) /* update compression ratio once every 2 seconds */ if (OUTPUT_COMPRESSION && compress_mq && should_run_polling_action(2000, &aux2)) { - measure_compression_ratio(); + measure_compression_ratio(); } } } @@ -1307,6 +1315,41 @@ static MENU_UPDATE_FUNC(pre_recording_update) } } +static MENU_UPDATE_FUNC( framed_preview_engine_update ) +{ + set_framed_preview_param( FRAMED_PREVIEW_PARAM__ENGINE, framed_preview_engine ); +} + +static MENU_UPDATE_FUNC( framed_preview_idle_style_update ) +{ + set_framed_preview_param( FRAMED_PREVIEW_PARAM__IDLE_STYLE, framed_preview_idle_style ); +} + +static MENU_UPDATE_FUNC( framed_preview_idle_resolution_update ) +{ + set_framed_preview_param( FRAMED_PREVIEW_PARAM__IDLE_RESOLUTION, framed_preview_idle_resolution ); +} + +static MENU_UPDATE_FUNC( framed_preview_recording_style_update ) +{ + set_framed_preview_param( FRAMED_PREVIEW_PARAM__RECORDING_STYLE, framed_preview_recording_style ); +} + +static MENU_UPDATE_FUNC( framed_preview_recording_resolution_update ) +{ + set_framed_preview_param( FRAMED_PREVIEW_PARAM__RECORDING_RESOLUTION, framed_preview_recording_resolution ); +} + +static MENU_UPDATE_FUNC( framed_preview_timing_update ) +{ + set_framed_preview_param( FRAMED_PREVIEW_PARAM__TIMING, framed_preview_timing ); +} + +static MENU_UPDATE_FUNC( framed_preview_statistics_update ) +{ + set_framed_preview_param( FRAMED_PREVIEW_PARAM__STATISTICS, framed_preview_statistics ); +} + static MENU_UPDATE_FUNC(h264_proxy_update) { if (h264_proxy_menu) @@ -2082,11 +2125,11 @@ void FAST hack_liveview_vsync() /* Finding regs if (get_halfshutter_pressed()) { - NotifyBox(5000, "shamem_read(0xc0f383d4) 0x%x", shamem_read(0xc0f383d4)); + NotifyBox(5000, "shamem_read(0xc0f383d4) 0x%x", shamem_read(0xc0f383d4)); } else { - NotifyBox(5000, "shamem_read(0xc0f383dc) 0x%x", shamem_read(0xc0f383dc)); + NotifyBox(5000, "shamem_read(0xc0f383dc) 0x%x", shamem_read(0xc0f383dc)); } */ @@ -2139,12 +2182,12 @@ void hack_liveview(int unhack) } /* disable auto exposure and auto white balance */ - if (((cam_eos_m || cam_100d) && small_hacks == 0x1) || (!cam_eos_m && !cam_100d)) - { + if (((cam_eos_m || cam_100d) && small_hacks == 0x1) || (!cam_eos_m && !cam_100d)) + { call("aewb_enableaewb", unhack ? 1 : 0); /* for new cameras */ call("lv_ae", unhack ? 1 : 0); /* for old cameras */ call("lv_wb", unhack ? 1 : 0); - } + } /* change dialog refresh timer from 50ms to 8192ms */ uint32_t dialog_refresh_timer_addr = /* in StartDialogRefreshTimer */ @@ -3126,9 +3169,9 @@ if (cam_5d3_113 || cam_5d3_123) /* Set corrected iso when selected max iso preset in crop_rec.c */ if (lens_info.raw_iso == 0x0) { - if (isoauto == 0x1) lens_info.iso = 400; - if (isoauto == 0x2) lens_info.iso = 800; - if (isoauto == 0x3) lens_info.iso = 1600; + if (isoauto == 0x1) lens_info.iso = 400; + if (isoauto == 0x2) lens_info.iso = 800; + if (isoauto == 0x3) lens_info.iso = 1600; } } mlv_fill_idnt(&idnt_hdr, mlv_start_timestamp); @@ -3774,7 +3817,7 @@ void raw_video_rec_task(uint32_t thread) /* faster writing speed that way */ /* seems to help 100D from going black screen */ PauseLiveView(); - if (cam_100d) ResumeLiveView(); + if (cam_100d) ResumeLiveView(); /* PauseLiveView breaks UI locks - why? */ gui_uilock(UILOCK_EVERYTHING); @@ -4036,6 +4079,98 @@ static struct menu_entry raw_video_menu[] = .help = "Auto mode OFF\n" "Autoselects preview modes,framing GRAY_ULTRA_FAST.\n", }, + { + .name = "Framed preview", + .select = menu_open_submenu, + .icon_type = IT_ACTION, + .help = "Configure framed preview.", + .children = ( struct menu_entry[] ) { + { + .name = "Engine", + .priv = &framed_preview_engine, + .max = 1, + .update = framed_preview_engine_update, + .choices = CHOICES( "legacy", "ultrafast" ), + .help = "Use legacy or ultrafast (cached) framed preview." + }, + { + .name = "Comportment", + .select = menu_open_submenu, + .icon_type = IT_ACTION, + .help = "Setup ultrafast preview comportment.", + .children = ( struct menu_entry[] ) { + { + .name = "Idle", + .select = menu_open_submenu, + .icon_type = IT_ACTION, + .help = "Setup idle preview comportment.", + .children = ( struct menu_entry[] ) { + { + .name = "Style", + .priv = &framed_preview_idle_style, + .max = 1, + .update = framed_preview_idle_style_update, + .choices = CHOICES( "colored", "grayscaled" ), + .help = "Setup idle preview coloring style." + }, + { + .name = "Resolution", + .priv = &framed_preview_idle_resolution, + .max = 1, + .update = framed_preview_idle_resolution_update, + .choices = CHOICES( "half", "quarter" ), + .help = "Setup idle horizontal resolution." + }, + MENU_EOL + }, + }, + { + .name = "Recording", + .select = menu_open_submenu, + .icon_type = IT_ACTION, + .help = "Setup recording preview comportment.", + .children = ( struct menu_entry[] ) { + { + .name = "Style", + .priv = &framed_preview_recording_style, + .max = 1, + .update = framed_preview_recording_style_update, + .choices = CHOICES( "colored", "grayscaled" ), + .help = "Setup recording preview coloring style." + }, + { + .name = "Resolution", + .priv = &framed_preview_recording_resolution, + .max = 1, + .update = framed_preview_recording_resolution_update, + .choices = CHOICES( "half", "quarter" ), + .help = "Setup recording horizontal resolution." + }, + MENU_EOL + }, + }, + MENU_EOL + }, + }, + { + .name = "Timing", + .priv = &framed_preview_timing, + .max = 2, + .update = framed_preview_timing_update, + .choices = CHOICES( "legacy", "tempered", "agressive" ), + .help = "Choose display timing configuration." + }, + { + .name = "Statistics", + .priv = &framed_preview_statistics, + .max = 1, + .update = framed_preview_statistics_update, + .choices = CHOICES( "OFF", "ON" ), + .help = "Dump preview statistics in the console." + }, + MENU_EOL + }, + }, { .name = "Card Spanning", .priv = &card_spanning, @@ -4327,7 +4462,7 @@ unsigned int raw_rec_keypress_cbr(unsigned int key) static REQUIRES(GuiMainTask) unsigned int raw_rec_keypress_cbr_raw(unsigned int raw_event) -{ +{ struct event * event = (struct event *) raw_event; if (use_h264_proxy()) @@ -4405,7 +4540,7 @@ static int raw_rec_should_preview(void) } if (get_ms_clock() - last_hs_unpress > 300) { - long_halfshutter_press = 1; + long_halfshutter_press = 1; /* when using x10toggle mode in crop_rec.c will disable framing preview temporarily*/ if (shamem_read(0xc0f11a88) == 0x1) long_halfshutter_press = 0; } @@ -4481,25 +4616,51 @@ unsigned int raw_rec_update_preview(unsigned int ctx) /* when recording, preview both full-size buffers, * to make sure it's not recording every other frame */ static int fi = 0; fi = !fi; - raw_preview_fast_ex( - RAW_IS_RECORDING ? fullsize_buffers[fi] : (void*)-1, - PREVIEW_HACKED && RAW_IS_RECORDING ? (void*)-1 : buffers->dst_buf, - -1, - -1, - (need_for_speed) - ? RAW_PREVIEW_GRAY_ULTRA_FAST - : /*(shamem_read(0xC0F06804) == 0x93a011b || shamem_read(0xC0F06804) == 0x8d6011b || shamem_read(0xC0F06804) == 0x962011b || shamem_read(0xC0F06804) == 0x8f8011b) && */RAW_IS_RECORDING && prevmode ? RAW_PREVIEW_GRAY_ULTRA_FAST /* 1x3 binning mode test */ - : RAW_PREVIEW_COLOR_HALFRES - ); + // legacy engine: + if( get_framed_preview_param( FRAMED_PREVIEW_PARAM__ENGINE ) == FRAMED_PREVIEW_PARAM__ENGINE__LEGACY ) { + raw_preview_fast_ex2( + RAW_IS_RECORDING ? fullsize_buffers[ fi ] : ( void * ) -1, + PREVIEW_HACKED && RAW_IS_RECORDING ? ( void * ) -1 : buffers->dst_buf, + -1, + -1, + need_for_speed ? + RAW_PREVIEW_GRAY_ULTRA_FAST : + /*(shamem_read(0xC0F06804) == 0x93a011b || shamem_read(0xC0F06804) == 0x8d6011b || shamem_read(0xC0F06804) == 0x962011b || shamem_read(0xC0F06804) == 0x8f8011b) && */ + ( RAW_IS_RECORDING && prevmode ) ? + RAW_PREVIEW_GRAY_ULTRA_FAST : /* 1x3 binning mode test */ + RAW_PREVIEW_COLOR_HALFRES, + raw_recording_state == RAW_RECORDING + ); + } + // ultrafast engine: + else { + raw_preview_fast_ex2( + RAW_IS_RECORDING ? fullsize_buffers[ fi ] : ( void * ) -1, + PREVIEW_HACKED && RAW_IS_RECORDING ? ( void * ) -1 : buffers->dst_buf, + -1, + -1, + RAW_PREVIEW_ADAPTIVE, + raw_recording_state == RAW_RECORDING + ); + } give_semaphore(settings_sem); - /* be gentle with the CPU, save it for recording (especially if the buffer is almost full) */ - msleep( - (need_for_speed) - ? ((queued_frames > valid_slot_count / 2) ? 416 : 200) - : 20 - ); + // legacy timing method: + const int framed_preview_timing = get_framed_preview_param( FRAMED_PREVIEW_PARAM__TIMING ); + if( framed_preview_timing == FRAMED_PREVIEW_PARAM__TIMING__LEGACY ) { + // be gentle with the CPU, save it for recording (especially if the buffer is almost full): + msleep( need_for_speed ? ( ( queued_frames > valid_slot_count / 2 ) ? 416 : 200 ) : 20 ); + } + // new timing method (tempered or agressive): + else { + // when there's too much queued frame, we need to slow down to avoid record stopping + // note: no need to sleep for nothing when not recording at all (allow realtime preview) + if( need_for_speed ) { + const bool agressive = framed_preview_timing == FRAMED_PREVIEW_PARAM__TIMING__AGRESSIVE; + msleep( queued_frames > ( valid_slot_count >> 1 ) ? ( agressive ? 200 : 416 ) : ( agressive ? 100 : 200 ) ); + } + } preview_dirty = 1; return 1; @@ -4599,6 +4760,15 @@ static unsigned int raw_rec_init() if (cam_dualcard) queue_sem = create_named_semaphore("queue_sem", 1); ASSERT(((uint32_t)task_create("compress_task", 0x0F, 0x1000, compress_task, (void*)0) & 1) == 0); + + // reinject previously saved preview default values: + set_framed_preview_param( FRAMED_PREVIEW_PARAM__ENGINE, framed_preview_engine ); + set_framed_preview_param( FRAMED_PREVIEW_PARAM__IDLE_STYLE, framed_preview_idle_style ); + set_framed_preview_param( FRAMED_PREVIEW_PARAM__IDLE_RESOLUTION, framed_preview_idle_resolution ); + set_framed_preview_param( FRAMED_PREVIEW_PARAM__RECORDING_STYLE, framed_preview_recording_style ); + set_framed_preview_param( FRAMED_PREVIEW_PARAM__RECORDING_RESOLUTION, framed_preview_recording_resolution ); + set_framed_preview_param( FRAMED_PREVIEW_PARAM__TIMING, framed_preview_timing ); + set_framed_preview_param( FRAMED_PREVIEW_PARAM__STATISTICS, framed_preview_statistics ); return 0; } @@ -4637,6 +4807,13 @@ MODULE_CONFIGS_START() MODULE_CONFIG(movie_restart) MODULE_CONFIG(preview_mode) MODULE_CONFIG(prevmode) + MODULE_CONFIG( framed_preview_engine ) + MODULE_CONFIG( framed_preview_idle_style ) + MODULE_CONFIG( framed_preview_idle_resolution ) + MODULE_CONFIG( framed_preview_recording_style ) + MODULE_CONFIG( framed_preview_recording_resolution ) + MODULE_CONFIG( framed_preview_timing ) + MODULE_CONFIG( framed_preview_statistics ) MODULE_CONFIG(use_srm_memory) MODULE_CONFIG(small_hacks) MODULE_CONFIG(more_hacks) diff --git a/modules/mlv_play/mlv_play.c b/modules/mlv_play/mlv_play.c index cfc3df8a..56d8fd35 100644 --- a/modules/mlv_play/mlv_play.c +++ b/modules/mlv_play/mlv_play.c @@ -1508,7 +1508,9 @@ static void mlv_play_render_task(uint32_t priv) if(mlv_play_paused && !mlv_play_should_stop() && buffer_paused) { mlv_play_render_frame(buffer_paused); - msleep(100); + // keep this sleep only with legacy framed preview engine (seems useless): + if( get_framed_preview_param( FRAMED_PREVIEW_PARAM__TIMING ) == FRAMED_PREVIEW_PARAM__TIMING__LEGACY ) + msleep( 100 ); continue; } diff --git a/src/raw.c b/src/raw.c index 2b817010..9ea07155 100644 --- a/src/raw.c +++ b/src/raw.c @@ -2205,44 +2205,517 @@ static void FAST raw_preview_fast_work(void* raw_buffer, void* lv_buffer, int y1 free(lv2rx); } -void FAST raw_preview_fast_ex(void* raw_buffer, void* lv_buffer, int y1, int y2, int quality) + +// *** cached ultrafast preview: +// +// The purpose is to reduce as much as possible the computation time required to perform the framed RAW +// preview rendering in LiveView in order to save CPU time to do other tasks (e.g.: recording RAW data). +// +// This is achieved by precomputing everything possible, notably the RAW buffer offsets and the RGB +// gamma transformations, so the drawing itself may consist in the lightest possible loop doing linear +// accesses to data, thanks to simple pointer dereferencing. +// +// By doing this in colored and grayscale preview we instantly get a smoother preview (both previewing & +// during recording), allowing us to potentially reducing the sleep times defined to leave enough headroom +// for the CPU to record the RAW data. +// +// The cache precomputation is managed by simply computing a determinant value that may change when +// selecting a new RAW video resolution. + + +// ultrafast cache structure: +typedef struct { - if (raw_info.bits_per_pixel != 14) + // used to determine if we need to (re)init the cache: + uint64_t determinant; + + // raw offsets pointer: + uint32_t * p_raw_offsets; + // pointer to the last useful data in the buffer: + const uint32_t * p_raw_offsets_last; + + // pointer to the first useful offset in LV: + uint32_t p_first_lv_offset; + + // gamma LUT for red & blue channels (RGB) and for green channel (RGB & luminance in grayscale): + // [memory footprint] 2 slot x 1024 values x 8bits = 2KB + uint8_t gamma_rb[ 1024 ]; + uint8_t gamma_g[ 1024 ]; + + // raw width stride, used to go from even to odd lines: + uint32_t rawStride; +} +preview_ultrafast_cache; + +// unique preview ultrafast cache: +static preview_ultrafast_cache g_preview_ultrafast_cache = { + .determinant = 0, // voided before first initialization + .p_raw_offsets = 0 // must be allocated the very first time +}; + + +// preview statistics structure: +typedef struct +{ + // how many frame do we want to draw before dumping averaged statistics? + const int dump_frame_count; + + // time point (ms) set before the first framed preview draw call: + int clock_before_first_draw_ms; + + // cumulated time (ms) of preview draw calls: + int cumulated_draw_time_ms; + + // currently drawn preview frame count: + int drawn_frame_count; +} +preview_statistics; + +// unique preview statistics: +static preview_statistics g_preview_statistics = { + .dump_frame_count = 100, // dump every 100 frame + .clock_before_first_draw_ms = 0, + .cumulated_draw_time_ms = 0, + .drawn_frame_count = 0 +}; + +// reset statistics: +void reset_preview_statistics() +{ + g_preview_statistics.clock_before_first_draw_ms = 0; + g_preview_statistics.cumulated_draw_time_ms = 0; + g_preview_statistics.drawn_frame_count = 0; +} + + +// preview ultrafast parameters container: +static int g_framed_preview_param[ 7 ]; + +void set_framed_preview_param( const int _param, const int _value ) +{ + g_framed_preview_param[ _param ] = _value; +} + +int get_framed_preview_param( const int _param ) +{ + return g_framed_preview_param[ _param ]; +} + + +// ultrafast preview cache initialization: +static bool FAST init_preview_ultrafast_cache( const int _y1, const int _y2, const bool _quarter ) +{ + // notation shortcut: + preview_ultrafast_cache * puc = &g_preview_ultrafast_cache; + + // precompute gamma conversions, using a 10bits (1024 values) LUT converted to 14 bits color data. + // red & blue components are converted over 11bits while green remains on 10bits to follow a + // 2-1-2 (R-G-B) pattern and get a proper white balance. + // red, green & blue components are using a x² ramp and stored on 8bits (256 values) + float gamma_ev = 0, gamma = 0; + for( int i = 0; i < 1024; i++ ) { + + // get gamma in ev range: + gamma_ev = raw_to_ev( i << 4 ); + + // 8-bits gamma value for red & blue: + gamma = ( COERCE( gamma_ev + 11, 0, 10 ) * 255 ) / 10; + puc->gamma_rb[ i ] = ( gamma * gamma ) / 255; + + // 8-bits gamma value for green: + gamma = ( COERCE( gamma_ev + 10, 0, 10 ) * 255 ) / 10; + puc->gamma_g[ i ] = ( gamma * gamma ) / 255; + } + + // remember current RAW data stride (used to switch from even to odd rows): + puc->rawStride = raw_info.width >> 3; + + // create RAW offset buffer the very first time, using the maximum LV (half) resolution: + // [memory footprint] ( half LV width [720 / 2] ) x LV height [480] x 32bits (offset) = 675KB + if( puc->p_raw_offsets == 0 ) { + puc->p_raw_offsets = malloc( ( ( vram_lv.width * vram_lv.height ) >> 1 ) * sizeof( uint32_t ) ); + if( puc->p_raw_offsets == 0 ) { + printf( "ERROR: ultrafast cache allocation failure!\n" ); + return false; // memory allocation failure + } + } + // point to the data origin to feed the buffer: + uint32_t * p_raw_offsets = puc->p_raw_offsets; + + // compute left and right boundaries: + int x1 = COERCE( RAW2LV_X( preview_rect_x ), 0, vram_lv.width ); + int x2 = COERCE( RAW2LV_X( preview_rect_x + preview_rect_w ), 0, vram_lv.width ); + if( x2 < x1 ) { + return false; // wtf? + } + + // cache LV to RAW horizontal transformations (same for all the rows): + int * p_lv2raw_x = malloc( vram_lv.width * sizeof( int ) ); + if( p_lv2raw_x == 0 ) { + return false; // allocation failure + } + for( int x = x1; x < x2; x++ ) { + p_lv2raw_x[ x ] = LV2RAW_X( x ) & ~1; + } + + // reset first useful LV offset for subsequent set: + puc->p_first_lv_offset = 0; + + // offset shifting & pixel skiping values depends of half or quarter resolution: + const int lv_offset_shift = _quarter ? 3 : 2; + const int lv_pixel_skip = _quarter ? 4 : 2; + + // for each useful LV row: + for( int y = _y1; y < _y2; y++ ) { + + // compute RAW vertical position: + int yr = LV2RAW_Y( y ) & ~1; + // nothing to do if outside boundaries: + if( yr <= preview_rect_y || yr >= preview_rect_y + preview_rect_h ) { + continue; + } + + // remember the first useful offset in LiveView, then we will increment linearly: + if( puc->p_first_lv_offset == 0 ) + puc->p_first_lv_offset = ( y * ( vram_lv.width << 1 ) ) >> lv_offset_shift; + + // for each LV column (half or quarter LV horizontal resolution): + for( int x = 0; x < vram_lv.width; x += lv_pixel_skip ) { + + // outside horizontal boudaries, set offset to null: + if( x < x1 || x >= x2 ) { + *p_raw_offsets++ = ( uint32_t ) -1; + continue; + } + + // compute RAW buffer offset, pointing to the red component of the 1st of the 8 pixel groups: + *p_raw_offsets++ = ( p_lv2raw_x[ x ] + yr * raw_info.width ) >> 3; + } + } + + // release LV to RAW cache: + free( p_lv2raw_x ); + + // save last useful pointer to RAW offset: + puc->p_raw_offsets_last = p_raw_offsets; + + // done: + return true; +} + + +// optimize a bit the RGB to YUV422 conversion routine: +__attribute__( ( always_inline ) ) static uint32_t _rgb2yuv422_rec709( const int _r, const int _g, const int _b ) +{ + // Y computation from RGB: + int y = ( 217 * _r + 732 * _g + 73 * _b ) >> 10; + if( y > 255 ) y = 255; + + // Y computation from RGB: + int u = ( -117 * _r - 394 * _g + 512 * _b ) / 1024; + if( u < -128 ) u = -128; else if( u > 127 ) u = 127; + + // V computation from RGB: + int v = ( 512 * _r -465 * _g - 46 * _b ) / 1024; + if( v < -128 ) v = -128; else if( v > 127 ) v = 127; + + // YUV422 computation: + return ( u & 0xFF ) | ( y << 8 ) | ( ( v & 0xFF ) << 16 ) | ( y << 24 ); +} + + +// template definitions for the drawing routine itself (covers both color/grayscale, half/quarter resolution): + +#define T_PREVIEW_ULTRAFAST_DRAW_READ_COLOR \ + /* point to the RAW red value on the current even line: */ \ + p_raw_pointer = p_raw_buffer + raw_offset; \ + /* get red value: */ \ + r = *( p_gamma_rb + ( p_raw_pointer->a >> 4 ) ); \ + /* get green & blue values on the next odd line: */ \ + p_raw_pointer += rawStride; \ + /* get green value: */ \ + g = *( p_gamma_g + ( p_raw_pointer->a >> 4 ) ); \ + /* get blue value: */ \ + b = *( p_gamma_rb + ( ( ( p_raw_pointer->b_hi << 12 ) | p_raw_pointer->b_lo ) >> 4 ) ); \ + \ + /* RGB conversion to YUV422: */ \ + yuv = _rgb2yuv422_rec709( r, g, b ) + + +#define T_PREVIEW_ULTRAFAST_DRAW_READ_GRAYSCALE \ + /* compute Y component based over the green component (closest odd line) in the RAW buffer, */ \ + /* using gamma LUT: */ \ + y = *( p_gamma_g + ( ( p_raw_buffer + raw_offset )->a >> 4 ) ) + + +#define T_PREVIEW_ULTRAFAST_DRAW_WRITE_COLOR_HALF \ + /* set YUV data in the LV buffer (one YUV422 32-bits pixels): */ \ + *p_lv_buffer++ = yuv + + +#define T_PREVIEW_ULTRAFAST_DRAW_WRITE_COLOR_QUARTER \ + /* set YUV data in the LV buffer (2 duplicated YUV422 32-bits pixels): */ \ + *p_lv_buffer++ = ( yuv << 32 ) | yuv + + +#define T_PREVIEW_ULTRAFAST_DRAW_WRITE_GRAYSCALE_HALF \ + /* set YUV data in the LV buffer (1 pixel of 32-bits YUV422 grayscale data, */ \ + /* meaning two times the same Y value with no chrominance data): */ \ + *p_lv_buffer++ = ( y << 8 ) | ( y << 24 ) + + +#define T_PREVIEW_ULTRAFAST_DRAW_WRITE_GRAYSCALE_QUARTER \ + /* set YUV data in the LV buffer (2 pixels duplication of 32-bits YUV422 grayscale data, */ \ + /* meaning two times the same Y value with no chrominance data): */ \ + *p_lv_buffer++ = ( y << 8 ) | ( y << 24 ) | ( y << 40 ) | ( y << 56 ) + + +#define T_PREVIEW_ULTRAFAST_DRAW( _LV_POINTER_TYPE, _READ_FN, _WRITE_FN ) \ + /* get LV buffer pointer: */ \ + register _LV_POINTER_TYPE * p_lv_buffer = p_lv_buffer_void; \ + \ + /* set LV buffer at its first useful position: */ \ + p_lv_buffer += puc->p_first_lv_offset; \ + \ + /* fast linear access: */ \ + while( p_raw_offsets != p_raw_offsets_last ) { \ + \ + /* get RAW offset: */ \ + raw_offset = *p_raw_offsets++; \ + \ + /* offset is null, skip this pixel (left & right black bars): */ \ + if( raw_offset == ( uint32_t ) -1 ) { \ + p_lv_buffer++; \ + continue; \ + } \ + \ + /* variable read function: */ \ + _READ_FN; \ + \ + /* variable feed function */ \ + _WRITE_FN; \ + } + + +// ultra fast preview generic drawing routine: +static void FAST draw_preview_ultrafast( const void * _p_raw_buffer, const void * _p_lv_buffer, const int _y1, const int _y2, const bool _reinit, const bool _grayscale, const bool _quarter ) +{ + // notation shortcut: + const preview_ultrafast_cache * const puc = &g_preview_ultrafast_cache; + + // init cache if needed (determined externally): + if( _reinit && !init_preview_ultrafast_cache( _y1, _y2, _quarter ) ) { + return; // fail to init ultrafast cache + } + + // get LV buffer pointer: + register void * p_lv_buffer_void = CACHEABLE( _p_lv_buffer ); + if( p_lv_buffer_void == 0 ) { + return; // unavailable buffer + } + + // get cacheable RAW buffer pointer: + const struct raw_pixblock * p_raw_buffer = CACHEABLE( _p_raw_buffer ); + if( p_raw_buffer == 0 ) { + return; // unavailable buffer + } + + // [grayscale] we need to point directly to an odd line: + if( _grayscale ) { + p_raw_buffer += puc->rawStride; + } + + // [color] independent r, g, & b components: + int r = 0, g = 0, b = 0; + // [color] combined 32bits YUV value (stored on 64bits for subsequent shift): + int64_t yuv = 0; + // [grayscale] Y component (luminance) of the YUV422 LV output buffer: + int64_t y = 0; + + // pointer to precomputed RAW offsets: + const uint32_t * p_raw_offsets = puc->p_raw_offsets; + if( p_raw_offsets == 0 ) { + return; // ultrafast preview cache not initialized + } + // point to last RAW offset: + const uint32_t * const p_raw_offsets_last = puc->p_raw_offsets_last; + + // [color] pointer to RAW data, used to switch between R, G & B components on even & odd lines: + register const struct raw_pixblock * p_raw_pointer = 0; + + // [color] RAW stride used to switch from even to odd lines: + const uint32_t rawStride = puc->rawStride; + + // [color] pointer to red+blue gamma LUT: + const uint8_t * const p_gamma_rb = puc->gamma_rb; + // pointer to green gamma LUT: + const uint8_t * const p_gamma_g = puc->gamma_g; + + // RAW offet value: + uint32_t raw_offset = 0; + + // quarter resolution: + if( _quarter ) { + // [grayscale]: + if( _grayscale ) { + T_PREVIEW_ULTRAFAST_DRAW( uint64_t, T_PREVIEW_ULTRAFAST_DRAW_READ_GRAYSCALE, T_PREVIEW_ULTRAFAST_DRAW_WRITE_GRAYSCALE_QUARTER ); + return; + } + // [color]: + T_PREVIEW_ULTRAFAST_DRAW( uint64_t, T_PREVIEW_ULTRAFAST_DRAW_READ_COLOR, T_PREVIEW_ULTRAFAST_DRAW_WRITE_COLOR_QUARTER ); return; + } + + // half resolution: + // [grayscale]: + if( _grayscale ) { + T_PREVIEW_ULTRAFAST_DRAW( uint32_t, T_PREVIEW_ULTRAFAST_DRAW_READ_GRAYSCALE, T_PREVIEW_ULTRAFAST_DRAW_WRITE_GRAYSCALE_HALF ); + return; + } + // [color]: + T_PREVIEW_ULTRAFAST_DRAW( uint32_t, T_PREVIEW_ULTRAFAST_DRAW_READ_COLOR, T_PREVIEW_ULTRAFAST_DRAW_WRITE_COLOR_HALF ); +} + + +// printf doesn't seems to be able to dump float value, so here's a function that splits +// a float value to a "x.y" string, with 3 numbers after the dot: +char * format_float( const double _value, char * _buffer, const size_t _buffer_len ) +{ + const int left = ( int ) _value; + const int right = ( ( int )( _value * 1000 ) ) - ( left * 1000 ); + snprintf( _buffer, _buffer_len - 1, "%d.%d", left, right ); + return _buffer; +} + +// updated drawing routine with an additional recording parameter: +void FAST raw_preview_fast_ex2( void * _p_raw_buffer, void * _p_lv_buffer, int _y1, int _y2, int _quality, const bool _recording ) +{ + // only support 14-bits data: + if( raw_info.bits_per_pixel != 14 ) { + return; + } + + // check if the YUV buffer is initialized: yuv422_buffer_check(); - if (raw_buffer == (void*)-1) - raw_buffer = (void*)raw_info.buffer; + // set default values if needed: + _p_raw_buffer = ( _p_raw_buffer == ( void * ) -1 ) ? ( void * ) raw_info.buffer : _p_raw_buffer; + _p_lv_buffer = ( _p_lv_buffer == ( void * ) -1 ) ? ( void * ) YUV422_LV_BUFFER_DISPLAY_ADDR : _p_lv_buffer; + _y1 = ( _y1 == -1 ) ? BM2LV_Y( os.y0 ) : _y1; + _y2 = ( _y2 == -1 ) ? BM2LV_Y( os.y_max ) : _y2; + _quality = ( _quality == -1 ) ? RAW_PREVIEW_ADAPTIVE : _quality; - if (lv_buffer == (void*)-1) - lv_buffer = (void*)YUV422_LV_BUFFER_DISPLAY_ADDR; + // determine final style & resolution: + int style = -1; + int resolution = -1; + + // forced colored/half resolution: + if( _quality == RAW_PREVIEW_COLOR_HALFRES ) { + style = FRAMED_PREVIEW_PARAM__STYLE__COLORED; + resolution = FRAMED_PREVIEW_PARAM__RESOLUTION_HALF; + } + else + // forced grayscaled/quarter resolution: + if( _quality == RAW_PREVIEW_GRAY_ULTRA_FAST ) { + style = FRAMED_PREVIEW_PARAM__STYLE__GRAYSCALED; + resolution = FRAMED_PREVIEW_PARAM__RESOLUTION_QUARTER; + } + // adaptive quality, depends of idle/recording settings: + else{ + + // recording state: + if( _recording ) { + style = g_framed_preview_param[ FRAMED_PREVIEW_PARAM__RECORDING_STYLE ]; + resolution = g_framed_preview_param[ FRAMED_PREVIEW_PARAM__RECORDING_RESOLUTION ]; + } + // non-recording (idle) states: + else { + style = g_framed_preview_param[ FRAMED_PREVIEW_PARAM__IDLE_STYLE ]; + resolution = g_framed_preview_param[ FRAMED_PREVIEW_PARAM__IDLE_RESOLUTION ]; + } + + // legacy engine, the resolution depends of style (colored: half, grayscaled: quarter): + // note anyway this updated resolution parameter will not be used by legacy code + if( g_framed_preview_param[ FRAMED_PREVIEW_PARAM__ENGINE ] == FRAMED_PREVIEW_PARAM__ENGINE__LEGACY ) { + resolution = ( style == FRAMED_PREVIEW_PARAM__STYLE__COLORED ) ? FRAMED_PREVIEW_PARAM__RESOLUTION_HALF : FRAMED_PREVIEW_PARAM__RESOLUTION_QUARTER; + } + } - if (y1 == -1) - y1 = BM2LV_Y(os.y0); + // compute the current determinant value based over RAW state & framed preview configuration: + const uint64_t ultrafast_determinant = + ( preview_rect_x * preview_rect_y ) + ( preview_rect_w * preview_rect_h ) + + ( raw_info.black_level * raw_info.white_level ) + ( _y1 * _y2 ) + + ( ( style << 2 ) | ( resolution << 1 ) | g_framed_preview_param[ FRAMED_PREVIEW_PARAM__ENGINE ] ); + + // different determinant? then we need to reinit the preview cache: + const bool reinit_preview_cache = ultrafast_determinant != g_preview_ultrafast_cache.determinant; - if (y2 == -1) - y2 = BM2LV_Y(os.y_max); + // do we have to compute & dump preview statistics? + const bool dump_preview_statistics = g_framed_preview_param[ FRAMED_PREVIEW_PARAM__STATISTICS ]; - if (quality == -1) - quality = 0; + // framed preview statistics computation: + int clock_before_draw_ms = 0, clock_after_draw_ms = 0; + if( !reinit_preview_cache && dump_preview_statistics ) { + clock_before_draw_ms = get_ms_clock(); + if( g_preview_statistics.clock_before_first_draw_ms == 0 ) + g_preview_statistics.clock_before_first_draw_ms = clock_before_draw_ms; + } - switch (quality) - { - case RAW_PREVIEW_GRAY_ULTRA_FAST: - raw_preview_fast_work(raw_buffer, lv_buffer, y1, y2); - break; + // call legacy drawing routing: + if( g_framed_preview_param[ FRAMED_PREVIEW_PARAM__ENGINE ] == FRAMED_PREVIEW_PARAM__ENGINE__LEGACY ) { + // colored (half resolution): + if( style == FRAMED_PREVIEW_PARAM__STYLE__COLORED ) { + raw_preview_color_work( _p_raw_buffer, _p_lv_buffer, _y1, _y2 ); + } + // grayscaled (quarter resolution): + else { + raw_preview_fast_work( _p_raw_buffer, _p_lv_buffer, _y1, _y2 ); + } + } + // call ultrafast (unique) drawing routine: + else { + draw_preview_ultrafast( _p_raw_buffer, _p_lv_buffer, _y1, _y2, reinit_preview_cache, style == FRAMED_PREVIEW_PARAM__STYLE__GRAYSCALED, resolution == FRAMED_PREVIEW_PARAM__RESOLUTION_QUARTER ); + } + + // framed preview statistics computation & dump: + if( !reinit_preview_cache && dump_preview_statistics ) { + clock_after_draw_ms = get_ms_clock(); + g_preview_statistics.cumulated_draw_time_ms += ( clock_after_draw_ms - clock_before_draw_ms ); + if( ++g_preview_statistics.drawn_frame_count == g_preview_statistics.dump_frame_count ) { + const double drawn_frame_count = g_preview_statistics.drawn_frame_count; + const double preview_routine_duration_ms = ( ( double ) g_preview_statistics.cumulated_draw_time_ms ) / drawn_frame_count; + const double display_fps = drawn_frame_count * 1000 / ( ( double ) ( clock_after_draw_ms - g_preview_statistics.clock_before_first_draw_ms ) ); + char ms_buffer[ 32 ], fps_buffer[ 32 ]; + printf( "[Framed preview] %sms %sfps\n", format_float( preview_routine_duration_ms, ms_buffer, 32 ), format_float( display_fps, fps_buffer, 32 ) ); + reset_preview_statistics(); + } + } - case RAW_PREVIEW_COLOR_HALFRES: - default: - raw_preview_color_work(raw_buffer, lv_buffer, y1, y2); - break; + // we've just reconfigured ultrafast cache: + if( reinit_preview_cache ) { + + // save new determinant: + g_preview_ultrafast_cache.determinant = ultrafast_determinant; + + // reset statistics to avoid bad values: + reset_preview_statistics(); + + // cleanup the whole liveview, implicitly creating black bars where needed: + memset( CACHEABLE( _p_lv_buffer ), 0, ( vram_lv.width * vram_lv.height ) << 1 ); } } + +void FAST raw_preview_fast_ex( void * _p_raw_buffer, void * _p_lv_buffer, const int _y1, const int _y2, int const _quality ) +{ + raw_preview_fast_ex2( _p_raw_buffer, _p_lv_buffer, _y1, _y2, _quality, false ); +} + + void FAST raw_preview_fast() { - raw_preview_fast_ex((void*)-1, (void*)-1, -1, -1, -1); + raw_preview_fast_ex2( ( void * ) -1, ( void * ) -1, -1, -1, -1, false ); } #ifdef CONFIG_RAW_LIVEVIEW diff --git a/src/raw.h b/src/raw.h index b00d5902..cc08f9dc 100644 --- a/src/raw.h +++ b/src/raw.h @@ -113,6 +113,42 @@ int raw_blue_pixel_bright(int x, int y); #define GRAY_PROJECTION_BRIGHT_ONLY 0x100 /* you can also analyze the bright exposure (suitable for shadows, SNR... */ #define GRAY_PROJECTION_DARK_AND_BRIGHT 0x200 /* warning: might be more accurate on regular images, but has undefined behavior on dual ISO images */ +// framed preview parameters: +#define FRAMED_PREVIEW_PARAM__ENGINE 0 // engine type to use to deal with framed preview +#define FRAMED_PREVIEW_PARAM__IDLE_STYLE 1 // style to apply when idle +#define FRAMED_PREVIEW_PARAM__IDLE_RESOLUTION 2 // resolution to apply when idle +#define FRAMED_PREVIEW_PARAM__RECORDING_STYLE 3 // style to apply when recording +#define FRAMED_PREVIEW_PARAM__RECORDING_RESOLUTION 4 // resolution to apply when recording +#define FRAMED_PREVIEW_PARAM__TIMING 5 // timing policy +#define FRAMED_PREVIEW_PARAM__STATISTICS 6 // statistics dump state + +// framed preview engine values: +#define FRAMED_PREVIEW_PARAM__ENGINE__LEGACY 0 // "legacy" engine +#define FRAMED_PREVIEW_PARAM__ENGINE__ULTRAFAST 1 // ultrafast (cached) engine + +// framed preview style values: +#define FRAMED_PREVIEW_PARAM__STYLE__COLORED 0 // colored display +#define FRAMED_PREVIEW_PARAM__STYLE__GRAYSCALED 1 // grayscaled display (faster) + +// framed preview resolution values: +#define FRAMED_PREVIEW_PARAM__RESOLUTION_HALF 0 // half resolution (more accurate) +#define FRAMED_PREVIEW_PARAM__RESOLUTION_QUARTER 1 // quarter resolution (faster) + +// framed preview timing values: +#define FRAMED_PREVIEW_PARAM__TIMING__LEGACY 0 // "legacy" timing (regular sleep statements) +#define FRAMED_PREVIEW_PARAM__TIMING__TEMPERED 1 // tempered timing policy, affecting idle only +#define FRAMED_PREVIEW_PARAM__TIMING__AGRESSIVE 2 // agressive timing, affecting also recording + +// framed preview statistics values: +#define FRAMED_PREVIEW_PARAM__STATISTICS_OFF 0 // deactivated statistics console dump +#define FRAMED_PREVIEW_PARAM__STATISTICS_ON 1 // activated statistics console dump + +// change the value of a given framed preview parameter: +void set_framed_preview_param( const int _param, const int _value ); + +// get the current value of a given framed preview parameter: +int get_framed_preview_param( const int _param ); + /* input: 0 - 16384 (valid range: from black level to white level) */ /* output: -14 ... 0 */ float raw_to_ev(int raw); @@ -122,9 +158,15 @@ int ev_to_raw(float ev); void raw_preview_fast(); /* pass -1 if default value for some parameter is fine */ -void raw_preview_fast_ex(void* raw_buffer, void* lv_buffer, int start_line, int end_line, int quality); -#define RAW_PREVIEW_COLOR_HALFRES 0 /* 360x480 color, pretty slow */ -#define RAW_PREVIEW_GRAY_ULTRA_FAST 1 /* 180x240, aims to be real-time */ +void raw_preview_fast_ex( void * _p_raw_buffer, void * _p_lv_buffer, int _y1, int _y2, int _quality ); + +// possible quality values: +#define RAW_PREVIEW_COLOR_HALFRES 0 // 360x480 color, pretty slow +#define RAW_PREVIEW_GRAY_ULTRA_FAST 1 // 180x240, aims to be real-time +#define RAW_PREVIEW_ADAPTIVE 2 // choice depends on idle & recording framed preview settings + +// updated framed preview drawing routine, requiring an additional recording state: +void raw_preview_fast_ex2( void * _p_raw_buffer, void * _p_lv_buffer, const int _y1, const int _y2, const int _quality, const bool _recording ); /* request/release/check LiveView RAW flag (lv_save_raw) */ /* you have to call request/release in pairs (be careful not to request once and release twice) */