GCC Code Coverage Report


Directory: ./
File: framebuffer/FrameBuffer.h
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 9 14 64.3%
Functions: 9 14 64.3%
Branches: 1 2 50.0%

Line Branch Exec Source
1 #pragma once
2 /*
3 * This file is part of the Falcon Player (FPP) and is Copyright (C)
4 * 2013-2022 by the Falcon Player Developers.
5 *
6 * The Falcon Player (FPP) is free software, and is covered under
7 * multiple Open Source licenses. Please see the included 'LICENSES'
8 * file for descriptions of what files are covered by each license.
9 *
10 * This source file is covered under the LGPL v2.1 as described in the
11 * included LICENSE.LGPL file.
12 */
13
14 #include <atomic>
15 #include "fpp-json-fwd.h"
16 #include <condition_variable>
17 #include <list>
18 #include <mutex>
19 #include <thread>
20
21 #include "../config.h"
22
23 typedef enum {
24 IT_Random = -2,
25 IT_Default = -1,
26 IT_Normal = 0,
27 IT_SlideUp, // Slide up from the bottom to the top
28 IT_SlideDown, // Slide down from the top to the bottom
29 IT_SlideLeft, // Slide in from the right to the left
30 IT_SlideRight, // Slide in from the left to the right
31 IT_WipeUp, // Draw new over old by row starting from the bottom
32 IT_WipeDown, // Draw new over old by row starting from the top
33 IT_WipeLeft, // Draw new over old by column starting from the left
34 IT_WipeRight, // Draw new over old by column starting from the right
35 IT_WipeToHCenter, // Wipe down/up to center
36 IT_WipeFromHCenter, // Wipe up/down from center
37 IT_HorzBlindsOpen, // Horizontal blinds opening \__Very little difference in these two
38 IT_HorzBlindsClose, // Horizontal blinds closing /
39 IT_Mosaic, // Fill in small random squares until totally done
40 IT_MAX, // PLACEHOLDER - ALL ITEMS ABOVE THIS LINE ARE ENABLED, BELOW ARE DISABLED
41 IT_Wipe45, // Wipe at 45 degree angle to upper right corner
42 IT_Wipe135, // Wipe at 135 degree angle to bottom right corner
43 IT_Wipe225, // Wipe at 225 degree angle to bottom left corner
44 IT_Wipe315, // Wipe at 315 degree angle to top left corner
45 IT_WipeToVCenter, // Wipe from left/right to vert line going down center
46 IT_WipeFromVCenter, // Wipe to left/right from vert line going down center
47 IT_WipeInRect, // Wipe from outside corners inward in rect shape
48 IT_WipeOutRect, // Wipe to outside corners outward in rect shape
49 IT_WipeInCorners, // Wipe 4 rectangles from corners growing inward (4 growing rectangles in corners, shrinking + sign)
50 IT_WipeOutCorners, // Wipe 4 rectangles from center shrinking outward (creates a growing + sign in middle of screen)
51 IT_WipeCW, // CW wipe from 0 degrees top dead center
52 IT_WipeCCW, // CCW wipe from 0 degrees top dead center
53 IT_WipeQuadCW, // Quad CW wipe from 0 degrees top dead center
54 IT_WipeQuadCCW, // Quad CCW wipe from 0 degrees top dead center
55 IT_WipeOctoCW, // Octo CW wipe from 0 degrees top dead center
56 IT_WipeOctoCCW, // Octo CCW wipe from 0 degrees top dead center
57 IT_VertBlindsOpen, // Vertical blinds (8 bands?)
58 IT_VertBlindsClose, // Vertical blinds (8 bands?)
59 IT_Fade, // Fade from one image to the other (copy original and slowly fade each pixel)
60 IT_END
61 } ImageTransitionType;
62
63 class FrameBuffer {
64 protected:
65 FrameBuffer();
66
67 public:
68 static FrameBuffer* createFrameBuffer(const Json::Value& config);
69
70 virtual ~FrameBuffer();
71
72 virtual int InitializeFrameBuffer() = 0;
73 virtual void DestroyFrameBuffer();
74 void ClipToPage();
75 virtual void SyncLoop() = 0;
76 virtual void SyncDisplay(bool pageChanged = false) = 0;
77 virtual void FBCopyData(const uint8_t* buffer, int draw = 0);
78
79 48 virtual void EnableDisplay() {}
80 ✗ virtual void DisableDisplay() {}
81
82 // Variable-refresh support (KMS/DPI only). A frame buffer that manages its
83 // own display timing can change the output refresh rate at runtime by
84 // adjusting the vertical blanking while keeping the pixel clock and
85 // resolution fixed. The base class does not support this.
86 ✗ virtual bool SupportsVariableRefresh() { return false; }
87 // Highest refresh rate (fps) achievable at the current resolution, or 0 when
88 // variable refresh is not supported.
89 ✗ virtual int GetMaxRefreshRate() { return 0; }
90 // Set the output refresh rate (fps). Returns true when applied (or already
91 // at that rate); a no-op returning false when unsupported.
92 ✗ virtual bool SetRefreshRate(int fps) { return false; }
93 // Block until the page returned by Page() is no longer being scanned out,
94 // i.e. the previous SyncDisplay() flip has retired. Lets a caller that
95 // prepares frames elsewhere copy into the free page without tearing. The
96 // base class has no flip tracking, so this is a no-op.
97 ✗ virtual void WaitForPageFree() {}
98
99 void FBStartDraw(ImageTransitionType transitionType = IT_Default);
100
101 virtual void Dump(void);
102 void DrawLoop(void);
103
104 39 int Width() { return m_width; }
105 13 int Height() { return m_height; }
106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 818 times.
818 int Page(bool producer = false) { return producer ? m_pPage : m_cPage; }
107 117 int PageCount() { return m_pages; }
108 39 int PageSize() { return m_pageSize; }
109 844 int RowStride() { return m_rowStride; }
110 831 int RowPadding() { return m_rowPadding; }
111 uint8_t* BufferPage(int page = 0);
112 int BitsPerPixel() { return m_bpp; }
113 1032126 int BytesPerPixel() { return m_bpp / 8; }
114 bool IsDirty(int page) { return (m_dirtyPages[page] == 1); }
115 void SetDirty(int page, uint8_t dirty = 1) { m_dirtyPages[page] = dirty; }
116
117 void ClearAllPages();
118 void Clear();
119
120 void NextPage(bool producer = false);
121
122 protected:
123 // Stops and joins the draw/sync loop thread. MUST be the first statement of
124 // every subclass destructor: the loop calls virtuals (SyncDisplay()) and
125 // writes into buffers the subclass destructor tears down, so it has to be
126 // dead before any derived state goes away. ~FrameBuffer() runs last, so
127 // joining only there leaves the thread running across the whole derived
128 // teardown. Idempotent - safe to call again from ~FrameBuffer().
129 void StopDrawLoop(void);
130
131 int FBInit(const Json::Value& config);
132 void FBDrawNormal(void);
133 void FBDrawSlideUp(void);
134 void FBDrawSlideDown(void);
135 void FBDrawSlideLeft(void);
136 void FBDrawSlideRight(void);
137 void FBDrawWipeUp(void);
138 void FBDrawWipeDown(void);
139 void FBDrawWipeLeft(void);
140 void FBDrawWipeRight(void);
141 void FBDrawWipeToHCenter(void);
142 void FBDrawWipeFromHCenter(void);
143 void FBDrawHorzBlindsOpen(void);
144 void FBDrawHorzBlindsClose(void);
145 void FBDrawMosaic(void);
146
147 // Helpers
148 void DrawSquare(int dx, int dy, int w, int h, int sx = -1, int sy = -1);
149 void CopyRows(int y, int rows);
150
151 std::string m_name;
152 std::string m_device;
153 int m_fbFd = -1;
154 int m_xOffset = -1;
155 int m_yOffset = -1;
156 int m_width = 0;
157 int m_height = 0;
158 int m_bpp = -1;
159 bool m_variableRefresh = false;
160 uint8_t* m_buffer = nullptr;
161 uint8_t* m_outputBuffer = nullptr;
162 // FBCopyData() writes the next image here, under m_stagingLock, and
163 // DrawLoop() copies it to m_outputBuffer before drawing: a transition
164 // reads m_outputBuffer for most of a second, and the next image must not
165 // be written into it meanwhile. m_bufferLock can't guard that, since a
166 // transition holds it throughout and the output thread can't wait.
167 uint8_t* m_stagingBuffer = nullptr;
168 std::mutex m_stagingLock;
169 // m_pages must always be in [1, MAX_PAGES] and m_cPage/m_pPage must always
170 // be valid indices into m_pageBuffers, i.e. < m_pages. Everything that
171 // dereferences a page goes through m_pageBuffers[m_cPage], so a page index
172 // pointing past what InitializeFrameBuffer() actually assigned reads a null
173 // pointer and faults on the first write.
174 static constexpr int MAX_PAGES = 3;
175 uint8_t* m_pageBuffers[MAX_PAGES] = { nullptr, nullptr, nullptr };
176 int m_pageSize = 0;
177
178 int m_pixelSize = 0;
179 int m_pixelsWide = 0;
180 int m_pixelsHigh = 0;
181
182 // The part of the m_pixelsWide x m_pixelsHigh source image that fits in a
183 // page once scaled by m_pixelSize. The copy loops write only this much and
184 // still step through the source at m_pixelsWide per row. A model larger
185 // than the device it lands on - a 1920x1080 model on a framebuffer that is
186 // only 768 rows tall - otherwise wrote past the end of the page mapping, or
187 // past m_outputBuffer, which is malloc(m_pageSize). Set by ClipToPage().
188 int m_drawCols = 0;
189 int m_drawRows = 0;
190 std::string m_clipWarning;
191
192 ImageTransitionType m_transitionType = IT_Normal;
193 std::atomic<ImageTransitionType> m_nextTransitionType{ IT_Normal };
194 int m_transitionTime = 800;
195
196 unsigned int m_typeSeed;
197
198 volatile bool m_runLoop = false;
199 // Set by FBCopyData() when a new image is in m_outputBuffer; taken by
200 // DrawLoop() before it draws, so one that arrives mid-draw is drawn next.
201 std::atomic<bool> m_imageReady{ false };
202 bool m_displayEnabledOnce = false; // Track if EnableDisplay() has been called
203
204 std::thread* m_drawThread = nullptr;
205 bool m_autoSync = false;
206 volatile uint8_t* m_dirtyPages = nullptr;
207
208 std::mutex m_bufferLock;
209 std::mutex m_drawLock;
210
211 std::condition_variable m_drawSignal;
212
213 int m_cPage = 0;
214 int m_pPage = 0;
215 int m_pages = 2;
216 int m_bufferSize = 0;
217 int m_rowStride = 0;
218 int m_rowPadding = 0;
219 };
220