Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TFT speed improvements #4684

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

robertfisk
Copy link
Contributor

Screen update performance with TFT displays is... slow. Particularly on nRF processors with the default Arduino SPI driver. This PR greatly improves TFT screen update speed. I have measured a 32x speedup with a 128x160 TFT screen running on a RAK4631.

Note I don't have any ESP32 boards to test with but I expect a similar improvement. Someone with a T-deck please check.

Issue 1:
The TFTDisplay.cpp translation layer uses a pixel-by-pixel screen update strategy which is simple but slow. The following strategy is implemented here:

  1. Quickly scan 8 rows at a time for modified pixels that need updating. This is efficient for the OLEDDisplay buffer format where 8 vertical pixels are stored in each byte.
  2. If modified pixels are found, scan each row individually and record where the first modified pixel is found (in the X direction).
  3. From the first modified pixel in that row until the end of the row, write 565 pixel data into a line buffer while also recording the last modified pixel in that row.
  4. Send the line buffer to the TFT display as a single block write, but only from the first to the last modified pixel.
  5. Repeat 2-4 for each of the 8 rows that might be modified.
  6. Repeat 1-5 for the next block of 8 rows.
  7. Copy display buffer to back buffer using memcpy (more efficient), but only if the display data has changed.

Not only does this speed up TFT screen updates, it also reduces idle CPU usage. The screen update function is called once every second and this PR is much more efficient processing unchanged frames.

I measure a 4x speed increase with the above changes on a nRF / RAK4631. ESP32s should experience a greater speedup as they already have support for SPI block transfers.

Issue 2:
The default Arduino SPI driver on nRF platforms does not support multi-byte transfers, limiting the effectiveness of the above improvements. I have forked the TFT_eSPI library to implement uint16, uint32, and large block transfers on nRF:
https://github.com/robertfisk/TFT_eSPI

Change the following line in your variant's platformio.ini lib_deps section from:
bodmer/TFT_eSPI
to:
https://github.com/robertfisk/TFT_eSPI.git#nrf_faster_spi

This forked library takes the 4x speed boost up to a 32x speed boost on nRF platforms.

Bonus Speedboost
On nRF platforms, add the following line to your variant.h to increase the second SPI port's maximum speed from 8MHz to 32MHz:
#define SPI_32MHZ_INTERFACE 1
Then configure SPI_FREQUENCY to some number between 8000000 and 32000000 depending on the capability of your hardware.

@caveman99
Copy link
Sponsor Member

Great work, however there's only one platform left that actually uses TFT_eSPI - all other TFT drivers have been ported to use lovyanGFX. I'd be in favor to throw out the eSPI dependency alltogether and see if we can accelerate the lovyan framebuffer copy in a similar way.

@robertfisk
Copy link
Contributor Author

Great work, however there's only one platform left that actually uses TFT_eSPI - all other TFT drivers have been ported to use lovyanGFX. I'd be in favor to throw out the eSPI dependency alltogether and see if we can accelerate the lovyan framebuffer copy in a similar way.

I assume we are using TFT_eSPI for the nRF board because lovyanGFX does not support nRF. This PR should give a similar many-multiple speedup on lovyanGFX / ESP32 as the TFTDisplay interface layer is used by both. I just need someone to check that as I don't have any ESP32 hardware myself.

@mverch67
Copy link
Collaborator

mverch67 commented Sep 12, 2024

I've tested this code with T-Deck and the SenseCAP Indicator (both esp32-S3 and lovyanGFX) but it does not work at all with the Indicator (screen white) and on T-Deck the display either stays black, boots in a loop, or the screen appears after 10 secs (skipping the boot screen). Though, once it is there, it feels fast.

Edit1:
Now after I had turned off the Indicator for a while and switched it back on the screen came immediately including boot screen. The T-Deck still does not want to behave normal.

Edit2:
I've reverted back to the original code and the T-Deck boot screen came as usual. So there must be indeed something wrong or missing in your code changes.

@robertfisk
Copy link
Contributor Author

Thanks for the feedback, it seems like I've still got work to do! @mverch67 are you able to capture a log from the Tdeck when it fails to boot? I'm particularly interested if you see "Not enough memory to create TFT line buffer"

@mverch67
Copy link
Collaborator

mverch67 commented Sep 13, 2024

Thanks for the feedback, it seems like I've still got work to do! @mverch67 are you able to capture a log from the Tdeck when it fails to boot? I'm particularly interested if you see "Not enough memory to create TFT line buffer"

//\ E S H T /\ S T / C

INFO  | ??:??:?? 1 Booted, wake cause 0 (boot count 1), reset_reason=reset
DEBUG | ??:??:?? 1 Filesystem files (20480/1048576 Bytes):
DEBUG | ??:??:?? 1  /prefs/channels.proto (57 Bytes)
DEBUG | ??:??:?? 1  /prefs/config.proto (172 Bytes)
DEBUG | ??:??:?? 1  /prefs/db.proto (972 Bytes)
DEBUG | ??:??:?? 1  /prefs/module.proto (105 Bytes)
DEBUG | ??:??:?? 2 Using analog input 4 for battery level
INFO  | ??:??:?? 2 ADCmod: ADC Characterization based on Two Point values and fitting curve coefficients stored in eFuse
INFO  | ??:??:?? 2 Scanning for i2c devices...
DEBUG | ??:??:?? 2 Scanning for I2C devices on port 1
DEBUG | ??:??:?? 2 I2C device found at address 0x40
DEBUG | ??:??:?? 2 Wire.available() = 2
DEBUG | ??:??:?? 2 Register MFG_UID: 0xffff
INFO  | ??:??:?? 2 INA219 sensor found at address 0x40
DEBUG | ??:??:?? 2 I2C device found at address 0x55
INFO  | ??:??:?? 2 T-Deck keyboard found
DEBUG | ??:??:?? 2 I2C device found at address 0x5d
INFO  | ??:??:?? 2 LPS22HB sensor found
INFO  | ??:??:?? 2 3 I2C devices found
DEBUG | ??:??:?? 2 acc_info = 0
DEBUG | ??:??:?? 2 found i2c sensor meshtastic_TelemetrySensorType_INA219
DEBUG | ??:??:?? 2 found i2c sensor meshtastic_TelemetrySensorType_LPS22
DEBUG | ??:??:?? 3 No SD_MMC card detected
INFO  | ??:??:?? 3 S:B:50,2.5.1.3d72fbb1
DEBUG | ??:??:?? 3 Total heap: 272240
DEBUG | ??:??:?? 3 Free heap: 230180
DEBUG | ??:??:?? 3 Total PSRAM: 8386295
DEBUG | ??:??:?? 3 Free PSRAM: 8386295
DEBUG | ??:??:?? 3 NVS: UsedEntries 88, FreeEntries 542, AllEntries 630, NameSpaces 4
DEBUG | ??:??:?? 3 Setup Preferences in Flash Storage
DEBUG | ??:??:?? 3 Number of Device Reboots: 1986
DEBUG | ??:??:?? 3 No OTA firmware available
INFO  | ??:??:?? 3 Initializing NodeDB
INFO  | ??:??:?? 3 Loading /prefs/db.proto
INFO  | ??:??:?? 3 Loaded /prefs/db.proto successfully
INFO  | ??:??:?? 3 Loaded saved devicestate version 22, with nodecount: 10
INFO  | ??:??:?? 3 Loading /prefs/config.proto
INFO  | ??:??:?? 3 Loaded /prefs/config.proto successfully
INFO  | ??:??:?? 3 Loaded saved config version 22
INFO  | ??:??:?? 3 Loading /prefs/module.proto
INFO  | ??:??:?? 3 Loaded /prefs/module.proto successfully
INFO  | ??:??:?? 3 Loaded saved moduleConfig version 23
INFO  | ??:??:?? 3 Loading /prefs/channels.proto
INFO  | ??:??:?? 3 Loaded /prefs/channels.proto successfully
INFO  | ??:??:?? 3 Loaded saved channelFile version 22
ERROR | ??:??:?? 3 Could not open / read /oem/oem.proto
DEBUG | ??:??:?? 3 cleanupMeshDB purged 0 entries
DEBUG | ??:??:?? 3 Using nodenum 0x99baac90 
DEBUG | ??:??:?? 3 Old Pubkey: b6 36 5e 88 73 67 b0 f9 a8 5e 9f a0 62 57 69 35 b0 35 f2 1c 95 fa 54 38 d1 a9 1c c9 c4 6d 81 21 
INFO  | ??:??:?? 3 Using saved PKI keys
DEBUG | ??:??:?? 3 Number of Device Reboots: 1986
DEBUG | ??:??:?? 3 Expanding short PSK #1
INFO  | ??:??:?? 3 Wanted region 3, using EU_868
INFO  | ??:??:?? 3 Saving /prefs/db.proto
DEBUG | ??:??:?? 3 Using GPIO00 for button
DEBUG | ??:??:?? 3 SPI.begin(SCK=40, MISO=38, MOSI=41, NSS=9)
DEBUG | ??:??:?? 3 TFTDisplay!
DEBUG | ??:??:?? 3 Set Timezone to GMT0
DEBUG | ??:??:?? 3 Read RTC time as 14
DEBUG | ??:??:?? 3 Starting audio thread
DEBUG | ??:??:?? 3 NeighborInfoModule is disabled
DEBUG | ??:??:?? 3 Trackball GPIO initialized (3, 15, 1, 2, 0)
ERROR | ??:??:?? 3 Could not open / read /prefs/cannedConf.proto
INFO  | ??:??:?? 3 CannedMessageModule is enabled
ERROR | ??:??:?? 3 Could not open / read /prefs/ringtone.proto
INFO  | ??:??:?? 3 Initializing External Notification Module
INFO  | ??:??:?? 3 Doing TFT init
INFO  | ??:??:?? 3 Power to TFT Backlight

[17:55:55.548] Disconnected
[17:55:56.551] Connected to /dev/ttyACM0
��@INFO  | ??:??:?? 1 

//\ E S H T /\ S T / C

To get the esp log stack trace I enabled the USB mode for T-Deck, but now the T-Deck works all the time without any issue.
So for the T-Deck I suggest to keep the USB MODE "on" all the time. With the former arduino-esp32 v3.0.x update this is now possible.
t-deck.json: "-DARDUINO_USB_MODE=1"

INFO  | ??:??:?? 2 Initializing External Notification Module
INFO  | ??:??:?? 2 Doing TFT init
INFO  | ??:??:?? 2 Power to TFT Backlight
[  2363][E][Wire.cpp:137] setPins(): bus already initialized. change pins only when not.
[  2371][W][Wire.cpp:301] begin(): Bus already started in Master Mode.
INFO  | ??:??:?? 2 Turning on screen
DEBUG | ??:??:?? 2 Brightness is set to value: 130 
DEBUG | ??:??:?? 2 haveGlyphs=1
INFO  | ??:??:?? 2 TouchScreen initialized 30 20
DEBUG | ??:??:?? 2 Module wants a UI Frame
DEBUG | ??:??:?? 2 Module wants a UI Frame
DEBUG | ??:??:?? 2 SX126xInterface(cs=9, irq=45, rst=17, busy=13)
DEBUG | ??:??:?? 2 SX126X_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at 1.800000 V
INFO  | ??:??:?? 2 Starting meshradio init...

But I think all devices that use TFTDisplay should be checked (e.g. heltec-tracker, T-Watch, PICOmputer, ...)

@robertfisk
Copy link
Contributor Author

INFO  | ??:??:?? 3 Doing TFT init
INFO  | ??:??:?? 3 Power to TFT Backlight

[17:55:55.548] Disconnected

It seems to be crashing at the point where it mallocs the new line buffer. For a 320px wide screen this would be 640 bytes. Is the ESP32 really short on heap space?

I don't have any ESP32 hardware to debug this, so I'm going to have to park this for now. Any suggestions are welcome

@robertfisk robertfisk marked this pull request as draft September 15, 2024 21:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants