GCC Code Coverage Report


Directory: ./
File: framebuffer/IOCTLFrameBuffer.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 169 227 74.4%
Functions: 6 7 85.7%
Branches: 125 266 47.0%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the LGPL v2.1 as described in the
10 * included LICENSE.LGPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include "IOCTLFrameBuffer.h"
16
17 #ifdef HAS_IOCTL_FB
18
19 #include <sys/ioctl.h>
20 #include <sys/mman.h>
21 #include <sys/socket.h>
22 #include <sys/uio.h>
23 #include <cstring>
24 #include <ctime>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <mutex>
28 #include <stdlib.h>
29 #include <string>
30 #include <thread>
31 #include <unistd.h>
32
33 #include "Warnings.h"
34 #include "common.h"
35 #include "log.h"
36 #include "settings.h"
37 #include "commands/Commands.h"
38
39 #include "FrameBuffer.h"
40
41 /*
42 *
43 */
44 52 IOCTLFrameBuffer::IOCTLFrameBuffer() {
45
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
52 LogDebug(VB_CHANNELOUT, "IOCTLFrameBuffer::IOCTLFrameBuffer()\n");
46 52 }
47
48 /*
49 *
50 */
51 68 IOCTLFrameBuffer::~IOCTLFrameBuffer() {
52 // Stop the loop before tearing anything down - see FrameBuffer::StopDrawLoop().
53 34 StopDrawLoop();
54 // ~FrameBuffer() can only reach the base override, so the tty mode and the
55 // rgb565 map were never restored/freed on delete.
56 34 IOCTLFrameBuffer::DestroyFrameBuffer();
57 68 }
58
59 52 int IOCTLFrameBuffer::InitializeFrameBuffer() {
60
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
52 std::string devicePath = m_device;
61
62 // If device is a DRM connector name (e.g., "HDMI-A-1", "DSI-1"), map to the
63 // framebuffer device. DSI is included so DSI touchscreens (e.g. the official
64 // 7" display) resolve to /dev/fb0 rather than a non-existent /dev/DSI-1.
65
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
104 if (m_device.find("HDMI") != std::string::npos ||
66
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 1 times.
104 m_device.find("DPI") != std::string::npos ||
67
4/6
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 51 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 51 times.
156 m_device.find("DSI") != std::string::npos ||
68 51 m_device.find("DisplayPort") != std::string::npos) {
69 // Use /dev/fb0 (primary framebuffer console)
70
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 devicePath = "fb0";
71
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogDebug(VB_CHANNELOUT, "IOCTLFrameBuffer: Mapping DRM connector '%s' to '%s'\n",
72 m_device.c_str(), devicePath.c_str());
73 }
74
75
3/6
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
52 std::string devString = getSetting("framebufferControlSocketPath", "/dev") + "/" + devicePath;
76
77 // For fb0, disable console and set mode before opening
78
8/10
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 50 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 51 times.
52 if (devicePath == "fb0" && m_width > 0 && m_height > 0) {
79 // Disable console to allow mode change
80
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 int ttyFd = open("/dev/console", O_RDWR);
81
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ttyFd > 0) {
82 ✗ ioctl(ttyFd, KDSETMODE, KD_GRAPHICS);
83 ✗ close(ttyFd);
84 }
85
86 // DON'T change the framebuffer mode - keep it at native resolution
87 // This allows auto-scaling to work properly
88 // The code below will detect if we need to scale (e.g. 192x108 → 1920x1080)
89 }
90
91
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
52 m_fbFd = open(devString.c_str(), O_RDWR);
92
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 51 times.
52 if (m_fbFd < 0) {
93
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogErr(VB_CHANNELOUT, "Error opening FrameBuffer device: %s\n", devString.c_str());
94
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 WarningHolder::AddWarning(36, "Framebuffer output: could not open device " + devString);
95 1 return 0;
96 }
97
98
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
51 if (ioctl(m_fbFd, FBIOGET_VSCREENINFO, &m_vInfo)) {
99 ✗ LogErr(VB_CHANNELOUT, "Error getting FrameBuffer info for device: %s\n", devString.c_str());
100 ✗ close(m_fbFd);
101 ✗ return 0;
102 }
103
104
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 50 times.
51 if (m_bpp != -1) {
105 1 m_vInfo.bits_per_pixel = m_bpp;
106
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 12 times.
50 } else if (m_vInfo.bits_per_pixel == 32) {
107 38 m_vInfo.bits_per_pixel = 24;
108 }
109
110 51 m_bpp = m_vInfo.bits_per_pixel;
111
112
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
51 if ((m_bpp != 24) && (m_bpp != 16)) {
113 ✗ LogErr(VB_CHANNELOUT, "Do not know how to handle %d BPP\n", m_bpp);
114 ✗ WarningHolder::AddWarning(36, "Framebuffer output: unsupported color depth (" + std::to_string(m_bpp) + " bpp)");
115 ✗ close(m_fbFd);
116 ✗ return 0;
117 }
118
119
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 49 times.
51 if (m_bpp == 16) {
120
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, "Current Bitfield offset info:\n");
121
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, " R: %d (%d bits)\n", m_vInfo.red.offset, m_vInfo.red.length);
122
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, " G: %d (%d bits)\n", m_vInfo.green.offset, m_vInfo.green.length);
123
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, " B: %d (%d bits)\n", m_vInfo.blue.offset, m_vInfo.blue.length);
124
125 // RGB565
126 2 m_vInfo.red.offset = 11;
127 2 m_vInfo.red.length = 5;
128 2 m_vInfo.green.offset = 5;
129 2 m_vInfo.green.length = 6;
130 2 m_vInfo.blue.offset = 0;
131 2 m_vInfo.blue.length = 5;
132 2 m_vInfo.transp.offset = 0;
133 2 m_vInfo.transp.length = 0;
134
135
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, "New Bitfield offset info should be:\n");
136
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, " R: %d (%d bits)\n", m_vInfo.red.offset, m_vInfo.red.length);
137
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, " G: %d (%d bits)\n", m_vInfo.green.offset, m_vInfo.green.length);
138
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, " B: %d (%d bits)\n", m_vInfo.blue.offset, m_vInfo.blue.length);
139 }
140
141 // Save actual framebuffer dimensions before we potentially override them
142 51 int actualFbWidth = m_vInfo.xres;
143 51 int actualFbHeight = m_vInfo.yres;
144
145
3/4
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 50 times.
✗ Branch 3 not taken.
51 if (m_width && m_height) {
146 // DON'T change the actual resolution - keep framebuffer at native resolution
147 // Only set virtual resolution for panning/double buffering
148 // m_vInfo.xres = m_width; // REMOVED - don't change visible resolution
149 // m_vInfo.yres = m_height; // REMOVED - don't change visible resolution
150 } else {
151 1 m_width = m_vInfo.xres;
152 1 m_height = m_vInfo.yres;
153 }
154
155
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 LogDebug(VB_CHANNELOUT, "IOCTLFrameBuffer: Before FBIOPUT - xres: %d, yres: %d, xres_virtual: %d, yres_virtual: %d\n",
156 m_vInfo.xres, m_vInfo.yres, m_vInfo.xres_virtual, m_vInfo.yres_virtual);
157
158 51 m_vInfo.xres_virtual = actualFbWidth; // Use actual FB width for virtual
159 51 m_vInfo.yres_virtual = actualFbHeight * m_pages; // Use actual FB height for virtual
160 51 m_vInfo.yoffset = 0;
161
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
51 if (m_autoSync) {
163 // Drawing starts on the second page -- but only if there IS one. With
164 // a single page m_pageBuffers[1] is never assigned, so starting at page
165 // 1 leaves FBCopyData()'s `ob = m_pageBuffers[m_cPage]` null and the
166 // first pixel write faults.
167 // Producer gets Page(true) then NextPage(true) after checking if page is clean.
168 ✗ m_cPage = m_pages > 1 ? 1 : 0;
169 ✗ m_pPage = m_cPage;
170 }
171
172
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 LogDebug(VB_CHANNELOUT, "IOCTLFrameBuffer: Setting resolution to %dx%d (virtual: %dx%d)\n",
173 m_vInfo.xres, m_vInfo.yres, m_vInfo.xres_virtual, m_vInfo.yres_virtual);
174
175
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 50 times.
51 if (ioctl(m_fbFd, FBIOPUT_VSCREENINFO, &m_vInfo)) {
176 // Single page of the device's own height - m_height is the model's,
177 // which the framebuffer was deliberately not resized to.
178 1 m_vInfo.yres_virtual = actualFbHeight;
179 1 m_cPage = 0;
180 1 m_pPage = 0;
181 1 m_pages = 1;
182
183
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ioctl(m_fbFd, FBIOPUT_VSCREENINFO, &m_vInfo)) {
184
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogErr(VB_CHANNELOUT, "Error setting FrameBuffer info\n");
185
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 close(m_fbFd);
186 1 return 0;
187 } else {
188 ✗ LogWarn(VB_CHANNELOUT, "Unable to allocate enough memory for double buffering, using single buffered framebuffer\n");
189 }
190 }
191
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (ioctl(m_fbFd, FBIOGET_FSCREENINFO, &m_fInfo)) {
193 ✗ LogErr(VB_CHANNELOUT, "Error getting fixed FrameBuffer info\n");
194 ✗ close(m_fbFd);
195 ✗ return 0;
196 }
197
198
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
50 LogDebug(VB_CHANNELOUT, "IOCTLFrameBuffer: After FBIOPUT - line_length: %d, xres: %d, yres: %d, xres_virtual: %d, yres_virtual: %d, smem_len: %u\n",
199 m_fInfo.line_length, m_vInfo.xres, m_vInfo.yres, m_vInfo.xres_virtual, m_vInfo.yres_virtual, m_fInfo.smem_len);
200
201 // A successful FBIOPUT does not mean the pages were granted. A driver
202 // with no fb_check_var (fbtft SPI displays) returns success without
203 // applying the request, and its video memory holds a single page, so
204 // mapping and clearing m_pages pages runs off the end of it (SIGBUS).
205 // Take the page count from what the device reports it has.
206
2/4
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
✗ Branch 3 not taken.
50 if (m_vInfo.yres && m_fInfo.line_length) {
207 50 uint32_t pageBytes = m_vInfo.yres * m_fInfo.line_length;
208 50 int avail = std::min<uint32_t>(m_vInfo.yres_virtual / m_vInfo.yres, m_fInfo.smem_len / pageBytes);
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (avail < 1) {
210 ✗ LogErr(VB_CHANNELOUT, "FrameBuffer %s has %u bytes of video memory, less than one %ux%u page\n",
211 devString.c_str(), m_fInfo.smem_len, m_vInfo.xres, m_vInfo.yres);
212 ✗ close(m_fbFd);
213 ✗ return 0;
214 }
215
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 49 times.
50 if (avail < m_pages) {
216
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogWarn(VB_CHANNELOUT, "FrameBuffer %s only has room for %d of %d pages, using %d\n",
217 devString.c_str(), avail, m_pages, avail);
218 1 m_pages = avail;
219
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (m_cPage >= m_pages || m_pPage >= m_pages) {
220 ✗ m_cPage = 0;
221 ✗ m_pPage = 0;
222 }
223 }
224 }
225
226 50 m_bpp = m_vInfo.bits_per_pixel;
227
2/4
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 50 times.
50 if (devString == "/dev/fb0") {
228 // Hide the text console. Without one (a container, a headless
229 // service) there is nothing to hide, and the framebuffer works.
230 ✗ m_ttyFd = open("/dev/console", O_RDWR);
231 ✗ if (m_ttyFd >= 0) {
232 ✗ ioctl(m_ttyFd, KDSETMODE, KD_GRAPHICS);
233 ✗ close(m_ttyFd);
234 }
235 ✗ m_ttyFd = -1;
236 }
237
238 50 m_rowStride = m_fInfo.line_length;
239 50 m_rowPadding = m_rowStride - (m_width * m_bpp / 8);
240 50 m_pageSize = m_vInfo.yres * m_fInfo.line_length;
241 50 m_bufferSize = m_pageSize * m_pages;
242 50 m_buffer = (uint8_t*)mmap(0, m_bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_fbFd, 0);
243
244
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if ((char*)m_buffer == (char*)-1) {
245 ✗ m_buffer = nullptr;
246 ✗ LogErr(VB_CHANNELOUT, "Error, unable to map framebuffer device %s\n", devString.c_str());
247 ✗ WarningHolder::AddWarning(36, "Framebuffer output: could not map device " + devString);
248 ✗ DestroyFrameBuffer();
249 ✗ return 0;
250 }
251
252 50 memset(m_buffer, 0, m_bufferSize);
253 50 m_pageBuffers[0] = m_buffer;
254
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1 times.
50 if (m_pages >= 2) {
255 49 m_pageBuffers[1] = m_buffer + m_pageSize;
256 }
257
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 49 times.
50 if (m_pages >= 3) {
258 1 m_pageBuffers[2] = m_buffer + 2 * m_pageSize;
259 }
260
261 // Auto-detect pixel scaling if actual framebuffer is larger than requested
262
4/6
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 37 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 37 times.
✗ Branch 5 not taken.
50 if (m_pixelSize == 0 && m_width > 0 && m_height > 0) {
263 // Check if we have a resolution mismatch (framebuffer bigger than model)
264
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
37 if (actualFbWidth > m_width && actualFbHeight > m_height) {
265 2 int xScale = actualFbWidth / m_width;
266 2 int yScale = actualFbHeight / m_height;
267
268 // Use the smaller scale factor to ensure we fit
269
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (xScale == yScale && xScale > 1) {
270 2 m_pixelSize = xScale;
271
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogInfo(VB_CHANNELOUT, "IOCTLFrameBuffer: Auto-detected pixelSize=%d (fb: %dx%d, model: %dx%d)\n",
272 m_pixelSize, actualFbWidth, actualFbHeight, m_width, m_height);
273 }
274 }
275 }
276
277
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 48 times.
50 if (m_bpp == 16) {
278
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, "Generating RGB565Map for Bitfield offset info:\n");
279
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, " R: %d (%d bits)\n", m_vInfo.red.offset, m_vInfo.red.length);
280
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, " G: %d (%d bits)\n", m_vInfo.green.offset, m_vInfo.green.length);
281
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogExcess(VB_CHANNELOUT, " B: %d (%d bits)\n", m_vInfo.blue.offset, m_vInfo.blue.length);
282
283 2 int rShift = m_vInfo.red.offset - (8 + (8 - m_vInfo.red.length));
284 2 int gShift = m_vInfo.green.offset - (8 + (8 - m_vInfo.green.length));
285 2 int bShift = m_vInfo.blue.offset - (8 + (8 - m_vInfo.blue.length));
286
287 uint16_t o;
288
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 m_rgb565map = new uint16_t**[32];
289
290
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 2 times.
66 for (uint16_t b = 0; b < 32; b++) {
291
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 m_rgb565map[b] = new uint16_t*[64];
292
2/2
✓ Branch 0 taken 4096 times.
✓ Branch 1 taken 64 times.
4160 for (uint16_t g = 0; g < 64; g++) {
293
1/2
✓ Branch 0 taken 4096 times.
✗ Branch 1 not taken.
4096 m_rgb565map[b][g] = new uint16_t[32];
294
2/2
✓ Branch 0 taken 131072 times.
✓ Branch 1 taken 4096 times.
135168 for (uint16_t r = 0; r < 32; r++) {
295 131072 o = 0;
296
297
1/2
✓ Branch 0 taken 131072 times.
✗ Branch 1 not taken.
131072 if (rShift >= 0)
298 131072 o |= r >> rShift;
299 else
300 ✗ o |= r << abs(rShift);
301
302
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 131072 times.
131072 if (gShift >= 0)
303 ✗ o |= g >> gShift;
304 else
305 131072 o |= g << abs(gShift);
306
307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 131072 times.
131072 if (bShift >= 0)
308 ✗ o |= b >> bShift;
309 else
310 131072 o |= b << abs(bShift);
311
312 131072 m_rgb565map[b][g][r] = o;
313 }
314 }
315 }
316 }
317 50 return 1;
318 52 }
319
320 17 void IOCTLFrameBuffer::DestroyFrameBuffer(void) {
321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (m_ttyFd != -1) {
322 // Show the text console
323 ✗ ioctl(m_ttyFd, KDSETMODE, KD_TEXT);
324
325 // Shouldn't need this anymore.
326 ✗ close(m_ttyFd);
327 ✗ m_ttyFd = -1;
328 }
329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (m_rgb565map) {
330 ✗ for (int r = 0; r < 32; r++) {
331 ✗ for (int g = 0; g < 64; g++) {
332 ✗ delete[] m_rgb565map[r][g];
333 }
334
335 ✗ delete[] m_rgb565map[r];
336 }
337
338 ✗ delete[] m_rgb565map;
339 ✗ m_rgb565map = nullptr;
340 }
341 17 FrameBuffer::DestroyFrameBuffer();
342 17 }
343
344 /*
345 *
346 */
347 847 void IOCTLFrameBuffer::FBCopyData(const uint8_t* buffer, int draw) {
348
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 845 times.
847 if (m_bpp == 16) {
349 2 int sBpp = 3;
350 uint8_t* d;
351
352 2 const uint8_t* sR = buffer + 0; // Input always RGB
353 2 const uint8_t* sG = buffer + 1;
354 2 const uint8_t* sB = buffer + 2;
355 2 uint8_t* ob = m_stagingBuffer;
356 2 int drow = 0;
357
358 // Only a direct draw may write the page. Otherwise the frame goes to
359 // m_stagingBuffer, which DrawLoop() then draws (see FrameBuffer.h).
360
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 std::unique_lock<std::mutex> lock(draw ? m_bufferLock : m_stagingLock);
361
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (draw) {
362 ✗ ob = (m_pixelSize == 1) ? m_pageBuffers[m_cPage] : m_outputBuffer;
363 }
364
365 // Clipped to the page - see FrameBuffer::ClipToPage().
366 2 int skip = (m_pixelsWide - m_drawCols) * sBpp;
367
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 2 times.
66 for (int y = 0; y < m_drawRows; y++) {
368 64 d = ob + (drow * m_pixelSize * m_rowStride);
369
2/2
✓ Branch 0 taken 4096 times.
✓ Branch 1 taken 64 times.
4160 for (int x = 0; x < m_drawCols; x++) {
370
2/2
✓ Branch 0 taken 4096 times.
✓ Branch 1 taken 4096 times.
8192 for (int sc = 0; sc < m_pixelSize; sc++) {
371 4096 *((uint16_t*)d) = m_rgb565map[*sR >> 3][*sG >> 2][*sB >> 3];
372 4096 d += 2;
373 }
374
375 4096 sG += sBpp;
376 4096 sB += sBpp;
377 4096 sR += sBpp;
378 }
379 64 sG += skip;
380 64 sB += skip;
381 64 sR += skip;
382
383 64 d = ob + (drow * m_pixelSize * m_rowStride);
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 for (int sc = 1; sc < m_pixelSize; sc++) {
385 ✗ memcpy(d + (sc * m_rowStride), d, m_rowStride);
386 }
387
388 64 drow++;
389 }
390
391
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 lock.unlock();
392
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!draw) {
393 2 m_imageReady = true;
394 }
395 2 } else {
396 845 FrameBuffer::FBCopyData(buffer, draw);
397 }
398 847 }
399
400 ✗ void IOCTLFrameBuffer::SyncLoop() {
401 ✗ int dummy = 0;
402
403 ✗ if (m_pages == 1) {
404 // using /dev/fb* and only one page so writes are immediate, no need to sync
405 ✗ return;
406 }
407
408 ✗ while (m_runLoop) {
409 // Wait for vsync
410 ✗ dummy = 0;
411 // LogInfo(VB_CHANNELOUT, "%s - %d - waiting for vsync\n", m_device.c_str(), m_cPage);
412 ✗ ioctl(m_fbFd, FBIO_WAITFORVSYNC, &dummy);
413
414 // Allow the producer to catch up if needed
415 ✗ if (!m_dirtyPages[m_cPage] && m_dirtyPages[(m_cPage + 1) % m_pages])
416 ✗ NextPage();
417
418 ✗ if (m_dirtyPages[m_cPage]) {
419 // Pan the display to the page
420 // LogInfo(VB_CHANNELOUT, "%s - %d - flipping to cPage\n", m_device.c_str(), m_cPage);
421
422 ✗ m_vInfo.yoffset = m_vInfo.yres * m_cPage;
423 ✗ ioctl(m_fbFd, FBIOPAN_DISPLAY, &m_vInfo);
424
425 // LogInfo(VB_CHANNELOUT, "%s - %d - marking clear and bumping cPage\n", m_device.c_str(), m_cPage);
426 ✗ m_dirtyPages[m_cPage] = 0;
427 ✗ NextPage();
428 }
429 }
430 }
431
432 1688 void IOCTLFrameBuffer::SyncDisplay(bool pageChanged) {
433
1/2
✓ Branch 0 taken 1688 times.
✗ Branch 1 not taken.
1688 if (m_fbFd != -1) {
434
2/2
✓ Branch 0 taken 1143 times.
✓ Branch 1 taken 545 times.
1688 if (!pageChanged)
435 1143 return;
436
437
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 544 times.
545 if (m_pages == 1)
438 1 return;
439
440 // Wait for vsync
441 544 int dummy = 0;
442 // LogInfo(VB_CHANNELOUT, "%s - %d - SyncDisplay() waiting for vsync\n", m_device.c_str(), m_cPage);
443
444 // FBIO_WAITFORVSYNC causes DPIPixels on fb1 to not work when fb0 is in use for Virtual Matrix
445 // or Virtual Display. It shouldn't be needed since we are using page flipping.
446 // ioctl(m_fbFd, FBIO_WAITFORVSYNC, &dummy);
447
448 // Pan the display to the new page
449 // LogInfo(VB_CHANNELOUT, "%s - %d - SyncDisplay() flipping to cPage\n", m_device.c_str(), m_cPage);
450 544 m_vInfo.yoffset = m_vInfo.yres * m_cPage;
451 544 ioctl(m_fbFd, FBIOPAN_DISPLAY, &m_vInfo);
452 }
453 }
454
455 #endif
456