clean-up display modes
This commit is contained in:
@@ -30,6 +30,8 @@ build_flags =
|
|||||||
-DENABLE_SDCARD=1
|
-DENABLE_SDCARD=1
|
||||||
-DENABLE_LCD_DMA=1
|
-DENABLE_LCD_DMA=1
|
||||||
-DENABLE_LCD_FRAMEBUFFER=1
|
-DENABLE_LCD_FRAMEBUFFER=1
|
||||||
|
-DENABLE_FRAMEBUFFER_FLIP_X_Y=1
|
||||||
|
-DENABLE_DOUBLE_BUFFERING=1
|
||||||
-DENABLE_USB_STORAGE_DEVICE=0
|
-DENABLE_USB_STORAGE_DEVICE=0
|
||||||
-DPEANUT_FULL_GBC_SUPPORT=1
|
-DPEANUT_FULL_GBC_SUPPORT=1
|
||||||
-DUSE_TINYUSB
|
-DUSE_TINYUSB
|
||||||
|
|||||||
+89
-68
@@ -19,9 +19,24 @@
|
|||||||
|
|
||||||
TFT_eSPI tft = TFT_eSPI();
|
TFT_eSPI tft = TFT_eSPI();
|
||||||
#if ENABLE_LCD_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
static TFT_eSprite framebuffer = TFT_eSprite(&tft);
|
#if ENABLE_FRAMEBUFFER_FLIP_X_Y
|
||||||
|
#define FRAMEBUFFER_WIDTH DISPLAY_HEIGHT
|
||||||
|
#define FRAMEBUFFER_HEIGHT DISPLAY_WIDTH
|
||||||
#else
|
#else
|
||||||
static TFT_eSPI& framebuffer = tft;
|
#define FRAMEBUFFER_WIDTH DISPLAY_WIDTH
|
||||||
|
#define FRAMEBUFFER_HEIGHT DISPLAY_HEIGHT
|
||||||
|
#endif
|
||||||
|
#define FRAMEBUFFER_PIXELS (FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT)
|
||||||
|
#if ENABLE_LCD_DMA && ENABLE_DOUBLE_BUFFERING
|
||||||
|
#define BUFFER_COUNT 2
|
||||||
|
#else // !ENABLE_LCD_DMA
|
||||||
|
#define BUFFER_COUNT 1 // no use in double buffering without DMA
|
||||||
|
#endif
|
||||||
|
static uint16_t framebuffers[BUFFER_COUNT][FRAMEBUFFER_PIXELS];
|
||||||
|
static int8_t activeFramebufferId = 0;
|
||||||
|
#else // !ENABLE_LCD_FRAMEBUFFER
|
||||||
|
// Note DMA mode does not have a measurable effect without a framebuffer
|
||||||
|
static uint16_t linebuffer[DISPLAY_WIDTH];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static uint8_t scaledLineOffsetTable[LCD_HEIGHT]; // scaled to 240 lines
|
static uint8_t scaledLineOffsetTable[LCD_HEIGHT]; // scaled to 240 lines
|
||||||
@@ -45,13 +60,24 @@ static void calcExtraLineTable() {
|
|||||||
|
|
||||||
void lcd_init(bool isCore1) {
|
void lcd_init(bool isCore1) {
|
||||||
tft.init();
|
tft.init();
|
||||||
|
|
||||||
#if ENABLE_LCD_DMA
|
#if ENABLE_LCD_DMA
|
||||||
// do not enable DMA on core0 as it fails on core1 if it is already enabled
|
// do not enable DMA on core0 as it fails on core1 if it is already enabled
|
||||||
if (isCore1 && !tft.initDMA(/*ctrl_cs not supported in RP2040 implementation*/)) {
|
if (isCore1 && !tft.initDMA(/*ctrl_cs not supported in RP2040 implementation*/)) {
|
||||||
error("Failed to initialize TFT DMA");
|
error("Failed to initialize TFT DMA");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
tft.setRotation(1);
|
|
||||||
|
bool rotate = true;
|
||||||
|
#if ENABLE_FRAMEBUFFER_FLIP_X_Y
|
||||||
|
// the rotation only flips x and y in GRAM but does not change the LCD screen refresh direction.
|
||||||
|
// If the display is a native portrait (and not landscape) lcd, rotating the lcd to landscape results in ugly
|
||||||
|
// diagonal update lines. Keeping it in portrait mode and Flipping x and y in the framebuffer results in nicer
|
||||||
|
// horizontal update lines. Even better would be VSync or TE line handling (which is not present in most cheap displays)
|
||||||
|
rotate = !isCore1; // rotate in start menu but not in-game
|
||||||
|
#endif
|
||||||
|
tft.setRotation(rotate ? 1 : 0);
|
||||||
|
|
||||||
tft.fillScreen(TFT_BLACK);
|
tft.fillScreen(TFT_BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,37 +99,39 @@ void lcd_draw_line(struct gb_s* gb, const uint8_t pixels[LCD_WIDTH], const uint_
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_LCD_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
void lcd_pushColors(size_t offset, const uint16_t* pixels, uint_fast16_t count) {
|
void lcd_pushLine(uint16_t screenColOffset, uint16_t screenLineOffset, uint16_t line, const uint16_t* pixels, uint_fast16_t width) {
|
||||||
uint16_t* image = (uint16_t*) framebuffer.getPointer();
|
uint16_t* framebuffer = framebuffers[activeFramebufferId];
|
||||||
memcpy(&image[offset], pixels, count * sizeof(uint16_t));
|
#if ENABLE_FRAMEBUFFER_FLIP_X_Y
|
||||||
}
|
uint_fast16_t pos = (screenColOffset * DISPLAY_HEIGHT) + DISPLAY_HEIGHT - (screenLineOffset + line) - 1;
|
||||||
|
for (uint_fast16_t i = 0; i < width; ++i) {
|
||||||
|
framebuffer[pos] = pixels[i];
|
||||||
|
pos += DISPLAY_HEIGHT;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
void lcd_pushColors(uint16_t colOffset, uint16_t lineOffset, const uint16_t* pixels, uint_fast16_t count) {
|
uint32_t offset = screenColOffset + (uint32_t)(screenLineOffset + line) * DISPLAY_WIDTH;
|
||||||
framebuffer.setAddrWindow(colOffset, lineOffset, count, 1);
|
memcpy(&framebuffer[offset], pixels, width * sizeof(uint16_t));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#else // !ENABLE_LCD_FRAMEBUFFER
|
||||||
|
void lcd_pushLine(uint16_t screenColOffset, uint16_t screenLineOffset, uint16_t line, const uint16_t* pixels, uint_fast16_t width) {
|
||||||
|
tft.setAddrWindow(screenColOffset, screenLineOffset + line, width, 1);
|
||||||
#if ENABLE_LCD_DMA
|
#if ENABLE_LCD_DMA
|
||||||
// DMA mode does not have a measurable effect without a framebuffer
|
|
||||||
static uint16_t dmaBuffer[DISPLAY_WIDTH];
|
|
||||||
tft.dmaWait();
|
tft.dmaWait();
|
||||||
memcpy(dmaBuffer, pixels, count * sizeof(uint16_t));
|
memcpy(linebuffer, pixels, width * sizeof(uint16_t));
|
||||||
tft.setSwapBytes(true);
|
tft.setSwapBytes(true);
|
||||||
tft.startWrite(); // manual start required as DMA transfer is asynchronous
|
tft.startWrite(); // manual start required as DMA transfer is asynchronous
|
||||||
tft.pushPixelsDMA((uint16_t*) dmaBuffer, count);
|
tft.pushPixelsDMA((uint16_t*) linebuffer, width);
|
||||||
//tft.endWrite(); // do not call endWrite(), as it will wait for the DMA transfer to finish, which results in no performance gain
|
//tft.endWrite(); // do not call endWrite(), as it will wait for the DMA transfer to finish, which results in no performance gain
|
||||||
#else
|
#else
|
||||||
tft.pushColors((uint16_t*) pixels, count, true);
|
tft.pushColors((uint16_t*) pixels, width, true);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void lcd_write_pixels_normal(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
void lcd_write_pixels_normal(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
||||||
const uint16_t colOffset = (DISPLAY_WIDTH - count) / 2;
|
const uint16_t colOffset = (DISPLAY_WIDTH - LCD_WIDTH) / 2;
|
||||||
const uint16_t screenLineOffset = (DISPLAY_HEIGHT - LCD_HEIGHT) / 2;
|
const uint16_t screenLineOffset = (DISPLAY_HEIGHT - LCD_HEIGHT) / 2;
|
||||||
const uint16_t lineOffset = screenLineOffset + line;
|
lcd_pushLine(colOffset, screenLineOffset, line, pixels, count);
|
||||||
#if ENABLE_LCD_FRAMEBUFFER
|
|
||||||
lcd_pushColors(lineOffset * DISPLAY_WIDTH + colOffset, pixels, count);
|
|
||||||
#else
|
|
||||||
lcd_pushColors(colOffset, lineOffset, pixels, count);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_write_pixels_stretched(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
void lcd_write_pixels_stretched(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
||||||
@@ -117,18 +145,10 @@ void lcd_write_pixels_stretched(const uint16_t* pixels, uint8_t line, uint_fast1
|
|||||||
|
|
||||||
const uint8_t lineRepeated = IS_REPEATED(line);
|
const uint8_t lineRepeated = IS_REPEATED(line);
|
||||||
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
||||||
#if ENABLE_LCD_FRAMEBUFFER
|
lcd_pushLine(0, 0, lineOffset, doubledPixels, stretchedWidth);
|
||||||
size_t offset = lineOffset * DISPLAY_WIDTH;
|
|
||||||
lcd_pushColors(offset, doubledPixels, stretchedWidth);
|
|
||||||
if (lineRepeated) {
|
if (lineRepeated) {
|
||||||
lcd_pushColors(offset + DISPLAY_WIDTH, doubledPixels, stretchedWidth);
|
lcd_pushLine(0, 0, lineOffset + 1, doubledPixels, stretchedWidth);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
lcd_pushColors(0, lineOffset, doubledPixels, stretchedWidth);
|
|
||||||
if (lineRepeated) {
|
|
||||||
lcd_pushColors(0, lineOffset + 1, doubledPixels, stretchedWidth);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_write_pixels_stretched_keep_aspect(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
void lcd_write_pixels_stretched_keep_aspect(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
||||||
@@ -144,58 +164,65 @@ void lcd_write_pixels_stretched_keep_aspect(const uint16_t* pixels, uint8_t line
|
|||||||
|
|
||||||
const uint16_t colOffset = (DISPLAY_WIDTH - stretchedWidth) / 2;
|
const uint16_t colOffset = (DISPLAY_WIDTH - stretchedWidth) / 2;
|
||||||
|
|
||||||
uint8_t lineRepeated = IS_REPEATED(line);
|
const uint8_t lineRepeated = IS_REPEATED(line);
|
||||||
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
||||||
#if ENABLE_LCD_FRAMEBUFFER
|
lcd_pushLine(colOffset, 0, lineOffset, doubledPixels, stretchedWidth);
|
||||||
size_t offset = lineOffset * DISPLAY_WIDTH + colOffset;
|
|
||||||
lcd_pushColors(offset, doubledPixels, stretchedWidth);
|
|
||||||
if (lineRepeated) {
|
if (lineRepeated) {
|
||||||
lcd_pushColors(offset + DISPLAY_WIDTH, doubledPixels, stretchedWidth);
|
lcd_pushLine(colOffset, 0, lineOffset + 1, doubledPixels, stretchedWidth);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
lcd_pushColors(colOffset, lineOffset, doubledPixels, stretchedWidth);
|
|
||||||
if (lineRepeated) {
|
|
||||||
lcd_pushColors(colOffset, lineOffset + 1, doubledPixels, stretchedWidth);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes pixels to screen or framebuffer
|
// Writes pixels to screen or framebuffer
|
||||||
void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) {
|
void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
||||||
switch (scalingMode)
|
switch (scalingMode)
|
||||||
{
|
{
|
||||||
case ScalingMode::STRETCH:
|
case ScalingMode::STRETCH:
|
||||||
lcd_write_pixels_stretched(pixels, line, nmemb);
|
lcd_write_pixels_stretched(pixels, line, count);
|
||||||
break;
|
break;
|
||||||
case ScalingMode::STRETCH_KEEP_ASPECT:
|
case ScalingMode::STRETCH_KEEP_ASPECT:
|
||||||
lcd_write_pixels_stretched_keep_aspect(pixels, line, nmemb);
|
lcd_write_pixels_stretched_keep_aspect(pixels, line, count);
|
||||||
break;
|
break;
|
||||||
case ScalingMode::NORMAL:
|
case ScalingMode::NORMAL:
|
||||||
default:
|
default:
|
||||||
lcd_write_pixels_normal(pixels, line, nmemb);
|
lcd_write_pixels_normal(pixels, line, count);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if ENABLE_LCD_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
// Writes framebuffer to screen
|
|
||||||
void lcd_write_framebuffer_to_screen() {
|
void lcd_swap_buffers() {
|
||||||
tft.setSwapBytes(true);
|
#if BUFFER_COUNT == 2
|
||||||
#ifdef ENABLE_LCD_DMA
|
activeFramebufferId = (activeFramebufferId == 0) ? 1 : 0;
|
||||||
tft.startWrite(); // manual start required as DMA transfer is asynchronous
|
|
||||||
tft.pushImageDMA(0, 0, framebuffer.width(), framebuffer.height(), (uint16_t *) framebuffer.getPointer());
|
|
||||||
//tft.endWrite(); // do not call endWrite(), as it will wait for the DMA transfer to finish, which results in no performance gain
|
|
||||||
#else
|
|
||||||
tft.pushImage(0, 0, framebuffer.width(), framebuffer.height(), (uint16_t *) framebuffer.getPointer());
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes framebuffer to screen
|
||||||
|
void lcd_write_framebuffer_to_screen() {
|
||||||
|
uint16_t* framebuffer = framebuffers[activeFramebufferId];
|
||||||
|
tft.setSwapBytes(true);
|
||||||
|
#if ENABLE_LCD_DMA
|
||||||
|
tft.startWrite(); // manual start required as DMA transfer is asynchronous
|
||||||
|
tft.pushImageDMA(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, framebuffer);
|
||||||
|
//tft.endWrite(); // do not call endWrite(), as it will wait for the DMA transfer to finish, which results in no performance gain
|
||||||
|
lcd_swap_buffers();
|
||||||
|
#else
|
||||||
|
tft.pushImage(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, framebuffer);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void lcd_fill(uint16_t color) {
|
|
||||||
|
void lcd_clear() {
|
||||||
#if ENABLE_LCD_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
framebuffer.fillSprite(color);
|
memset(framebuffers[0], 0, FRAMEBUFFER_PIXELS * sizeof(uint16_t));
|
||||||
|
#if BUFFER_COUNT == 2
|
||||||
|
memset(framebuffers[1], 0, FRAMEBUFFER_PIXELS * sizeof(uint16_t));
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
tft.fillScreen(color);
|
tft.fillScreen(TFT_BLACK);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +253,7 @@ void core1_lcd_draw_line(const uint_fast8_t line) {
|
|||||||
void core1DispatchLoop() {
|
void core1DispatchLoop() {
|
||||||
union core_cmd cmd;
|
union core_cmd cmd;
|
||||||
|
|
||||||
/* Handle commands coming from core0. */
|
// Handle commands coming from core0
|
||||||
cmd.full = multicore_fifo_pop_blocking();
|
cmd.full = multicore_fifo_pop_blocking();
|
||||||
switch (cmd.cmd) {
|
switch (cmd.cmd) {
|
||||||
case CORE_CMD_LCD_LINE:
|
case CORE_CMD_LCD_LINE:
|
||||||
@@ -234,7 +261,7 @@ void core1DispatchLoop() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CORE_CMD_IDLE_SET:
|
case CORE_CMD_IDLE_SET:
|
||||||
lcd_fill(TFT_BLACK);
|
lcd_clear();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CORE_CMD_NOP:
|
case CORE_CMD_NOP:
|
||||||
@@ -244,16 +271,10 @@ void core1DispatchLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void core1_init() {
|
void core1_init() {
|
||||||
/* Initialise and control LCD on core 1. */
|
// Initialise and control LCD on core 1
|
||||||
lcd_init(true);
|
lcd_init(true);
|
||||||
|
|
||||||
#if ENABLE_LCD_FRAMEBUFFER
|
lcd_clear();
|
||||||
framebuffer.setColorDepth(16);
|
|
||||||
framebuffer.createSprite(tft.width(), tft.height());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Clear LCD screen. */
|
|
||||||
lcd_fill(TFT_BLACK);
|
|
||||||
|
|
||||||
calcExtraLineTable();
|
calcExtraLineTable();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user