From 1c35033c1d52c8be04607e9ebd6c1df6b38c8fed Mon Sep 17 00:00:00 2001 From: ainyan03 <205502311+ainyan03@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:42:55 +0000 Subject: [PATCH 1/2] Panel_AMOLED: send clipped image rows in one pixel-write frame When an image was clipped horizontally (its rows are not contiguous in the source), Panel_AMOLED sent each row separately: with use_dma the rows were queued without ever sending the pixel-write start, and without it every row restarted the write (RAMWR), which rewinds to the start of the window. On a CO5300 panel without a frame buffer, a clipped pushSprite or pushImage drew nothing on either path. The write is now started once and every row is streamed in that frame. --- src/lgfx/v1/panel/Panel_AMOLED.inl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lgfx/v1/panel/Panel_AMOLED.inl b/src/lgfx/v1/panel/Panel_AMOLED.inl index 6685a270..6fa85e79 100644 --- a/src/lgfx/v1/panel/Panel_AMOLED.inl +++ b/src/lgfx/v1/panel/Panel_AMOLED.inl @@ -528,6 +528,8 @@ namespace lgfx else { auto add = param->src_bitwidth * bytes; + // Keep every row in one RAMWR frame; restarting it rewinds the window. + start_qspi(); if (use_dma) { if (_cfg.dlen_16bit && ((wb * h) & 1)) @@ -545,7 +547,7 @@ namespace lgfx { do { - write_bytes(src, wb, false); + _bus->writeBytes(src, wb, true, false); src += add; } while (--h); } From 473ebb5e468923b8c0ae5e21c42ddb63ece148fc Mon Sep 17 00:00:00 2001 From: ainyan03 <205502311+ainyan03@users.noreply.github.com> Date: Fri, 16 Oct 2026 02:42:55 +0000 Subject: [PATCH 2/2] Let the destination bus decide DMA for sprites, deprecate SpriteBuffer::use_dma() pushSprite passed SpriteBuffer::use_dma() to the panel, deciding on the caller side whether the sprite buffer could be read by DMA. The bus already checks the source itself, so pushSprite now always passes true and use_dma() is deprecated. The meaning of use_dma is documented on Bus / Panel / LGFXBase: true allows the bus to keep reading the caller's buffer until the transfer completes (it copies or uses the CPU when it cannot read the source directly); false means the buffer is not read after the call returns. Wait on the destination with waitDMA() before reusing the buffer while startWrite() is held. pushImage keeps false and pushImageDMA keeps true. --- src/lgfx/v1/Bus.hpp | 5 +++++ src/lgfx/v1/LGFXBase.hpp | 3 +++ src/lgfx/v1/LGFX_Sprite.hpp | 8 ++++---- src/lgfx/v1/Panel.hpp | 5 +++++ src/lgfx/v1/misc/SpriteBuffer.hpp | 1 + 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/lgfx/v1/Bus.hpp b/src/lgfx/v1/Bus.hpp index a5f3462a..1d024a2b 100644 --- a/src/lgfx/v1/Bus.hpp +++ b/src/lgfx/v1/Bus.hpp @@ -81,6 +81,8 @@ namespace lgfx virtual void initDMA(void) = 0; /// DMA転送キューを追加する。; + /// Permits retaining data until the destination's waitDMA() completes. + /// The bus must copy or use CPU output if DMA cannot read the source directly. virtual void addDMAQueue(const uint8_t* data, uint32_t length) = 0; // { writeBytes(data, length, true); } /// 蓄積したDMA転送キューの送信を実行する。; @@ -111,6 +113,9 @@ namespace lgfx virtual void writePixels(pixelcopy_t* pc, uint32_t length) = 0; /// 引数のバイト列を送信する。; + /// use_dma permits reading data after return; keep it unchanged until the destination's waitDMA() completes. + /// false consumes data before return. Internal copies may use DMA in either case. + /// The bus selects direct DMA, an internal copy, or CPU output based on the source. virtual void writeBytes(const uint8_t* data, uint32_t length, bool dc, bool use_dma) = 0; virtual void beginRead(uint_fast8_t dummy_bits) { beginRead(); if (dummy_bits) { readData(dummy_bits); } } diff --git a/src/lgfx/v1/LGFXBase.hpp b/src/lgfx/v1/LGFXBase.hpp index 2548a206..655eb777 100644 --- a/src/lgfx/v1/LGFXBase.hpp +++ b/src/lgfx/v1/LGFXBase.hpp @@ -446,6 +446,9 @@ namespace lgfx pushImage(x, y, w, h, &pc, true); } + /// use_dma permits reading param->src_data after return; keep it unchanged until this destination's waitDMA() completes. + /// false consumes the source before return. Internal copies may use DMA in either case. + /// The bus selects direct DMA, an internal copy, or CPU output based on the source. void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, pixelcopy_t *param, bool use_dma = false); //---------------------------------------------------------------------------- diff --git a/src/lgfx/v1/LGFX_Sprite.hpp b/src/lgfx/v1/LGFX_Sprite.hpp index 5daa25af..2b3ffc12 100644 --- a/src/lgfx/v1/LGFX_Sprite.hpp +++ b/src/lgfx/v1/LGFX_Sprite.hpp @@ -421,10 +421,10 @@ namespace lgfx void push_sprite(LovyanGFX* dst, int32_t x, int32_t y, uint32_t transp = pixelcopy_t::NON_TRANSP) { pixelcopy_t p(_img, dst->getColorDepth(), getColorDepth(), dst->hasPalette(), _palette, transp); - // use_dma はバッファが DMA の転送元になれるかで決まる (外部 RAM も対応チップでは真)。 - // DMA 中にこのスプライトのバッファを書き換えないこと。dst の startWrite() 保持中に再描画するなら - // 先に dst->waitDMA() を呼ぶ (待つのは転送先。スプライト自身の waitDMA() は何もしない) - dst->pushImage(x, y, _panel_sprite._panel_width, _panel_sprite._panel_height, &p, _panel_sprite.getSpriteBuffer()->use_dma()); + // Permit the destination bus to retain the source; it decides whether direct DMA is possible. + // If dst->startWrite() is kept open, call dst->waitDMA() before modifying or freeing this buffer. + // Waiting on the source sprite does not wait for the destination's transfer. + dst->pushImage(x, y, _panel_sprite._panel_width, _panel_sprite._panel_height, &p, true); } void push_rotate_zoom(LovyanGFX* dst, float x, float y, float angle, float zoom_x, float zoom_y, uint32_t transp = pixelcopy_t::NON_TRANSP) diff --git a/src/lgfx/v1/Panel.hpp b/src/lgfx/v1/Panel.hpp index 15074974..59993e72 100644 --- a/src/lgfx/v1/Panel.hpp +++ b/src/lgfx/v1/Panel.hpp @@ -128,8 +128,13 @@ namespace lgfx virtual void setWindow(uint_fast16_t xs, uint_fast16_t ys, uint_fast16_t xe, uint_fast16_t ye) = 0; virtual void drawPixelPreclipped(uint_fast16_t x, uint_fast16_t y, uint32_t rawcolor) = 0; virtual void writeFillRectPreclipped(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, uint32_t rawcolor) = 0; + /// use_dma permits retaining param->src_data until this destination's waitDMA() completes. + /// false consumes the source before return; internal copies may still use DMA. + /// The bus copies or uses CPU output if DMA cannot read the source directly. virtual void writeImage(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t* param, bool use_dma) = 0; virtual void writeImageARGB(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t* param) = 0; + /// use_dma permits retaining param->src_data until this destination's waitDMA() completes. + /// false consumes the source before return; the bus may DMA an internal copy. virtual void writePixels(pixelcopy_t* param, uint32_t len, bool use_dma) = 0; virtual uint32_t readCommand(uint_fast16_t cmd, uint_fast8_t index = 0, uint_fast8_t length = 4) = 0; diff --git a/src/lgfx/v1/misc/SpriteBuffer.hpp b/src/lgfx/v1/misc/SpriteBuffer.hpp index ed18b574..51a28c18 100644 --- a/src/lgfx/v1/misc/SpriteBuffer.hpp +++ b/src/lgfx/v1/misc/SpriteBuffer.hpp @@ -75,6 +75,7 @@ namespace lgfx void release(void); + [[deprecated("DMA source eligibility is determined by the destination bus")]] bool use_dma(void) const; bool use_memcpy(void) const { return _source != AllocationSource::Psram; } };