Skip to content

Commit

Permalink
clean up lut.c, fix 2bpp order on LCD
Browse files Browse the repository at this point in the history
  • Loading branch information
vroland committed Jun 9, 2024
1 parent b53bd38 commit 78a6d8b
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 255 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(app_sources "src/epdiy.c"
"src/output_common/lut.S"
"src/output_common/line_queue.c"
"src/output_common/render_context.c"
"src/output_common/render_method.c"
"src/font.c"
"src/displays.c"
"src/diff.S"
Expand Down
4 changes: 2 additions & 2 deletions src/diff.S
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <xtensa/config/core-isa.h>
#include <xtensa/config/core-matmap.h>
#include "output_common/render_method.h"
#include "sdkconfig.h"

#ifdef RENDER_METHOD_LCD
#ifdef CONFIG_IDF_TARGET_ESP32S3

.text
.align 4
Expand Down
4 changes: 2 additions & 2 deletions src/output_common/lut.S
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <xtensa/config/core-isa.h>
#include <xtensa/config/core-matmap.h>
#include "render_method.h"
#include "sdkconfig.h"

#ifdef RENDER_METHOD_LCD
#ifdef CONFIG_IDF_TARGET_ESP32S3

.text
.align 4
Expand Down
163 changes: 72 additions & 91 deletions src/output_common/lut.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "render_method.h"

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "esp_system.h" // for ESP_IDF_VERSION_VAL
Expand Down Expand Up @@ -89,9 +90,6 @@ nibble_shift_buffer_right(uint8_t* buf, uint32_t len) {
}
}

///////////////////////////// Looking up EPD Pixels
//////////////////////////////////

__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_1bpp(
const uint32_t* line_data, uint8_t* epd_input, const uint8_t* lut, uint32_t epd_width
) {
Expand Down Expand Up @@ -122,8 +120,8 @@ __attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_lut_64k(
uint16_t v4 = *(line_data_16++);

#ifdef RENDER_METHOD_LCD
uint32_t pixel = conversion_lut[v1] << 16 | conversion_lut[v2] << 24 | conversion_lut[v3]
| conversion_lut[v4] << 8;
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;
Expand Down Expand Up @@ -159,6 +157,15 @@ void IRAM_ATTR calc_epd_input_1ppB_1k_S3_VE_aligned(
const uint32_t* ld, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
);

#ifdef RENDER_METHOD_I2S
void calc_epd_input_1ppB_1k_S3_VE_aligned(
const uint32_t* ld, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
) {
// dummy implementation, should never be called.
abort();
}
#endif

/**
* Lookup accelerated by the S3 Vector Extensions.
* Uses a 1K padded LUT (each entry takes up 32 bits)
Expand Down Expand Up @@ -288,7 +295,7 @@ __attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_1k_lut_black(
* Unpack the waveform data into a lookup table, with bit shifted copies.
*/
__attribute__((optimize("O3"))) static void IRAM_ATTR
waveform_lut(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
build_2ppB_lut_1k(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
const uint8_t* p_lut = phases->luts + (16 * 4 * frame);
for (uint8_t to = 0; to < 16; to++) {
for (uint8_t from_packed = 0; from_packed < 4; from_packed++) {
Expand Down Expand Up @@ -317,7 +324,7 @@ waveform_lut(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
* 64k to loop up two bytes at once
*/
__attribute__((optimize("O3"))) static void IRAM_ATTR
waveform_lut_64k(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
build_1ppB_lut_64k(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
const uint8_t* p_lut = phases->luts + (16 * 4 * frame);
for (uint8_t to = 0; to < 16; to++) {
for (uint8_t from_packed = 0; from_packed < 4; from_packed++) {
Expand Down Expand Up @@ -349,7 +356,7 @@ waveform_lut_64k(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
* A 32bit aligned lookup table for lookup using the ESP32-S3 vector extensions.
*/
__attribute__((optimize("O3"))) static void IRAM_ATTR
waveform_lut_S3_VE(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
build_1ppB_lut_S3_VE_1k(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
uint32_t* lut32 = (uint32_t*)lut;
const uint8_t* p_lut = phases->luts + (16 * 4 * frame);
for (uint8_t to = 0; to < 16; to++) {
Expand All @@ -369,8 +376,8 @@ waveform_lut_S3_VE(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 IRAM_ATTR
waveform_lut_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 @@ -409,99 +416,73 @@ waveform_lut_static_from(uint8_t* lut, const EpdWaveformPhases* phases, uint8_t
}
}

/**
* Set all pixels not in [xmin,xmax) to nop in the current line buffer.
*/
__attribute__((optimize("O3"))) void mask_line_buffer(
uint8_t* lb, int line_buf_len, int xmin, int xmax
) {
#ifdef RENDER_METHOD_I2S
const int offset_table[4] = { 2, 3, 0, 1 };
#else
const int offset_table[4] = { 0, 1, 2, 3 };
#endif

// lower bound to where byte order is not an issue.
int memset_start = (xmin / 16) * 4;
int memset_end = min(((xmax + 15) / 16) * 4, line_buf_len);

// memset the areas where order is not an issue
memset(lb, 0, memset_start);
memset(lb + memset_end, 0, line_buf_len - memset_end);

// mask unused pixels at the start of the output interval
uint8_t line_start_mask = 0xFF << (2 * (xmin % 4));
uint8_t line_end_mask = 0xFF >> (8 - 2 * (xmax % 4));

// number of full bytes to mask
int lower_full_bytes = max(0, (xmin / 4 - memset_start));
int upper_full_bytes = max(0, (memset_end - ((xmax + 3) / 4)));
assert(lower_full_bytes <= 3);
assert(upper_full_bytes <= 3);
assert(memset_end >= 4);
static void build_2ppB_lut_64k_from_0(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
build_2ppB_lut_64k_static_from(lut, phases, 0, frame);
}

// mask full bytes
for (int i = 0; i < lower_full_bytes; i++) {
lb[memset_start + offset_table[i]] = 0x0;
}
for (int i = 0; i < upper_full_bytes; i++) {
lb[memset_end - 4 + offset_table[3 - i]] = 0x0;
}
static void build_2ppB_lut_64k_from_15(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
build_2ppB_lut_64k_static_from(lut, phases, 0xF, frame);
}

// mask partial bytes
if ((memset_start + lower_full_bytes) * 4 < xmin) {
lb[memset_start + offset_table[lower_full_bytes]] &= line_start_mask;
}
if ((memset_end - upper_full_bytes) * 4 > xmax) {
lb[memset_end - 4 + offset_table[3 - upper_full_bytes]] &= line_end_mask;
}
static void build_8ppB_lut_256b_from_15(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
memcpy(lut, lut_1bpp_black, sizeof(lut_1bpp_black));
}

__attribute__((optimize("O3"))) enum EpdDrawError IRAM_ATTR calculate_lut(
uint8_t* lut, int lut_size, enum EpdDrawMode mode, int frame, const EpdWaveformPhases* phases
) {
enum EpdDrawMode selected_mode = mode & 0x3F;

#ifdef RENDER_METHOD_LCD
if ((mode & MODE_PACKING_1PPB_DIFFERENCE) && !(mode & MODE_FORCE_NO_PIE)) {
waveform_lut_S3_VE(lut, phases, frame);
return EPD_DRAW_SUCCESS;
}
#endif

// two pixel per byte packing with only target color
if (lut_size == (1 << 16)) {
if (mode & MODE_PACKING_2PPB) {
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) {
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) {
pair.build_func = &build_2ppB_lut_64k_from_0;
pair.lookup_func = &calc_epd_input_4bpp_lut_64k;
return pair;
}
} else if (lut_size >= 1024) {
if (mode & PREVIOUSLY_WHITE) {
waveform_lut_static_from(lut, phases, 0x0F, frame);
pair.build_func = &build_2ppB_lut_1k;
pair.lookup_func = &calc_epd_input_4bpp_1k_lut_white;
return pair;
} else if (mode & PREVIOUSLY_BLACK) {
waveform_lut_static_from(lut, phases, 0x00, frame);
} else {
waveform_lut(lut, phases, frame);
pair.build_func = &build_2ppB_lut_1k;
pair.lookup_func = &calc_epd_input_4bpp_1k_lut_black;
return pair;
}
// one pixel per byte with from and to colors
} else if (mode & MODE_PACKING_1PPB_DIFFERENCE) {
waveform_lut_64k(lut, phases, frame);
} else {
return EPD_DRAW_LOOKUP_NOT_IMPLEMENTED;
}

// 1bit per pixel monochrome with only target color
} else if (mode & MODE_PACKING_8PPB && selected_mode == MODE_EPDIY_MONOCHROME) {
// FIXME: Pack into waveform?
} else if (mode & MODE_PACKING_8PPB) {
if (lut_size < sizeof(lut_1bpp_black)) {
return pair;
}

if (mode & PREVIOUSLY_WHITE) {
memcpy(lut, lut_1bpp_black, sizeof(lut_1bpp_black));
pair.build_func = &build_8ppB_lut_256b_from_15;
pair.lookup_func = &calc_epd_input_1bpp;
return pair;
} else if (mode & PREVIOUSLY_BLACK) {
// FIXME: implement!
// memcpy(render_context.conversion_lut, lut_1bpp_white, sizeof(lut_1bpp_white));
return EPD_DRAW_LOOKUP_NOT_IMPLEMENTED;
} else {
return EPD_DRAW_LOOKUP_NOT_IMPLEMENTED;
// FIXME: to implement
}

// unknown format.
} else {
return EPD_DRAW_LOOKUP_NOT_IMPLEMENTED;
}
return EPD_DRAW_SUCCESS;

return pair;
}


101 changes: 23 additions & 78 deletions src/output_common/lut.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,97 +2,42 @@

#include <stdint.h>
#include "epdiy.h"
#include "esp_attr.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"

// Make a block of 4 pixels lighter on the EPD.
#define CLEAR_BYTE 0B10101010
// Make a block of 4 pixels darker on the EPD.
#define DARK_BYTE 0B01010101

///////////////////////////// Utils /////////////////////////////////////

/*
* Reorder the output buffer to account for I2S FIFO order.
/**
* Type signature of a framebuffer to display output lookup function.
*/
void reorder_line_buffer(uint32_t* line_data, int buf_len);

typedef struct {
int thread_id;
const uint8_t* data_ptr;
EpdRect crop_to;
void (*done_cb)(void);
SemaphoreHandle_t start_smphr;
EpdRect area;
int frame;
/// index of the waveform mode when using vendor waveforms.
/// This is not necessarily the mode number if the waveform header
// only contains a selection of modes!
int waveform_index;
/// waveform range when using vendor waveforms
int waveform_range;
/// Draw time for the current frame in 1/10ths of us.
int frame_time;
const EpdWaveform* waveform;
enum EpdDrawMode mode;
enum EpdDrawError error;
const bool* drawn_lines;
// Queue of input data lines
QueueHandle_t* pixel_queue;
// Queue of display data lines
QueueHandle_t* display_queue;
typedef void (*lut_func_t)(const uint32_t* line_buffer, uint8_t* epd_input, const uint8_t* lut, uint32_t epd_width);

// Lookup table size.
size_t conversion_lut_size;
// Lookup table space.
uint8_t* conversion_lut;
} OutputParams;
/**
* Type signature of a LUT preparation function.
*/
typedef void (*lut_build_func_t)(uint8_t* lut, const EpdWaveformPhases* phases, int frame);

// void feed_display(OutputParams *params);
// void provide_out(OutputParams *params);

void bit_shift_buffer_right(uint8_t* buf, uint32_t len, int shift);
void mask_line_buffer(uint8_t* lb, int line_buf_len, int xmin, int xmax);
void nibble_shift_buffer_right(uint8_t* buf, uint32_t len);
typedef struct {
lut_build_func_t build_func;
lut_func_t lookup_func;
} LutFunctionPair;

void calc_epd_input_1ppB(
const uint32_t* ld, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
);
void calc_epd_input_1ppB_64k(
const uint32_t* ld, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
);
void calc_epd_input_1ppB_1k_S3_VE(
const uint32_t* ld, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
);
/**
* 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);

uint8_t lookup_pixels_4bpp_1k(
uint16_t in, const uint8_t* conversion_lut, uint8_t from, uint32_t epd_width
);
void calc_epd_input_1bpp(
const uint32_t* line_data, uint8_t* epd_input, const uint8_t* lut, uint32_t epd_width
);
void calc_epd_input_4bpp_1k_lut_white(
const uint32_t* ld, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
);
void calc_epd_input_4bpp_1k_lut_black(
const uint32_t* ld, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
);
void calc_epd_input_4bpp_1k_lut(
const uint32_t* ld,
uint8_t* epd_input,
const uint8_t* conversion_lut,
uint8_t from,
uint32_t epd_width
);

void calc_epd_input_4bpp_lut_64k(
const uint32_t* line_data, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
);
/*
* Reorder the output buffer to account for I2S FIFO order.
*/
void reorder_line_buffer(uint32_t* line_data, int buf_len);

enum EpdDrawError calculate_lut(
uint8_t* lut, int lut_size, enum EpdDrawMode mode, int frame, const EpdWaveformPhases* phases
);

extern const uint32_t lut_1bpp_black[256];
// 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);
Loading

0 comments on commit 78a6d8b

Please sign in to comment.