Arm-2D  
2D Image Processing Library for Cortex-M Processors
arm_2d_helper_pfb.h
1/*
2 * Copyright (C) 2022 Arm Limited or its affiliates. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Licensed under the Apache License, Version 2.0 (the License); you may
7 * not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19/* ----------------------------------------------------------------------
20 * Project: Arm-2D Library
21 * Title: #include "arm_2d_helper_pfb.h"
22 * Description: Public header file for the PFB helper service
23 *
24 * $Date: 09. April 2023
25 * $Revision: V.1.5.0
26 *
27 * Target Processor: Cortex-M cores
28 * -------------------------------------------------------------------- */
29
30#ifndef __ARM_2D_HELPER_PFB_H__
31#define __ARM_2D_HELPER_PFB_H__
32
33/*============================ INCLUDES ======================================*/
34#include "arm_2d.h"
35
36#include "./__arm_2d_helper_common.h"
37
38#ifdef __cplusplus
39extern "C" {
40#endif
41
42
43#if defined(__clang__)
44# pragma clang diagnostic push
45# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
46# pragma clang diagnostic ignored "-Wpadded"
47#endif
48
49
50/*!
51 * \addtogroup gHelper 7 Helper Services
52 * @{
53 */
54
55/*============================ MACROS ========================================*/
56
57#define ARM_2D_FPS_MODE_RENDER_ONLY 0
58#define ARM_2D_FPS_MODE_REAL 1
59
60/*============================ MACROFIED FUNCTIONS ===========================*/
61
62/*!
63 * \brief a macro wrapper in uppercase to help initialising PFB service
64 * \param[in] __CB_ADDR the address of the arm_2d_helper_pfb_t object
65 * \param[in] __SCREEN_WIDTH the width of the screen
66 * \param[in] __SCREEN_HEIGHT the hight of the screen
67 * \param[in] __PIXEL_TYPE the integer type of the pixel, i.e. uint8_t, uint16_t,
68 * uint32_t
69 * \param[in] __WIDTH the width of the PFB block
70 * \param[in] __HEIGHT the height of the PFB block
71 * \note For the same number of pixels in a PFB block, please priority the width
72 * over height, for example, 240 * 1 is better than 30 * 8
73 * \param[in] __PFB_NUM the number of PFB blocks in the built-in PFB pool.
74 * \param[in] ... a code block to add additional initializer, see example below:
75 * \return arm_2d_err_t the result of the initialisation process
76 *
77 * \code {.c}
78
79 static ARM_NOINIT arm_2d_helper_pfb_t s_tExamplePFB;
80 ...
81 // initialise FPB helper
82 if (ARM_2D_HELPER_PFB_INIT(
83 &s_tExamplePFB, // FPB Helper object
84 __GLCD_CFG_SCEEN_WIDTH__, // screen width
85 __GLCD_CFG_SCEEN_HEIGHT__, // screen height
86 uint16_t, // colour date type
87 240, // PFB block width
88 1, // PFB block height
89 1, // number of PFB in the PFB pool
90 {
91 .evtOnLowLevelRendering = {
92 // callback for low level rendering
93 .fnHandler = &__pfb_render_handler,
94 },
95 .evtOnDrawing = {
96 // callback for drawing GUI
97 .fnHandler = &__pfb_draw_background_handler,
98 },
99 },
100 //.FrameBuffer.bSwapRGB16 = true,
101 ) < 0) {
102 //! error detected
103 assert(false);
104 }
105 * \endcode
106 *
107 */
108#define ARM_2D_HELPER_PFB_INIT( __CB_ADDR, /* PFB Helper object address */ \
109 __SCREEN_WIDTH, /* Screen width */ \
110 __SCREEN_HEIGHT,/* Screen height */ \
111 __PIXEL_TYPE, /* The type of the pixels */ \
112 __WIDTH, /* The width of the PFB block */\
113 __HEIGHT, /* The height of the PFB block*/\
114 __PFB_NUM, /* Block count in the PFB pool*/\
115 ... /* Event Handler */ \
116 ) \
117 ({ \
118 __attribute__((section(".bss.noinit.arm_2d_pfb_pool"))) \
119 static struct { \
120 arm_2d_pfb_t tFPB; \
121 __ALIGNED(4) \
122 __PIXEL_TYPE tBuffer[(__WIDTH) * (__HEIGHT)]; \
123 } s_tPFBs[__PFB_NUM]; \
124 \
125 arm_2d_helper_pfb_cfg_t tCFG = { \
126 .tDisplayArea.tSize = { \
127 .iWidth = (__SCREEN_WIDTH), \
128 .iHeight = (__SCREEN_HEIGHT), \
129 }, \
130 \
131 .FrameBuffer.ptPFBs = (arm_2d_pfb_t *)s_tPFBs, \
132 .FrameBuffer.tFrameSize = { \
133 .iWidth = (__WIDTH), \
134 .iHeight = (__HEIGHT), \
135 }, \
136 .FrameBuffer.wBufferSize = sizeof(s_tPFBs[0].tBuffer), \
137 .FrameBuffer.hwPFBNum = dimof(s_tPFBs), \
138 .Dependency = \
139 __VA_ARGS__ \
140 }; \
141 \
142 arm_2d_helper_pfb_init((__CB_ADDR), &tCFG); \
143 })
144
145/*!
146 * \brief a macro wrapper to update the evtOnDrawring event handler
147 * \param[in] __CB_ADDR the address of the arm_2d_helper_pfb_t object
148 * \param[in] __HANDLER the new handler
149 * \param[in] ... [Optional] an address (of user defined structure) passed to the
150 * event handler.
151 * \return arm_2d_err_t the process result
152 */
153#define ARM_2D_HELPER_PFB_UPDATE_ON_DRAW_HANDLER( \
154 __CB_ADDR, /* PFB Helper object address */ \
155 __HANDLER, /* new on-draw-handler function*/\
156 ...) /* An optional target address */ \
157 arm_2d_helper_pfb_update_dependency((__CB_ADDR), \
158 ARM_2D_PFB_DEPEND_ON_DRAWING, \
159 (arm_2d_helper_pfb_dependency_t []) {{ \
160 .evtOnDrawing = { \
161 .fnHandler = (__HANDLER), \
162 .pTarget = (NULL,##__VA_ARGS__),\
163 }, \
164 }})
165
166
167#define __IMPL_ARM_2D_REGION_LIST(__NAME, ...) \
168 enum { \
169 __NAME##_offset = __COUNTER__, \
170 }; \
171 __VA_ARGS__ \
172 arm_2d_region_list_item_t __NAME[] = {
173
174
175#define IMPL_ARM_2D_REGION_LIST(__NAME, ...) \
176 __IMPL_ARM_2D_REGION_LIST(__NAME,##__VA_ARGS__)
177
178
179#define END_IMPL_ARM_2D_REGION_LIST(...) \
180 };
181
182#define __ADD_REGION_TO_LIST(__NAME, ...) \
183 { \
184 .ptNext = (arm_2d_region_list_item_t *) \
185 &(__NAME[__COUNTER__ - __NAME##_offset]), \
186 .tRegion = { \
187 __VA_ARGS__ \
188 }, \
189 }
190
191#define ADD_REGION_TO_LIST(__NAME, ...) \
192 __ADD_REGION_TO_LIST(__NAME, ##__VA_ARGS__)
193
194
195#define __ADD_LAST_REGION_TO_LIST(__NAME, ...) \
196 { \
197 .ptNext = NULL, \
198 .tRegion = { \
199 __VA_ARGS__ \
200 }, \
201 }
202
203#define ADD_LAST_REGION_TO_LIST(__NAME, ...) \
204 __ADD_LAST_REGION_TO_LIST(__NAME, ##__VA_ARGS__)
205
206
207#define IMPL_PFB_ON_DRAW(__NAME) IMPL_ON_DRAW_EVT(__NAME)
208
209
210#define IMPL_PFB_ON_LOW_LV_RENDERING(__NAME) \
211 void __NAME(void *pTarget, \
212 const arm_2d_pfb_t *ptPFB, \
213 bool bIsNewFrame)
214
215
216#define IMPL_PFB_ON_FRAME_SYNC_UP(__NAME) \
217 bool __NAME(void *pTarget)
218
219
220/*!
221 * \brief a macro wrapper in lowercase to help initialising PFB service
222 * \param[in] __CB_ADDR the address of the arm_2d_helper_pfb_t object
223 * \param[in] __SCREEN_WIDTH the width of the screen
224 * \param[in] __SCREEN_HEIGHT the hight of the screen
225 * \param[in] __PIXEL_TYPE the integer type of the pixel, i.e. uint8_t, uint16_t,
226 * uint32_t
227 * \param[in] __WIDTH the width of the PFB block
228 * \param[in] __HEIGHT the height of the PFB block
229 * \note For the same number of pixels in a PFB block, please priority the width
230 * over height, for example, 240 * 1 is better than 30 * 8
231 * \param[in] __PFB_NUM the number of PFB blocks in the built-in PFB pool.
232 * \param[in] ... a code block to add additional initializer, see example below:
233 * \return arm_2d_err_t the result of the initialisation process
234 *
235 * \code {.c}
236
237 static ARM_NOINIT arm_2d_helper_pfb_t s_tExamplePFB;
238 ...
239 // initialise FPB helper
240 if (init_arm_2d_helper_pfb(
241 &s_tExamplePFB, // FPB Helper object
242 __GLCD_CFG_SCEEN_WIDTH__, // screen width
243 __GLCD_CFG_SCEEN_HEIGHT__, // screen height
244 uint16_t, // colour date type
245 240, // PFB block width
246 1, // PFB block height
247 1, // number of PFB in the PFB pool
248 {
249 .evtOnLowLevelRendering = {
250 // callback for low level rendering
251 .fnHandler = &__pfb_render_handler,
252 },
253 .evtOnDrawing = {
254 // callback for drawing GUI
255 .fnHandler = &__pfb_draw_background_handler,
256 },
257 },
258 //.FrameBuffer.bSwapRGB16 = true,
259 ) < 0) {
260 //! error detected
261 assert(false);
262 }
263 * \endcode
264 *
265 */
266#define init_arm_2d_helper_pfb( __CB_ADDR, \
267 __SCREEN_WIDTH, \
268 __SCREEN_HEIGHT, \
269 __PIXEL_TYPE, \
270 __WIDTH, \
271 __HEIGHT, \
272 __PFB_NUM, \
273 ... \
274 ) \
275 ARM_2D_HELPER_PFB_INIT( \
276 __CB_ADDR, \
277 __SCREEN_WIDTH, \
278 __SCREEN_HEIGHT, \
279 __PIXEL_TYPE, \
280 __WIDTH, \
281 __HEIGHT, \
282 __PFB_NUM, \
283 ##__VA_ARGS__ \
284 )
285
286/*!
287 * \brief a macro wrapper to update the evtOnDrawring event handler
288 * \param[in] __CB_ADDR the address of the arm_2d_helper_pfb_t object
289 * \param[in] __HANDLER the new handler
290 * \param[in] ... [Optional] an address (of user defined structure) passed to the
291 * event handler.
292 * \return arm_2d_err_t the process result
293 */
294#define update_arm_2d_helper_pfb_on_draw_handler( \
295 __CB_ADDR, /* PFB Helper object address */ \
296 __HANDLER, /* new on-draw-handler function*/\
297 ...) /* An optional target address */ \
298 ARM_2D_HELPER_PFB_UPDATE_ON_DRAW_HANDLER( \
299 (__CB_ADDR), \
300 (__HANDLER),##__VA_ARGRS__)
301
302/*!
303 * \brief tell PFB helper that a low level LCD flushing work is complete
304 * \note This function is THREAD-SAFE, You can call this function asynchronously,
305 * e.g.
306 * - A ISR to indicate DMA-transfer complete event or
307 * - A different Thread
308 * \param[in] ptThis the PFB control block
309 * \param[in] ... the used PFB block.
310 * \note please do not use this parameter, it is only kept for backward
311 * compatability.
312 */
313#define arm_2d_helper_pfb_report_rendering_complete(__PFB_HELPER_PTR,...) \
314 __arm_2d_helper_pfb_report_rendering_complete((__PFB_HELPER_PTR), \
315 (NULL,##__VA_ARGS__))
316
317
318#define impl_arm_2d_region_list(__NAME, ...) \
319 IMPL_ARM_2D_REGION_LIST(__NAME,##__VA_ARGS__)
320#define add_region_to_list(__NAME, ...) \
321 ADD_REGION_TO_LIST(__NAME, ##__VA_ARGS__)
322#define add_last_region_to_list(__NAME, ...) \
323 ADD_LAST_REGION_TO_LIST(__NAME, ##__VA_ARGS__)
324#define end_impl_arm_2d_region_list(...) \
325 END_IMPL_ARM_2D_REGION_LIST(__VA_ARGS__)
326
327#define impl_pfb_on_draw(__NAME) IMPL_PFB_ON_DRAW(__NAME)
328#define impl_pfb_on_low_lv_rendering(__NAME) \
329 IMPL_PFB_ON_LOW_LV_RENDERING(__NAME)
330
331/*============================ TYPES =========================================*/
332
334/*!
335 * \brief the header of a PFB block
336 */
337typedef struct arm_2d_pfb_t {
338 struct arm_2d_pfb_t *ptNext; //!< next pfb block
339 arm_2d_helper_pfb_t *ptPFBHelper; //!< the pfb helper service current PFB block comes from
340 arm_2d_tile_t tTile; //!< descriptor
341 uint32_t u24Size : 24;
342 uint32_t : 7;
343 uint32_t bIsNewFrame : 1; //!< a flag to indicate the starting of a frame
345
346/*!
347 * \brief the node of a region list
348 *
349 */
351 struct arm_2d_region_list_item_t *ptNext; //!< the next node
352 arm_2d_region_t tRegion; //!< the region
353 bool bIgnore; //!< ignore this region
355
356/*!
357 * \brief the On Low Level Rendering event handler for the low level (LCD Driver)
358 *
359 * \param[in] pTarget a user attached target address
360 * \param[in] ptPFB the PFB block
361 * \param[in] bIsNewFrame a flag indicate the starting of a new frame
362 */
364 void *pTarget,
365 const arm_2d_pfb_t *ptPFB,
366 bool bIsNewFrame);
367
368/*!
369 * \brief on low level render event
370 */
372 arm_2d_helper_render_handler_t *fnHandler; //!< event handler function
373 void *pTarget; //!< user attached target
375
376
377
378/*!
379 * \brief the enumeration for events
380 *
381 */
382enum {
383 ARM_2D_PFB_DEPEND_ON_LOW_LEVEL_RENDERING = _BV(0), //!< On Low Level Rendering Event
384 ARM_2D_PFB_DEPEND_ON_DRAWING = _BV(1), //!< On Drawing Event
385 ARM_2D_PFB_DEPEND_ON_LOW_LEVEL_SYNC_UP = _BV(2), //!< On Low Level Sync-up Event
386 ARM_2D_PFB_DEPEND_ON_FRAME_SYNC_UP = _BV(3), //!< On Frame Sync-up Event
387 ARM_2D_PFB_DEPEND_ON_EACH_FRAME_CPL = _BV(4), //!< On Each Frame Complete Event
388 ARM_2D_PFB_DEPEND_ON_NAVIGATION = _BV(5), //!< On Drawing Navigation Event
389};
390
391/*!
392 * \brief The PFB Helper Service Dependency
393 *
394 */
396 //! event handler for low level rendering
398
399 //! event handler for drawing GUI
400 arm_2d_helper_draw_evt_t evtOnDrawing;
401
402 //! low level rendering handler wants to sync-up (return arm_fsm_rt_wait_for_obj)
404
405 //! event handler for each frame complete
407
408 //! event handler for drawing GUI
409 struct {
410 arm_2d_helper_draw_evt_t evtOnDrawing;
411 arm_2d_region_list_item_t *ptDirtyRegion;
413
415
416/*!
417 * \brief PFB Helper configuration
418 *
419 */
421
422 arm_2d_region_t tDisplayArea; //!< screen description
423
424 struct {
425 arm_2d_pfb_t *ptPFBs; //!< current PFB block
426 arm_2d_size_t tFrameSize; //!< the size of the frame
427 uint32_t wBufferSize; //!< the buffer size
428 uint16_t hwPFBNum; //!< the number of PFB
429 uint16_t bDoNOTUpdateDefaultFrameBuffer : 1; //!< A flag to disable automatically default-framebuffer-registration
430 uint16_t bDisableDynamicFPBSize : 1; //!< A flag to disable resize of the PFB block
431 uint16_t bSwapRGB16 : 1; //!< A flag to enable swapping high and low bytes of an RGB16 pixel
432 uint16_t bDebugDirtyRegions : 1; //!< A flag to show dirty regions on screen for debug
433 uint16_t : 2;
434 uint16_t u3PixelWidthAlign : 3; //!< Pixel alignment in Width for dirty region (2^n)
435 uint16_t u3PixelHeightAlign : 3; //!< Pixel alignment in Height for dirty region (2^n)
436 uint16_t u4PoolReserve : 4; //!< reserve specific number of PFB for other helper services
437
438 } FrameBuffer; //!< frame buffer context
439
440 arm_2d_helper_pfb_dependency_t Dependency; //!< user registered dependency
441
443
444/*!
445 * \brief the PFB helper control block
446 *
447 */
449
450ARM_PRIVATE(
451 arm_2d_helper_pfb_cfg_t tCFG; //!< user configuration
452
453 struct {
454 arm_2d_region_t tDrawRegion;
455 arm_2d_region_t tTargetRegion;
456 arm_2d_region_list_item_t *ptDirtyRegion;
457
458 arm_2d_tile_t tPFBTile;
459 arm_2d_size_t tFrameSize;
460 uint32_t wPFBPixelCount;
461 bool bFirstIteration;
462 bool bIsRegionChanged;
463 uint8_t chPT;
464 struct {
465 uint8_t bIsNewFrame : 1;
466 uint8_t bIsFlushRequested :1;
467 uint8_t bNoAdditionalDirtyRegionList;
468 };
469 uint16_t hwFreePFBCount;
470 arm_2d_pfb_t *ptCurrent;
471 arm_2d_pfb_t *ptFreeList;
472 arm_2d_pfb_t *ptFlushing;
473 struct {
474 arm_2d_pfb_t *ptHead;
475 arm_2d_pfb_t *ptTail;
476 }FlushFIFO;
477 arm_2d_tile_t *ptFrameBuffer;
478 } Adapter;
480
481 struct {
482 int64_t lTimestamp; //!< PLEASE DO NOT USE
483 int32_t nTotalCycle; //!< cycles used by drawing
484 int32_t nRenderingCycle; //!< cycles used in LCD flushing
485 } Statistics; //!< performance statistics
486
487};
488
489/*!
490 * \brief the Transform helper control block
491 *
492 */
493typedef struct {
494
495 float fAngle;
496 float fScale;
497
498ARM_PRIVATE(
499 arm_2d_region_list_item_t tDirtyRegions[2];
500 arm_2d_op_t *ptTransformOP;
501
502
503 struct {
504 float fValue;
505 float fStep;
506 } Angle;
507
508 struct {
509 float fValue;
510 float fStep;
511 } Scale;
512
513 bool bNeedUpdate;
515
517
518/*============================ GLOBAL VARIABLES ==============================*/
519/*============================ LOCAL VARIABLES ===============================*/
520/*============================ PROTOTYPES ====================================*/
521
522/*!
523 * \brief initialize pfb helper service
524 * \param[in] ptThis the pfb helper control block
525 * \param[in] ptCFG the configuration
526 * \return arm_2d_err_t the process result
527 */
528extern
529ARM_NONNULL(1,2)
532
533/*!
534 * \brief get the display (screen) region
535 * \param[in] ptThis the pfb helper control block
536 * \return arm_2d_region_t the screen region
537 */
538extern
539ARM_NONNULL(1)
541
542/*!
543 * \brief the task function for pfb helper
544 * \param[in] ptThis an initialised PFB control block
545 * \param[in] ptDirtyRegions a region list pending for refresh, NULL means
546 * refreshing the whole screen
547 * \retval arm_fsm_rt_cpl complete refreshing one frame
548 * \retval arm_fsm_rt_on_going the refreshing work is on-going
549 * \retval arm_fsm_rt_wait_for_obj user's OnDrawing event handler wants to wait
550 * for some objects, e.g. semaphore etc.
551 * \retval <0 An error is detected
552 */
553extern
554ARM_NONNULL(1)
556 arm_2d_region_list_item_t *ptDirtyRegions);
557
558/*!
559 * \brief flush the FPB FIFO
560 * \note This function is THREAD-SAFE
561 * \note For normal usage, please DO NOT use this function unless you know what
562 * you are doing.
563 * \param[in] ptThis an initialised PFB control block
564 */
565extern
566ARM_NONNULL(1)
568
569/*!
570 * \brief update PFB dependency (event handlers)
571 * \param[in] ptThis the PFB control block
572 * \param[in] chMask the bit mask for event handlers
573 * \param[in] ptDependency the new dependency description
574 * \return arm_2d_err_t the process result
575 */
576extern
577ARM_NONNULL(1,3)
579 arm_2d_helper_pfb_t *ptThis,
580 uint_fast8_t chMask,
581 const arm_2d_helper_pfb_dependency_t *ptDependency);
582
583/*!
584 * \brief tell PFB helper that a low level LCD flushing work is complete
585 * \note This function is THREAD-SAFE, You can call this function asynchronously,
586 * e.g.
587 * - A ISR to indicate DMA-transfer complete event or
588 * - A different Thread
589 * \param[in] ptThis the PFB control block
590 * \param[in] ptPFB the used PFB block
591 */
592extern
593ARM_NONNULL(1)
595 arm_2d_pfb_t *ptPFB);
596
597/*!
598 * \brief swap the high and low bytes for each rgb16 pixel
599 *
600 * \param[in] phwBuffer the pixel buffer
601 * \note the phwBuffer MUST aligned to half-word addresses
602 *
603 * \param[in] wSize the number of pixels
604 */
605extern
606void arm_2d_helper_swap_rgb16(uint16_t *phwBuffer, uint32_t wCount);
607
608/*!
609 * \brief try to get a PFB block from the pool
610 * \param[in] ptThis the PFB control block
611 * \retval NULL the pool is empty
612 * \retval !NULL a valid pfb block
613 */
614extern
615ARM_NONNULL(1)
617
618/*!
619 * \brief free a PFB block to the pool
620 * \param[in] ptThis the PFB control block
621 * \param[in] ptPFB the target PFB block
622 */
623extern
624ARM_NONNULL(1)
626
627/*!
628 * \brief initialize a given transform helper
629 * \param[in] ptThis the target helper
630 * \param[in] ptTransformOP the target transform OP, NULL is not accepted.
631 * \param[in] fAngleStep the minimal acceptable angle change.
632 * \param[in] fScaleStep the minimal acceptable scale ratio change.
633 * \param[in] ppDirtyRegionList the address of the dirty region list
634 */
635extern
636ARM_NONNULL(1,2,5)
638 arm_2d_op_t *ptTransformOP,
639 float fAngleStep,
640 float fScaleStep,
641 arm_2d_region_list_item_t **ppDirtyRegionList);
642
643/*!
644 * \brief the on-frame-begin event handler for a given transform helper
645 * \param[in] ptThis the target helper
646 * \note Usually this event handler should be insert the frame start event
647 * handler of a target scene.
648 */
649extern
650ARM_NONNULL(1)
652
653/*!
654 * \brief update a given transform helper with new values
655 * \param[in] ptThis the target helper
656 * \param[in] fAngle the new angle value
657 * \param[in] fScale the new scale ratio
658 * \note The new value is only accepted when the change between the old value
659 * and the new value is larger than the minimal acceptable mount.
660 */
661extern
662ARM_NONNULL(1)
664 float fAngle,
665 float fScale);
666
667/*!
668 * \brief update the dirty region after a transform operation
669 * \param[in] ptThis the target helper
670 * \param[in] bIsNewFrame whether this is a new frame
671 */
672extern
673ARM_NONNULL(1)
676 bool bIsNewFrame);
677
678/*! @} */
679
680#if defined(__clang__)
681# pragma clang diagnostic pop
682#endif
683
684#ifdef __cplusplus
685}
686#endif
687
688
689
690#endif