roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
blit.cpp
Go to the documentation of this file.
2
3#if defined(ESP_PLATFORM)
4
5#include <algorithm>
6#include <cstdint>
7#include <cstring>
8
10
11#if CONFIG_IDF_TARGET_ESP32S3
12
13#include "esp_async_memcpy.h"
14#include "esp_err.h"
15#include "esp_memory_utils.h"
16#include "freertos/FreeRTOS.h"
17#include "freertos/semphr.h"
18#include "roo_logging.h"
19
20#ifndef ROO_DISPLAY_DMA_MIN_BYTES
21#define ROO_DISPLAY_DMA_MIN_BYTES 256
22#endif
23
24namespace roo_display {
25namespace {
26
27constexpr size_t kDmaAlign = 16;
28constexpr size_t kExtDmaAlign = 32;
29constexpr size_t kDmaThresholdBytes = ROO_DISPLAY_DMA_MIN_BYTES;
30
31struct BlitState {
32 BlitState()
33 : done(xSemaphoreCreateBinaryStatic(&done_buf)),
34 lock(xSemaphoreCreateMutexStatic(&lock_buf)) {}
35
36 async_memcpy_handle_t handle = nullptr;
37 StaticSemaphore_t done_buf;
38 SemaphoreHandle_t done = nullptr;
39 StaticSemaphore_t lock_buf;
40 SemaphoreHandle_t lock = nullptr;
41
42 const roo::byte* src_row = nullptr;
43 roo::byte* dst_row = nullptr;
44 size_t src_stride = 0;
45 size_t dst_stride = 0;
46 size_t row_bytes = 0;
47 size_t remaining_rows = 0;
48 bool transfer_failed = false;
49 bool install_attempted = false;
50};
51
52BlitState& state() {
53 static BlitState s;
54 return s;
55}
56
57void copy_sync(const roo::byte* src_ptr, size_t src_stride, roo::byte* dst_ptr,
58 size_t dst_stride, size_t width, size_t height) {
59 if (src_stride == width && dst_stride == width) {
60 std::memcpy(dst_ptr, src_ptr, width * height);
61 return;
62 }
63
64 const roo::byte* src_row = src_ptr;
65 roo::byte* dst_row = dst_ptr;
66 for (size_t row = 0; row < height; ++row) {
67 std::memcpy(dst_row, src_row, width);
68 src_row += src_stride;
69 dst_row += dst_stride;
70 }
71}
72
73inline bool is_aligned(const void* ptr, size_t align) {
74 return (reinterpret_cast<uintptr_t>(ptr) % align) == 0;
75}
76
77inline size_t dma_align_for_ptr(const void* ptr) {
78 return esp_ptr_external_ram(ptr) ? kExtDmaAlign : kDmaAlign;
79}
80
81inline bool can_dma_copy_rows(const void* src, const void* dst,
82 size_t row_bytes, size_t src_stride,
83 size_t dst_stride) {
84 const bool src_dma_capable =
85 esp_ptr_dma_capable(src) || esp_ptr_dma_ext_capable(src);
86 const bool dst_dma_capable =
87 esp_ptr_dma_capable(dst) || esp_ptr_dma_ext_capable(dst);
88 const size_t required_align =
89 std::max(dma_align_for_ptr(src), dma_align_for_ptr(dst));
90 return row_bytes > 0 && is_aligned(src, required_align) &&
91 is_aligned(dst, required_align) && (row_bytes % required_align) == 0 &&
92 (src_stride % required_align) == 0 &&
93 (dst_stride % required_align) == 0 && src_dma_capable &&
94 dst_dma_capable;
95}
96
97bool on_copy_done(async_memcpy_handle_t handle, async_memcpy_event_t*,
98 void* ctx) {
99 auto* st = static_cast<BlitState*>(ctx);
100 if (st->remaining_rows > 0) {
101 st->src_row += st->src_stride;
102 st->dst_row += st->dst_stride;
103 --st->remaining_rows;
104 }
105
106 if (st->remaining_rows == 0 || st->transfer_failed) {
107 BaseType_t high_wakeup = pdFALSE;
108 xSemaphoreGiveFromISR(st->done, &high_wakeup);
109 return high_wakeup == pdTRUE;
110 }
111
112 esp_err_t err =
113 esp_async_memcpy(handle, st->dst_row, const_cast<roo::byte*>(st->src_row),
114 st->row_bytes, on_copy_done, st);
115 if (err != ESP_OK) {
116 st->transfer_failed = true;
117 BaseType_t high_wakeup = pdFALSE;
118 xSemaphoreGiveFromISR(st->done, &high_wakeup);
119 return high_wakeup == pdTRUE;
120 }
121 return false;
122}
123
124bool dma_copy_rows_and_wait(BlitState& st, const roo::byte* src, roo::byte* dst,
125 size_t src_stride, size_t dst_stride,
126 size_t row_bytes, size_t row_count) {
127 while (xSemaphoreTake(st.done, 0) == pdTRUE) {
128 }
129
130 st.src_row = src;
131 st.dst_row = dst;
132 st.src_stride = src_stride;
133 st.dst_stride = dst_stride;
134 st.row_bytes = row_bytes;
135 st.remaining_rows = row_count;
136 st.transfer_failed = false;
137
138 esp_err_t err = esp_async_memcpy(st.handle, st.dst_row,
139 const_cast<roo::byte*>(st.src_row),
140 st.row_bytes, on_copy_done, &st);
141 CHECK_EQ(err, ESP_OK) << esp_err_to_name(err);
142
143 CHECK_EQ(xSemaphoreTake(st.done, portMAX_DELAY), pdTRUE);
144 return !st.transfer_failed;
145}
146
147void ensure_init() {
148 BlitState& st = state();
149 if (st.handle != nullptr || st.install_attempted) return;
150 CHECK_NOTNULL(st.done);
151 CHECK_NOTNULL(st.lock);
152
153 st.install_attempted = true;
154 async_memcpy_config_t cfg = ASYNC_MEMCPY_DEFAULT_CONFIG();
155 cfg.backlog = 2;
156 async_memcpy_handle_t handle = nullptr;
157 if (esp_async_memcpy_install(&cfg, &handle) == ESP_OK) {
158 st.handle = handle;
159 }
160}
161
162} // namespace
163
164void blit_init() { ensure_init(); }
165
166void blit_deinit() {
167 BlitState& st = state();
168 if (st.handle == nullptr || st.lock == nullptr) return;
169 if (xSemaphoreTake(st.lock, 0) != pdTRUE) return;
170 esp_async_memcpy_uninstall(st.handle);
171 st.handle = nullptr;
172 st.install_attempted = false;
173 xSemaphoreGive(st.lock);
174}
175
176void blit(const roo::byte* src_ptr, size_t src_stride, roo::byte* dst_ptr,
177 size_t dst_stride, size_t width, size_t height) {
178 if (src_ptr == nullptr || dst_ptr == nullptr || width == 0 || height == 0) {
179 return;
180 }
181
182 // Normalize contiguous transfer into one super-row so threshold and DMA
183 // eligibility checks can share one code path.
184 size_t dma_row_bytes = width;
185 size_t dma_row_count = height;
186 size_t dma_src_stride = src_stride;
187 size_t dma_dst_stride = dst_stride;
188 if (src_stride == width && dst_stride == width) {
189 dma_row_bytes = width * height;
190 dma_row_count = 1;
191 dma_src_stride = dma_row_bytes;
192 dma_dst_stride = dma_row_bytes;
193 }
194
195 if (dma_row_bytes < kDmaThresholdBytes ||
196 !can_dma_copy_rows(src_ptr, dst_ptr, dma_row_bytes, dma_src_stride,
197 dma_dst_stride)) {
198 copy_sync(src_ptr, src_stride, dst_ptr, dst_stride, width, height);
199 return;
200 }
201
202 ensure_init();
203 BlitState& st = state();
204 CHECK_EQ(xSemaphoreTake(st.lock, portMAX_DELAY), pdTRUE);
205
206 bool success =
207 dma_copy_rows_and_wait(st, src_ptr, dst_ptr, dma_src_stride,
208 dma_dst_stride, dma_row_bytes, dma_row_count);
209
210 xSemaphoreGive(st.lock);
211
212 if (!success) {
213 copy_sync(src_ptr, src_stride, dst_ptr, dst_stride, width, height);
214 }
215}
216
217} // namespace roo_display
218
219#else
220
221namespace roo_display {
222
223void blit_init() {}
224
225void blit_deinit() {}
226
227void blit(const roo::byte* src_ptr, size_t src_stride, roo::byte* dst_ptr,
228 size_t dst_stride, size_t width, size_t height) {
229 if (src_ptr == nullptr || dst_ptr == nullptr || width == 0 || height == 0) {
230 return;
231 }
232
233 if (src_stride == width && dst_stride == width) {
234 std::memcpy(dst_ptr, src_ptr, width * height);
235 return;
236 }
237
238 const roo::byte* src_row = src_ptr;
239 roo::byte* dst_row = dst_ptr;
240 for (size_t row = 0; row < height; ++row) {
241 std::memcpy(dst_row, src_row, width);
242 src_row += src_stride;
243 dst_row += dst_stride;
244 }
245}
246
247} // namespace roo_display
248
249#endif
250
251#endif // defined(ESP_PLATFORM)
Defines 140 opaque HTML named colors.
void blit_deinit()
Definition blit.cpp:13
void blit_init()
Definition blit.cpp:11
void blit(const roo::byte *src_ptr, size_t src_stride, roo::byte *dst_ptr, size_t dst_stride, size_t width, size_t height)
Definition blit.cpp:15