mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-08-17 23:55:04 +00:00
fix(gdeq031t10): only commit the shadow after the panel confirms (#607)
shadow_framebuffer records what the panel is currently showing, but both refresh paths updated it before the refresh was confirmed, so a failed trigger or a BUSY timeout left it describing content that was never displayed. That misdescription compounds: the shadow is sent to the controller as CMD_DATA_START_OLD, so the per-pixel transitions get computed from an image the panel never held, and draw_bitmap() diffs against it, so a region matching a wrong shadow is treated as clean and never repainted. Both paths now commit only after wait_while_busy() confirms. refresh_full() also sends new data straight from the render buffer, which drops a full-framebuffer copy from the path.
This commit is contained in:
parent
94deee8875
commit
d72d5a0ef2
@ -231,9 +231,9 @@ static void refresh_full(Gdeq031t10Internal* internal, bool mirror_180, enum Gde
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_I(TAG, "memcpy render_bitmap -> shadow_buffer");
|
// Sent straight from the render buffer: the shadow still has to describe what the panel
|
||||||
std::memcpy(internal->shadow_framebuffer, render_bitmap, FRAMEBUFFER_SIZE);
|
// is showing right now until this refresh is confirmed below.
|
||||||
if (!write_command(internal, CMD_DATA_START_NEW) || !write_data(internal, internal->shadow_framebuffer, FRAMEBUFFER_SIZE)) {
|
if (!write_command(internal, CMD_DATA_START_NEW) || !write_data(internal, render_bitmap, FRAMEBUFFER_SIZE)) {
|
||||||
LOG_E(TAG, "Failed to send new frame data");
|
LOG_E(TAG, "Failed to send new frame data");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -243,7 +243,13 @@ static void refresh_full(Gdeq031t10Internal* internal, bool mirror_180, enum Gde
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delay_millis(1); // datasheet requires >=200us settle before polling BUSY
|
delay_millis(1); // datasheet requires >=200us settle before polling BUSY
|
||||||
if (!wait_while_busy(internal)) {
|
if (wait_while_busy(internal)) {
|
||||||
|
// Confirmed: the panel now shows this frame, so the shadow can too.
|
||||||
|
std::memcpy(internal->shadow_framebuffer, render_bitmap, FRAMEBUFFER_SIZE);
|
||||||
|
} else {
|
||||||
|
// Leave the shadow alone: the panel did not confirm, so it still holds the previous
|
||||||
|
// frame. Claiming otherwise would make the next update compute its transitions from
|
||||||
|
// content the panel never displayed, and hide the difference from the change scan.
|
||||||
LOG_E(TAG, "Full refresh did not complete");
|
LOG_E(TAG, "Full refresh did not complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,15 +306,13 @@ static void refresh_window(Gdeq031t10Internal* internal, bool mirror_180, const
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// New region: render data, and update the shadow for this region as we go.
|
// New region: render data only. The shadow still has to describe what the panel is
|
||||||
|
// showing until this refresh is confirmed below.
|
||||||
n = 0;
|
n = 0;
|
||||||
for (int row = first_row; row <= last_row; row++) {
|
for (int row = first_row; row <= last_row; row++) {
|
||||||
const size_t base = static_cast<size_t>(row) * BYTES_PER_ROW + first_byte_col;
|
const size_t base = static_cast<size_t>(row) * BYTES_PER_ROW + first_byte_col;
|
||||||
for (int c = 0; c < width_bytes; c++) {
|
std::memcpy(&internal->region_buffer[n], &render_bitmap[base], width_bytes);
|
||||||
const uint8_t value = render_bitmap[base + c];
|
n += width_bytes;
|
||||||
internal->region_buffer[n++] = value;
|
|
||||||
internal->shadow_framebuffer[base + c] = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!write_command(internal, CMD_DATA_START_NEW) || !write_data(internal, internal->region_buffer, n)) {
|
if (!write_command(internal, CMD_DATA_START_NEW) || !write_data(internal, internal->region_buffer, n)) {
|
||||||
LOG_E(TAG, "Failed to send new window data");
|
LOG_E(TAG, "Failed to send new window data");
|
||||||
@ -316,14 +320,25 @@ static void refresh_window(Gdeq031t10Internal* internal, bool mirror_180, const
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool confirmed = false;
|
||||||
if (write_command(internal, CMD_DISPLAY_REFRESH)) {
|
if (write_command(internal, CMD_DISPLAY_REFRESH)) {
|
||||||
delay_millis(1);
|
delay_millis(1);
|
||||||
if (!wait_while_busy(internal)) {
|
confirmed = wait_while_busy(internal);
|
||||||
|
if (!confirmed) {
|
||||||
LOG_E(TAG, "Window refresh did not complete");
|
LOG_E(TAG, "Window refresh did not complete");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_E(TAG, "Failed to trigger window refresh");
|
LOG_E(TAG, "Failed to trigger window refresh");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only commit the region the panel confirmed; on failure the shadow keeps describing
|
||||||
|
// the previous content, so the next change scan still sees this region as dirty.
|
||||||
|
if (confirmed) {
|
||||||
|
for (int row = first_row; row <= last_row; row++) {
|
||||||
|
const size_t base = static_cast<size_t>(row) * BYTES_PER_ROW + first_byte_col;
|
||||||
|
std::memcpy(&internal->shadow_framebuffer[base], &render_bitmap[base], width_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
write_command(internal, CMD_PARTIAL_OUT);
|
write_command(internal, CMD_PARTIAL_OUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user