perf(lcd): cache idle eye frames and anchor blink timing
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
from pathlib import Path
|
||||
import re, subprocess, tempfile
|
||||
src=(Path(__file__).resolve().parents[1]/'components/platform/src/lcd_direct.c').read_text()
|
||||
def extract(name):
|
||||
m=re.search(r'static [^;{}]+\b'+name+r'\([^;{}]*\)\s*\{',src)
|
||||
a=m.start();b=src.index('{',a)+1;depth=1
|
||||
while depth:
|
||||
if src[b]=='{':depth+=1
|
||||
if src[b]=='}':depth-=1
|
||||
b+=1
|
||||
return src[a:b]
|
||||
prefix='''#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#define ESP_OK 0
|
||||
#define ESP_ERR_INVALID_STATE 1
|
||||
#define LCD_DIRECT_EMOTE_STEP_MS 34U
|
||||
#define LCD_DIRECT_PRESENTATION_WAIT_POLL_MS 20U
|
||||
#define pdMS_TO_TICKS(x) ((x)/10)
|
||||
#define ESP_RETURN_ON_ERROR(expr,...) do {int e=(expr);if(e)return e;}while(0)
|
||||
typedef int esp_err_t;
|
||||
typedef uint32_t TickType_t;
|
||||
static int s_emote_handle, delays, stop_after, locks, unlocks;
|
||||
static bool stop;
|
||||
static int64_t now;
|
||||
static int64_t esp_timer_get_time(void){return now;}
|
||||
static bool lcd_direct_should_stop_idle(void){return stop;}
|
||||
static int gfx_emote_lock(int h){++locks;return 0;}
|
||||
static int gfx_emote_unlock(int h){++unlocks;return 0;}
|
||||
static int lcd_direct_emote_update_hud(bool force,bool *changed){*changed=false;return 0;}
|
||||
static int lcd_direct_emote_refresh(void){return 0;}
|
||||
static void vTaskDelay(TickType_t ticks){assert(ticks>0);now+=ticks*10000;++delays;if(stop_after && delays>=stop_after)stop=true;}
|
||||
'''
|
||||
test='''int main(void){
|
||||
for(size_t i=0;i<12;++i){
|
||||
assert(lcd_direct_emote_step_deadline(123000,i)==123000+(int64_t)(i+1)*34000);
|
||||
now+=13000;
|
||||
assert(lcd_direct_emote_wait_until(lcd_direct_emote_step_deadline(0,i))==0);
|
||||
assert(now>=(int64_t)(i+1)*34000 && now<(int64_t)(i+1)*34000+10000);
|
||||
}
|
||||
int64_t anchored=now;
|
||||
now=0;
|
||||
for(size_t i=0;i<12;++i){now+=13000;assert(lcd_direct_emote_wait(21)==0);}
|
||||
assert(anchored<418000 && now>=480000);
|
||||
now=200000;int before=delays;
|
||||
assert(lcd_direct_emote_wait_until(100000)==0 && now==200000 && delays==before);
|
||||
stop=true;assert(lcd_direct_emote_wait_until(100000)==1);
|
||||
stop=false;stop_after=delays+1;assert(lcd_direct_emote_wait_until(now+100000)==1);
|
||||
assert(locks==unlocks);
|
||||
printf("PASS: fixed deadlines, 10ms tick drift reduction (anchored=%lldus), late/stop handling and paired HUD locks\\n",(long long)anchored);
|
||||
}'''
|
||||
with tempfile.TemporaryDirectory(prefix='emote-timeline-') as tmp:
|
||||
c=Path(tmp)/'test.c';exe=Path(tmp)/'test'
|
||||
c.write_text(prefix+'\n'.join(extract(n) for n in ['lcd_direct_emote_wait_until','lcd_direct_emote_wait','lcd_direct_emote_step_deadline'])+'\n'+test)
|
||||
subprocess.run(['clang','-std=c11','-fsanitize=address,undefined',str(c),'-o',str(exe)],check=True)
|
||||
subprocess.run([str(exe)],check=True)
|
||||
@@ -9,6 +9,7 @@ def extract(name):
|
||||
if src[b]=='}': depth-=1
|
||||
b+=1
|
||||
return src[a:b]
|
||||
cache_table=re.search(r'static const uint8_t s_emote_cache_closures.*?\n};',src,re.S).group(0)
|
||||
prefix='''#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
@@ -16,6 +17,12 @@ prefix='''#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#define LCD_DIRECT_WIDTH 320
|
||||
#define LCD_DIRECT_EMOTE_CACHE_COUNT 9
|
||||
#define LCD_DIRECT_EMOTE_CLOSED_THRESHOLD 232
|
||||
#define LCD_DIRECT_EMOTE_CACHE_X 39
|
||||
#define LCD_DIRECT_EMOTE_CACHE_Y 63
|
||||
#define LCD_DIRECT_EMOTE_CACHE_WIDTH 242
|
||||
#define LCD_DIRECT_EMOTE_CACHE_HEIGHT 97
|
||||
#define LCD_DIRECT_EMOTE_DMA_ROWS 16
|
||||
#define LCD_DIRECT_TRANSFER_TIMEOUT_MS 100
|
||||
#define portENTER_CRITICAL(x) ((void)0)
|
||||
@@ -29,7 +36,12 @@ prefix='''#include <stdint.h>
|
||||
typedef int esp_err_t;
|
||||
typedef int gfx_disp_t;
|
||||
static bool s_emote_active, s_emote_transfer_fault;
|
||||
static uint32_t s_emote_completed_regions;
|
||||
static uint64_t s_emote_copy_us, s_emote_spi_us;
|
||||
static int64_t fake_time;
|
||||
static int64_t esp_timer_get_time(void) {return ++fake_time;}
|
||||
static int s_emote_flush_error, s_panel, s_transfer_done_sem;
|
||||
static uint16_t *s_emote_capture_pixels;
|
||||
static uint16_t dma[5120];
|
||||
static uint16_t *s_emote_dma_pixels=dma;
|
||||
static uint16_t capture[320*240];
|
||||
@@ -44,12 +56,36 @@ static int esp_lcd_panel_draw_bitmap(int panel,int x1,int y1,int x2,int y2,const
|
||||
}
|
||||
'''
|
||||
test='''int main(void) {
|
||||
for(unsigned i=0;i<9;++i) assert(lcd_direct_emote_cache_index(s_emote_cache_closures[i])==(int)i);
|
||||
assert(lcd_direct_emote_cache_index(255)==8);
|
||||
assert(lcd_direct_emote_cache_index(1)==-1);
|
||||
assert(lcd_direct_emote_fps_x100(12,570000)==2105);
|
||||
assert(lcd_direct_emote_fps_x100(9,570000)==1578);
|
||||
assert(lcd_direct_emote_fps_x100(12,0)==0);
|
||||
assert(lcd_direct_emote_fps_x100(0,570000)==0);
|
||||
uint16_t source[320*240];
|
||||
for(int i=0;i<320*240;++i) source[i]=(uint16_t)i;
|
||||
uint16_t cache[242*97+2]; cache[0]=cache[242*97+1]=0xabcd;
|
||||
s_emote_capture_pixels=cache+1; calls=ready=0;
|
||||
lcd_direct_emote_flush(NULL,0,0,320,240,source);
|
||||
assert(calls==0 && ready==1);
|
||||
for(int y=0;y<97;++y) for(int x=0;x<242;++x)
|
||||
assert(cache[1+y*242+x]==source[(y+63)*320+x+39]);
|
||||
assert(cache[0]==0xabcd && cache[242*97+1]==0xabcd);
|
||||
memset(cache+1,0,242*97*2);
|
||||
for(int y=0;y<240;y+=8)
|
||||
lcd_direct_emote_capture(cache+1,0,y,320,y+8,source+y*320);
|
||||
for(int y=0;y<97;++y) for(int x=0;x<242;++x)
|
||||
assert(cache[1+y*242+x]==source[(y+63)*320+x+39]);
|
||||
s_emote_capture_pixels=NULL;
|
||||
for(width=1;width<=320;++width) {
|
||||
calls=ready=fail_call=timeout_call=0;origin_y=65;s_emote_active=true;
|
||||
s_emote_transfer_fault=false;s_emote_flush_error=0;
|
||||
const uint32_t before=s_emote_completed_regions;
|
||||
lcd_direct_emote_flush(NULL,0,65,width,158,source);
|
||||
assert(s_emote_completed_regions==before+CONFIG_TQ_LCD_EMOTE_FPS_LOG);
|
||||
assert((s_emote_copy_us>0)==CONFIG_TQ_LCD_EMOTE_FPS_LOG);
|
||||
assert((s_emote_spi_us>0)==CONFIG_TQ_LCD_EMOTE_FPS_LOG);
|
||||
assert(ready==1 && s_emote_flush_error==0);
|
||||
assert(memcmp(source,capture,width*93*2)==0);
|
||||
assert(calls==(93+5120/width-1)/(5120/width));
|
||||
@@ -75,6 +111,10 @@ test='''int main(void) {
|
||||
}'''
|
||||
with tempfile.TemporaryDirectory(prefix='emote-transport-') as tmp:
|
||||
c=Path(tmp)/'test.c';exe=Path(tmp)/'test'
|
||||
c.write_text(prefix+extract('lcd_direct_emote_flush')+'\n'+extract('lcd_direct_emote_draw_closed_eyes')+'\n'+test)
|
||||
subprocess.run(['clang','-std=c11','-fsanitize=address,undefined',str(c),'-o',str(exe)],check=True)
|
||||
subprocess.run([str(exe)],check=True)
|
||||
for enabled in (0,1):
|
||||
c.write_text('#define CONFIG_TQ_LCD_EMOTE_FPS_LOG '+str(enabled)+'\n'+prefix+cache_table+'\n'+extract('lcd_direct_emote_cache_index')+'\n'+
|
||||
extract('lcd_direct_emote_capture')+'\n'+extract('lcd_direct_emote_flush')+'\n'+extract('lcd_direct_emote_draw_closed_eyes')+'\n'+
|
||||
extract('lcd_direct_emote_fps_x100')+'\n'+test)
|
||||
subprocess.run(['clang','-std=c11','-fsanitize=address,undefined',str(c),'-o',str(exe)],check=True)
|
||||
subprocess.run([str(exe)],check=True)
|
||||
print('PASS: cache mapping/capture without LCD transfer, FPS logging on/off and measured-rate arithmetic')
|
||||
|
||||
Reference in New Issue
Block a user