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