Skip to content

Commit

Permalink
test LCD 1k, bpp LUT functions
Browse files Browse the repository at this point in the history
  • Loading branch information
vroland committed Jun 16, 2024
1 parent 78a6d8b commit 181c2b5
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 100 deletions.
4 changes: 2 additions & 2 deletions examples/test/main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ static void print_banner(const char* text) {
void app_main(void) {
print_banner("Running all the registered tests");
UNITY_BEGIN();
// unity_run_tests_by_tag("unit", false);
unity_run_all_tests();
unity_run_tests_by_tag("lut", false);
// unity_run_all_tests();
UNITY_END();
}
127 changes: 70 additions & 57 deletions src/output_common/lut.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,36 +105,6 @@ __attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_1bpp(
}
}

__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_lut_64k(
const uint32_t* line_data, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
) {
uint32_t* wide_epd_input = (uint32_t*)epd_input;
const uint16_t* line_data_16 = (const uint16_t*)line_data;

// this is reversed for little-endian, but this is later compensated
// through the output peripheral.
for (uint32_t j = 0; j < epd_width / 16; j++) {
uint16_t v1 = *(line_data_16++);
uint16_t v2 = *(line_data_16++);
uint16_t v3 = *(line_data_16++);
uint16_t v4 = *(line_data_16++);

#ifdef RENDER_METHOD_LCD
uint32_t pixel = conversion_lut[v1] | conversion_lut[v2] << 8 | conversion_lut[v3] << 16
| conversion_lut[v4] << 24;
#elif RENDER_METHOD_I2S
uint32_t pixel = conversion_lut[v4];
pixel = pixel << 8;
pixel |= conversion_lut[v3];
pixel = pixel << 8;
pixel |= conversion_lut[v2];
pixel = pixel << 8;
pixel |= conversion_lut[v1];
#endif
wide_epd_input[j] = pixel;
}
}

/**
* Look up 4 pixels of a differential image in a LUT constructed for use with vector extensions.
*/
Expand Down Expand Up @@ -226,11 +196,43 @@ __attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_1ppB_64k(
#endif
}

__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_lut_64k(
const uint32_t* line_data, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
) {
const uint16_t* line_data_16 = (const uint16_t*)line_data;

#ifdef RENDER_METHOD_LCD
for (uint32_t j = 0; j < epd_width / 4; j++) {
epd_input[j] = conversion_lut[*(line_data_16++)];
}
#elif RENDER_METHOD_I2S
// TODO!
uint32_t* wide_epd_input = (uint32_t*)epd_input;

// this is reversed for little-endian, but this is later compensated
// through the output peripheral.
for (uint32_t j = 0; j < epd_width / 16; j++) {
uint16_t v1 = *(line_data_16++);
uint16_t v2 = *(line_data_16++);
uint16_t v3 = *(line_data_16++);
uint16_t v4 = *(line_data_16++);
uint32_t pixel = conversion_lut[v4];
pixel = pixel << 8;
pixel |= conversion_lut[v3];
pixel = pixel << 8;
pixel |= conversion_lut[v2];
pixel = pixel << 8;
pixel |= conversion_lut[v1];
wide_epd_input[j] = pixel;
}
#endif
}

/**
* Look up 4 pixels in a 1K LUT with fixed "from" value.
*/
__attribute__((optimize("O3"))) uint8_t lookup_pixels_4bpp_1k(
uint16_t in, const uint8_t* conversion_lut, uint8_t from, uint32_t epd_width
__attribute__((optimize("O3"))) static uint8_t lookup_pixels_4bpp_1k(
uint16_t in, const uint8_t* conversion_lut, uint8_t from
) {
uint8_t v;
uint8_t out;
Expand Down Expand Up @@ -258,22 +260,36 @@ __attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_1k_lut(
uint8_t from,
uint32_t epd_width
) {
uint16_t* ptr = (uint16_t*)ld;
// this is reversed for little-endian, but this is later compensated
// through the output peripheral.
for (uint32_t j = 0; j < epd_width / 4; j += 4) {
const uint16_t* line_data_16 = (const uint16_t*)ld;

#ifdef RENDER_METHOD_LCD
epd_input[j + 0] = lookup_pixels_4bpp_1k(*(ptr++), conversion_lut, from, epd_width);
epd_input[j + 1] = lookup_pixels_4bpp_1k(*(ptr++), conversion_lut, from, epd_width);
epd_input[j + 2] = lookup_pixels_4bpp_1k(*(ptr++), conversion_lut, from, epd_width);
epd_input[j + 3] = lookup_pixels_4bpp_1k(*(ptr++), conversion_lut, from, epd_width);
for (uint32_t j = 0; j < epd_width / 4; j++) {
epd_input[j] = lookup_pixels_4bpp_1k(*(line_data_16++), conversion_lut, from);
};
#elif RENDER_METHOD_I2S
epd_input[j + 2] = lookup_pixels_4bpp_1k(*(ptr++), conversion_lut, from, epd_width);
epd_input[j + 3] = lookup_pixels_4bpp_1k(*(ptr++), conversion_lut, from, epd_width);
epd_input[j + 0] = lookup_pixels_4bpp_1k(*(ptr++), conversion_lut, from, epd_width);
epd_input[j + 1] = lookup_pixels_4bpp_1k(*(ptr++), conversion_lut, from, epd_width);
#endif
uint32_t* wide_epd_input = (uint32_t*)epd_input;
const uint16_t* line_data_16 = (const uint16_t*)ld
// this is reversed for little-endian, but this is later compensated
// through the output peripheral.
for (uint32_t j = 0; j < epd_width / 16; j++) {
uint16_t v1 = *(line_data_16++);
uint16_t v2 = *(line_data_16++);
uint16_t v3 = *(line_data_16++);
uint16_t v4 = *(line_data_16++);
uint8_t o1 = lookup_pixels_4bpp_1k(v1, conversion_lut, from);
uint8_t o2 = lookup_pixels_4bpp_1k(v2, conversion_lut, from);
uint8_t o3 = lookup_pixels_4bpp_1k(v3, conversion_lut, from);
uint8_t o4 = lookup_pixels_4bpp_1k(v4, conversion_lut, from);
uint32_t pixel = o4;
pixel = pixel << 8;
pixel |= o3;
pixel = pixel << 8;
pixel |= o2;
pixel = pixel << 8;
pixel |= o1;
wide_epd_input[j] = pixel;
}
#endif
}

__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_1k_lut_white(
Expand Down Expand Up @@ -376,8 +392,9 @@ build_1ppB_lut_S3_VE_1k(uint8_t* lut, const EpdWaveformPhases* phases, int frame
* known, e.g. all white or all black.
* This LUT is use to look up 4 pixels at once, as with the epdiy LUT.
*/
__attribute__((optimize("O3"))) static void
build_2ppB_lut_64k_static_from(uint8_t* lut, const EpdWaveformPhases* phases, uint8_t from, int frame) {
__attribute__((optimize("O3"))) static void build_2ppB_lut_64k_static_from(
uint8_t* lut, const EpdWaveformPhases* phases, uint8_t from, int frame
) {
const uint8_t* p_lut = phases->luts + (16 * 4 * frame);

/// index into the packed "from" row
Expand Down Expand Up @@ -428,31 +445,29 @@ static void build_8ppB_lut_256b_from_15(uint8_t* lut, const EpdWaveformPhases* p
memcpy(lut, lut_1bpp_black, sizeof(lut_1bpp_black));
}



LutFunctionPair find_lut_functions(enum EpdDrawMode mode, uint32_t lut_size) {
LutFunctionPair pair;
pair.build_func = NULL;
pair.lookup_func = NULL;


if (mode & MODE_PACKING_1PPB_DIFFERENCE) {
if (EPD_CURRENT_RENDER_METHOD == RENDER_METHOD_LCD && !(mode & MODE_FORCE_NO_PIE) && lut_size >= 1024) {
if (mode & MODE_PACKING_1PPB_DIFFERENCE) {
if (EPD_CURRENT_RENDER_METHOD == RENDER_METHOD_LCD && !(mode & MODE_FORCE_NO_PIE)
&& lut_size >= 1024) {
pair.build_func = &build_1ppB_lut_S3_VE_1k;
pair.lookup_func = &calc_epd_input_1ppB_1k_S3_VE;
return pair;
} else if (lut_size >= 1 << 16) {
pair.build_func = &build_1ppB_lut_64k;
pair.lookup_func = &calc_epd_input_1bpp;
return pair;
}
}
} else if (mode & MODE_PACKING_2PPB) {
if (lut_size >= 1 << 16) {
if (mode & PREVIOUSLY_WHITE) {
pair.build_func = &build_2ppB_lut_64k_from_15;
pair.lookup_func = &calc_epd_input_4bpp_lut_64k;
return pair;
} else if (mode & PREVIOUSLY_BLACK) {
} else if (mode & PREVIOUSLY_BLACK) {
pair.build_func = &build_2ppB_lut_64k_from_0;
pair.lookup_func = &calc_epd_input_4bpp_lut_64k;
return pair;
Expand All @@ -472,7 +487,7 @@ LutFunctionPair find_lut_functions(enum EpdDrawMode mode, uint32_t lut_size) {
if (lut_size < sizeof(lut_1bpp_black)) {
return pair;
}

if (mode & PREVIOUSLY_WHITE) {
pair.build_func = &build_8ppB_lut_256b_from_15;
pair.lookup_func = &calc_epd_input_1bpp;
Expand All @@ -484,5 +499,3 @@ LutFunctionPair find_lut_functions(enum EpdDrawMode mode, uint32_t lut_size) {

return pair;
}


12 changes: 5 additions & 7 deletions src/output_common/lut.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,34 @@
// Make a block of 4 pixels darker on the EPD.
#define DARK_BYTE 0B01010101


/**
* Type signature of a framebuffer to display output lookup function.
* Type signature of a framebuffer to display output lookup function.
*/
typedef void (*lut_func_t)(const uint32_t* line_buffer, uint8_t* epd_input, const uint8_t* lut, uint32_t epd_width);
typedef void (*lut_func_t)(
const uint32_t* line_buffer, uint8_t* epd_input, const uint8_t* lut, uint32_t epd_width
);

/**
* Type signature of a LUT preparation function.
*/
typedef void (*lut_build_func_t)(uint8_t* lut, const EpdWaveformPhases* phases, int frame);


typedef struct {
lut_build_func_t build_func;
lut_func_t lookup_func;
} LutFunctionPair;

/**
* Select the appropriate LUT building and lookup function
* Select the appropriate LUT building and lookup function
* for the selected draw mode and allocated LUT size.
*/
LutFunctionPair find_lut_functions(enum EpdDrawMode mode, uint32_t lut_size);


/*
* Reorder the output buffer to account for I2S FIFO order.
*/
void reorder_line_buffer(uint32_t* line_data, int buf_len);


// legacy functions
void bit_shift_buffer_right(uint8_t* buf, uint32_t len, int shift);
void nibble_shift_buffer_right(uint8_t* buf, uint32_t len);
3 changes: 1 addition & 2 deletions src/output_common/render_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ typedef struct {
size_t conversion_lut_size;
// Lookup table space.
uint8_t* conversion_lut;

/// LUT lookup function. Must not be NULL.
lut_func_t lut_lookup_func;
/// LUT building function. Must not be NULL
Expand All @@ -72,7 +72,6 @@ typedef struct {
int skipping;
} RenderContext_t;


/**
* Based on the render context, assign the bytes per line,
* framebuffer start pointer, min and max vertical positions and the pixels per byte.
Expand Down
2 changes: 1 addition & 1 deletion src/output_common/render_method.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "sdkconfig.h"
#include "render_method.h"
#include "sdkconfig.h"

#ifdef CONFIG_IDF_TARGET_ESP32
const enum EpdRenderMethod EPD_CURRENT_RENDER_METHOD = RENDER_METHOD_I2S;
Expand Down
1 change: 0 additions & 1 deletion src/output_common/render_method.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ extern const enum EpdRenderMethod EPD_CURRENT_RENDER_METHOD;
#error "unknown chip, cannot choose render method!"
#endif


#ifdef __clang__
#define IRAM_ATTR
// define this if we're using clangd to make it accept the GCC builtin
Expand Down
2 changes: 1 addition & 1 deletion src/output_lcd/render_lcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ lcd_calculate_frame(RenderContext_t* ctx, int thread_id) {

LineQueue_t* lq = &ctx->line_queues[thread_id];
int l = 0;

// if there is an error, start the frame but don't feed data.
if (ctx->error) {
memset(ctx->line_threads, 0, ctx->lines_total);
Expand Down
2 changes: 1 addition & 1 deletion src/render.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ enum EpdDrawError IRAM_ATTR epd_draw_base(
);
}
#endif

LutFunctionPair lut_functions = find_lut_functions(mode, render_context.conversion_lut_size);
if (lut_functions.build_func == NULL || lut_functions.lookup_func == NULL) {
ESP_LOGE("epdiy", "no output lookup method found for your mode and LUT size!");
Expand Down
Loading

0 comments on commit 181c2b5

Please sign in to comment.