Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohossurface.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <render/qohossurface.h>
5
6#include <QtCore/qscopeguard.h>
7#include <QtGui/qimage.h>
8#include <native_window/external_window.h>
9#include <native_buffer/native_buffer.h>
10#include <native_window/buffer_handle.h>
11#include <poll.h>
12#include <qohosplugincore.h>
13#include <qohosutils.h>
14#include <sys/mman.h>
15#include <unistd.h>
16
17#if QT_CONFIG(vulkan)
18#include <QtGui/qvulkaninstance.h>
19#endif
20
21namespace ch = std::chrono;
22
23QT_BEGIN_NAMESPACE
24
25namespace
26{
27
28constexpr std::int32_t ohNativeWindowErrorCodeSuccess = 0;
29
30void setupNativeWindowBufferUsage(::OHNativeWindow *nativeWindow, std::uint64_t usageSetBits)
31{
32 std::uint64_t windowBufferUsage = 0;
33 auto getUsageRes = ::OH_NativeWindow_NativeWindowHandleOpt(
34 nativeWindow, ::NativeWindowOperation::GET_USAGE, &windowBufferUsage);
35 if (Q_UNLIKELY(getUsageRes != ohNativeWindowErrorCodeSuccess))
36 qOhosReportFatalErrorAndAbort("QOhosNativeXComponent: error reading window buffer usage: %d", getUsageRes);
37
38 std::uint64_t requestedWindowBufferUsage = windowBufferUsage | usageSetBits;
39 if (requestedWindowBufferUsage != windowBufferUsage) {
40 auto setUsageRes = ::OH_NativeWindow_NativeWindowHandleOpt(
41 nativeWindow, ::NativeWindowOperation::SET_USAGE, requestedWindowBufferUsage);
42 if (Q_UNLIKELY(setUsageRes != ohNativeWindowErrorCodeSuccess))
43 qOhosReportFatalErrorAndAbort("QOhosNativeXComponent: error setting window buffer usage: %d", setUsageRes);
44 }
45}
46
47void setupNativeWindowBufferFormat(::OHNativeWindow *nativeWindow)
48{
49 auto setFormatRes = ::OH_NativeWindow_NativeWindowHandleOpt(
50 nativeWindow, ::NativeWindowOperation::SET_FORMAT, QOhosSurface::bufferFormat);
51 if (Q_UNLIKELY(setFormatRes != ohNativeWindowErrorCodeSuccess)) {
52 qOhosReportFatalErrorAndAbort(
53 "%s: QOhosNativeXComponent: error setting window buffer format: %d",
54 Q_FUNC_INFO, setFormatRes);
55 }
56}
57
58std::shared_ptr<int> makeFdAutoClosingWrapper()
59{
60 auto fileDescriptor = std::make_shared<int>(-1);
61 return std::shared_ptr<int>(
62 fileDescriptor.get(),
63 [fileDescriptor](auto) {
64 if (*fileDescriptor != -1)
65 std::ignore = ::close(*fileDescriptor);
66 });
67}
68
69bool tryMapBufferHandleMemory(::BufferHandle *bufferHandle, void *&outMemory)
70{
71 void *bufferMemory = ::mmap(
72 bufferHandle->virAddr, bufferHandle->size, PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0);
73
74 if (bufferMemory == MAP_FAILED) {
75 int mmapErrno = errno;
76 qCWarning(QtForOhos, "%s: mmap failed with error: %d", Q_FUNC_INFO, mmapErrno);
77 return false;
78 }
79
80 outMemory = bufferMemory;
81 return true;
82}
83
84bool waitForFdReadyForReadOrTimeout(int fd, const char *fdName, ch::milliseconds timeoutMs)
85{
86 std::array<::pollfd, 1> pollFileDescriptors = {{
87 {
88 .fd = fd,
89 .events = POLLIN,
90 .revents = {},
91 },
92 }};
93
94 int pollRetCode;
95 do {
96 pollRetCode = ::poll(pollFileDescriptors.data(), pollFileDescriptors.size(), timeoutMs.count());
97 } while (pollRetCode == -1 && (errno == EINTR || errno == EAGAIN));
98
99 auto pollErrno = errno;
100
101 if (pollRetCode == -1) {
102 qCWarning(
103 QtForOhos, "%s: polling '%s' file descriptor failed with error: %d",
104 Q_FUNC_INFO, fdName, pollErrno);
105 } else if (pollRetCode == 0) {
106 qCWarning(
107 QtForOhos, "%s: polling '%s' file descriptor timed out",
108 Q_FUNC_INFO, fdName);
109 }
110
111 return pollRetCode > 0;
112}
113
114}
115
116QImage::Format
117QOhosSurface::mapNativeBufferFormatToQImageFormatOrFail(std::int32_t format)
118{
119 static_assert(
120 Q_BYTE_ORDER == Q_LITTLE_ENDIAN,
121 "Pixel format mapping is currently supported for little-endian targets only");
122
123 QImage::Format result;
124
125 switch (format) {
126 case ::NATIVEBUFFER_PIXEL_FMT_RGB_888:
127 result = QImage::Format_RGB888;
128 break;
129 case ::NATIVEBUFFER_PIXEL_FMT_RGBA_8888:
130 result = QImage::Format_RGBA8888;
131 break;
132 case ::NATIVEBUFFER_PIXEL_FMT_BGRA_8888:
133 result = QImage::Format_ARGB32_Premultiplied;
134 break;
135 default:
136 qOhosReportFatalErrorAndAbort("%s: unsupported format %d", Q_FUNC_INFO, format);
137 }
138
139 return result;
140}
141
142std::optional<QSize> QOhosSurface::tryGetBufferGeometryForWindow(::OHNativeWindow *nativeWindow)
143{
144 std::int32_t surfaceWidth = 0;
145 std::int32_t surfaceHeight = 0;
146
147 // NOTE - The parameters height and width are reversed than what is usual order
148 // on purpose as required by the documentation
149 std::int32_t getWindowHandleErrorCode = ::OH_NativeWindow_NativeWindowHandleOpt(
150 nativeWindow, ::GET_BUFFER_GEOMETRY, &surfaceHeight, &surfaceWidth);
151
152 return getWindowHandleErrorCode == ohNativeWindowErrorCodeSuccess
153 ? std::optional<QSize>({surfaceWidth, surfaceHeight})
154 : std::nullopt;
155}
156
157QOhosSurface::QOhosSurface(::OHNativeWindow *nativeWindow)
159{
160 if (nativeWindow == nullptr)
161 qOhosReportFatalErrorAndAbort("NativeWindow cannot be null");
162
163 setupNativeWindowBufferUsage(
164 nativeWindow,
165 ::OH_NativeBuffer_Usage::NATIVEBUFFER_USAGE_CPU_READ);
166}
167
168::OHNativeWindow *QOhosSurface::nativeWindow() const
169{
170 return m_nativeWindow;
171}
172
173void QOhosSurface::setNativeWindowSurface(
174 ::OHNativeWindow *nativeWindow, const std::optional<QSize> &optSurfaceSize)
175{
176 if (nativeWindow == nullptr)
177 qOhosReportFatalErrorAndAbort("NativeWindow cannot be null");
178
179 m_nativeWindow = nativeWindow;
180
181#if QT_CONFIG(vulkan)
182 if (m_vulkanSurface)
183 m_vulkanSurface->setNativeWindowSurface(nativeWindow);
184#endif
185 setupNativeWindowBufferUsage(
186 nativeWindow,
187 ::OH_NativeBuffer_Usage::NATIVEBUFFER_USAGE_CPU_READ);
188
189 if (m_eglSurface) {
190 m_eglSurface->setNativeWindowSurface(
191 reinterpret_cast<::EGLNativeWindowType>(m_nativeWindow), optSurfaceSize);
192 }
193}
194
195EGLSurface QOhosSurface::tryGetOrCreateEGLWindowSurface(EGLDisplay display, EGLConfig config, bool swappingBuffers)
196{
197 if (!m_eglSurface) {
198 m_eglSurface = std::make_unique<QOhosEGLSurface>();
199 m_eglSurface->setNativeWindowSurface(
200 reinterpret_cast<::EGLNativeWindowType>(m_nativeWindow), {});
201 }
202 return m_eglSurface->tryGetOrCreateEGLWindowSurface(display, config, swappingBuffers, {});
203}
204
205std::optional<QSize> QOhosSurface::surfaceResolution() const
206{
207 return tryGetBufferGeometryForWindow(m_nativeWindow);
208}
209
210void QOhosSurface::paintOnNativeWindowSurface(
211 std::function<std::vector<::Region::Rect>(QImage &, ::BufferHandle *)> paintFunc,
212 std::function<void(::BufferHandle *)> onFlushSuccessFunc)
213{
214 ::OHNativeWindowBuffer *nativeWindowBuffer = nullptr;
215 auto fenceFileDescriptor = makeFdAutoClosingWrapper();
216
217 setupNativeWindowBufferFormat(m_nativeWindow);
218
219 auto requestBufferEc = ::OH_NativeWindow_NativeWindowRequestBuffer(
220 m_nativeWindow, &nativeWindowBuffer, fenceFileDescriptor.get());
221 if (requestBufferEc != ohNativeWindowErrorCodeSuccess) {
222 qCWarning(QtForOhos, "%s: NativeWindowRequestBuffer failed with error code: %d", Q_FUNC_INFO, requestBufferEc);
223 return;
224 }
225
226 auto nativeWindowAbortBufferGuard = qScopeGuard([nativeWindow = m_nativeWindow, nativeWindowBuffer](){
227 auto abortBufferEc = ::OH_NativeWindow_NativeWindowAbortBuffer(nativeWindow, nativeWindowBuffer);
228 if (abortBufferEc != ohNativeWindowErrorCodeSuccess)
229 qCWarning(QtForOhos, "%s: NativeWindowAbortBuffer failed with error code: %d", Q_FUNC_INFO, abortBufferEc);
230 });
231
232 ::BufferHandle *bufferHandle = ::OH_NativeWindow_GetBufferHandleFromNative(nativeWindowBuffer);
233 if (bufferHandle == nullptr) {
234 qCWarning(QtForOhos, "%s: GetBufferHandleFromNative returned null", Q_FUNC_INFO);
235 return;
236 }
237
238 QImage::Format dstImageFormat = QOhosSurface::mapNativeBufferFormatToQImageFormatOrFail(bufferHandle->format);
239 QSize dstImageSize{bufferHandle->width, bufferHandle->height};
240
241 void *bufferMemory = nullptr;
242 if (!tryMapBufferHandleMemory(bufferHandle, bufferMemory))
243 return;
244
245 auto unmapMemoryGuard = qScopeGuard([bufferMemory, bufferMemorySize = bufferHandle->size]() {
246 int result = ::munmap(bufferMemory, bufferMemorySize);
247 if (result != 0) {
248 int munmapErrno = errno;
249 qCWarning(QtForOhos, "%s: munmap failed with error: %d", Q_FUNC_INFO, munmapErrno);
250 }
251 });
252
253 if (*fenceFileDescriptor != -1) {
254 if (!waitForFdReadyForReadOrTimeout(*fenceFileDescriptor, "window buffer fence", ch::seconds(3)))
255 return;
256 }
257
258 QImage dstImage(
259 reinterpret_cast<uchar *>(bufferMemory),
260 dstImageSize.width(), dstImageSize.height(),
261 bufferHandle->stride,
262 dstImageFormat);
263
264 auto rects = paintFunc(dstImage, bufferHandle);
265
266 const std::int32_t acquireFenceFileDescriptor = -1;
267 auto flushBufferEc = ::OH_NativeWindow_NativeWindowFlushBuffer(
268 m_nativeWindow, nativeWindowBuffer, acquireFenceFileDescriptor,
269 ::Region{
270 .rects = rects.empty() ? nullptr : rects.data(),
271 .rectNumber = static_cast<std::int32_t>(rects.size()),
272 });
273 if (flushBufferEc != ohNativeWindowErrorCodeSuccess) {
274 qCWarning(QtForOhos, "%s: failed to flush buffer with error code: %d", Q_FUNC_INFO, flushBufferEc);
275 return;
276 }
277 onFlushSuccessFunc(bufferHandle);
278 nativeWindowAbortBufferGuard.dismiss();
279}
280
281void QOhosSurface::clearNativeWindowSurface()
282{
283 paintOnNativeWindowSurface(
284 [](QImage &dstImage, ::BufferHandle *) {
285 dstImage.fill(Qt::transparent);
286 return std::vector<::Region::Rect>();
287 },
288 [](::BufferHandle *) {});
289}
290
291void QOhosSurface::setExtraUsageBitsForNativeWindowBuffer(std::uint64_t usageSetBits)
292{
293 setupNativeWindowBufferUsage(m_nativeWindow, usageSetBits);
294}
295
296#if QT_CONFIG(vulkan)
297
298VkSurfaceKHR *QOhosSurface::tryGetOrCreateVulkanWindowSurface(QVulkanInstance *instance)
299{
300 if (!m_vulkanSurface) {
301 m_vulkanSurface = std::make_unique<QOhosVulkanSurface>();
302 m_vulkanSurface->setNativeWindowSurface(m_nativeWindow);
303 setExtraUsageBitsForNativeWindowBuffer(NATIVEBUFFER_USAGE_HW_RENDER | NATIVEBUFFER_USAGE_HW_TEXTURE);
304 }
305 auto createFn = reinterpret_cast<PFN_vkCreateSurfaceOHOS>(
306 instance->getInstanceProcAddr("vkCreateSurfaceOHOS"));
307 auto destroyFn = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
308 instance->getInstanceProcAddr("vkDestroySurfaceKHR"));
309 return m_vulkanSurface->tryGetOrCreateVulkanWindowSurface(instance->vkInstance(), createFn, destroyFn);
310}
311
312#endif // QT_CONFIG(vulkan)
313
314QT_END_NAMESPACE
void setExtraUsageBitsForNativeWindowBuffer(std::uint64_t usageSetBits)
::OHNativeWindow * nativeWindow() const
void paintOnNativeWindowSurface(std::function< std::vector<::Region::Rect >(QImage &, ::BufferHandle *)> paintFunc, std::function< void(::BufferHandle *)> onFlushSuccessFunc)
void setNativeWindowSurface(::OHNativeWindow *nativeWindow, const std::optional< QSize > &optSurfaceSize)
std::optional< QSize > surfaceResolution() const
QOhosSurface(::OHNativeWindow *nativeWindow)
void clearNativeWindowSurface()
void setupNativeWindowBufferFormat(::OHNativeWindow *nativeWindow)
std::shared_ptr< int > makeFdAutoClosingWrapper()
bool waitForFdReadyForReadOrTimeout(int fd, const char *fdName, ch::milliseconds timeoutMs)
void setupNativeWindowBufferUsage(::OHNativeWindow *nativeWindow, std::uint64_t usageSetBits)
bool tryMapBufferHandleMemory(::BufferHandle *bufferHandle, void *&outMemory)
#define MAP_FAILED