6#include "render/qohossurface.h"
7#include <native_buffer/native_buffer.h>
8#include <native_window/buffer_handle.h>
9#include <native_window/external_window.h>
10#include <qarkui/vsync.h>
11#include <qohosutils.h>
12#include <render/qohosview.h>
13#include <QtCore/private/qohoslogger_p.h>
14#include <QtCore/qendian.h>
30 std::int32_t bufferQueueSize;
31 auto getBufferQueueSizeResult = ::OH_NativeWindow_NativeWindowHandleOpt(
32 nativeWindow, ::NativeWindowOperation::GET_BUFFERQUEUE_SIZE, &bufferQueueSize);
34 qOhosReportFatalErrorAndAbort(
35 "%s: failed to get buffer queue size with error code: %d",
36 Q_FUNC_INFO, getBufferQueueSizeResult);
38 return bufferQueueSize;
43 const auto bitsPerPixel = image.depth();
44 const auto bytesPerPixel = bitsPerPixel / 8;
50 return QSpan(image.scanLine(y), image.width() * qImageBytesPerPixel(image));
55 const auto sizeToCopy =
std::min(srcRow.size(), dstRow.size());
56 std::memcpy(dstRow.data(), srcRow.data(), sizeToCopy);
60 QOhosPlatformBackingStore::QImageView srcImage, QImage &dstImage,
const QRegion ®ion,
const QPoint &dstOffset)
62 const auto intersectedRegion =
63 region.intersected(QRect({}, srcImage.size()))
64 .intersected(QRect({}, dstImage.size()).translated(-dstOffset));
66 for (
const auto &rect : intersectedRegion) {
67 const auto xSrc = rect.x() * srcImage.bytesPerPixel();
68 const auto widthSrc = rect.width() * srcImage.bytesPerPixel();
69 const auto xDst = (rect.x() + dstOffset.x()) * qImageBytesPerPixel(dstImage);
70 const auto widthDst = rect.width() * qImageBytesPerPixel(dstImage);
72 for (
int row = 0; row < rect.height(); ++row) {
73 const int srcY = rect.y() + row;
74 const int dstY = srcY + dstOffset.y();
75 const auto srcRow = srcImage.constScanLine(srcY).subspan(xSrc, widthSrc);
76 auto dstRow = qImageScanLine(dstImage, dstY).subspan(xDst, widthDst);
77 copyImageRow(srcRow, dstRow);
84 static const QColor debugBoxColor(
"#7F00FF00");
86 QPainter painter(&dstImage);
87 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
88 for (
const QRect &rect : region)
89 painter.fillRect(rect, debugBoxColor);
93 const QRegion ®ion,
const QPoint &rootWindowOffset,
const QSize &dstImageSize)
95 std::vector<::Region::Rect> rects;
96 if (!region.isEmpty()) {
98 region.begin(), region.end(),
std::back_inserter(rects),
99 [&](
const auto &qrect) {
100 return ::Region::Rect{
101 .x = qrect.x() + rootWindowOffset.x(),
102 .y = (dstImageSize.height() - qrect.y()) + rootWindowOffset.y() - qrect.height(),
103 .w =
static_cast<std::uint32_t>(qrect.width()),
104 .h =
static_cast<std::uint32_t>(qrect.height()),
112 QtOhos::QThreadSafeRef<QWindow> qWindowRef, ::OHNativeWindow *nativeWindow,
113 QOhosConsumer<QWindow *> qtThreadFlushFunc)
115 auto sharedQtThreadFlushFunc = QtOhos::moveToSharedPtr(std::move(qtThreadFlushFunc));
116 return QtOhos::evalInJsThread(
117 [&](QtOhos::JsState &) {
118 auto sharedFrameRequestFunc = QtOhos::makeProxyWithJsThreadDeleter(
119 QtOhos::moveToSharedPtr(
120 QArkUi::makeVSyncFrameRequester(
122 [qWindowRef, sharedQtThreadFlushFunc]() {
123 qWindowRef.visitInQtThreadIfAlive(
124 [sharedQtThreadFlushFunc](QWindow &qWindow) {
125 (*sharedQtThreadFlushFunc)(&qWindow);
129 return [sharedFrameRequestFunc]() {
130 (*sharedFrameRequestFunc)();
139 std::max(0, -offset.x()),
140 std::max(0, -offset.y()));
145QOhosPlatformBackingStore::QOhosPlatformBackingStore(QWindow *window,
const CreateInfo &createInfo)
146 : QRasterBackingStore(window)
147 , m_debugDrawFlushedRegion(createInfo.debugDrawFlushedRegion)
148 , m_vsyncEnabled(createInfo.enableVsync)
150 std::make_shared<std::function<
void(QWindow *)>>(
151 [
this](QWindow *qWindow) {
152 flushImmediate(qWindow);
154 , m_windowContextManager(createInfo.enableVsync, m_flushFunc)
158void QOhosPlatformBackingStore::flush(QWindow *window,
const QRegion ®ion,
const QPoint &offset)
160 if (m_reinitializeContextManager) {
161 m_windowContextManager = WindowContextManager(m_vsyncEnabled, m_flushFunc);
162 m_reinitializeContextManager =
false;
165 auto *platformWindow = QOhosPlatformWindow::fromQWindowOrNull(window);
166 auto *surface = platformWindow !=
nullptr ? platformWindow->ownedSurfaceOrNull() :
nullptr;
167 auto bounds = region.translated(offset).boundingRect() & m_image.rect();
169 if (bounds.isEmpty())
172 if (surface ==
nullptr) {
173 qOhosPrintfDebug(
"Window %p has no surface", window);
177 m_windowContextManager
178 .getOrCreateWindowContext(window, surface->nativeWindow())
179 .flushData().updateDirtyRegionAndScheduleFlush(region, offset);
182void QOhosPlatformBackingStore::resize(
const QSize &size,
const QRegion &staticContents)
184 m_reinitializeContextManager = (size != m_requestedSize);
185 QRasterBackingStore::resize(size, staticContents);
188QImage::Format QOhosPlatformBackingStore::format()
const
190 return QOhosSurface::mapNativeBufferFormatToQImageFormatOrFail(QOhosSurface::bufferFormat);
193QOhosPlatformBackingStore::QImageView::QImageView(
const QImage &srcImage,
const QRect &subRect)
194 : m_srcImage(srcImage)
197 auto imageRect = QRect(QPoint{}, m_srcImage.size());
198 if (!imageRect.contains(m_subRect)) {
199 qOhosReportFatalErrorAndAbort(
200 "image rect: (%d, %d, %d, %d) does not contain sub-rect: (%d, %d, %d, %d)",
201 imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(),
202 m_subRect.x(), m_subRect.y(), m_subRect.width(), m_subRect.height());
205 constexpr auto minAcceptedImageDepth = 8;
206 if (srcImage.depth() < minAcceptedImageDepth)
207 qOhosReportFatalErrorAndAbort(
"QImageView is not supported for <8bpp QImages");
210std::size_t QOhosPlatformBackingStore::QImageView::bytesPerPixel()
const
212 return qImageBytesPerPixel(m_srcImage);
215QSpan<
const uchar> QOhosPlatformBackingStore::QImageView::constScanLine(
int i)
const
217 Q_ASSERT(i >= 0 && i < m_subRect.height());
218 auto srcScanLine = QSpan(
219 m_srcImage.constScanLine(m_subRect.y() + i), m_srcImage.bytesPerLine());
220 return srcScanLine.subspan(
221 m_subRect.x() * bytesPerPixel(), bytesPerLine());
224std::size_t QOhosPlatformBackingStore::QImageView::bytesPerLine()
const
226 return bytesPerPixel() * m_subRect.width();
229QSize QOhosPlatformBackingStore::QImageView::size()
const
231 return m_subRect.size();
234QOhosPlatformBackingStore::BufferRegionHandler::BufferRegionHandler(::OHNativeWindow *nativeWindow)
235 : m_bufferQueueSize(getNativeWindowBufferQueueSize(nativeWindow))
239std::optional<QRegion> QOhosPlatformBackingStore::BufferRegionHandler::mergeRegionForBufferHandle(
240 ::BufferHandle *bufferHandle,
241 QRegion region)
const
243 const auto it = m_buffersToFlushSequenceIds.find(bufferHandle);
244 if (it == m_buffersToFlushSequenceIds.end())
247 std::uint64_t numberOfRegions = m_flushSequenceId - it->second;
248 if (numberOfRegions > m_bufferQueueSize)
251 for (
auto it = m_lastFlushedRegions.cend() - numberOfRegions; it != m_lastFlushedRegions.cend(); ++it)
252 region = region.united(*it);
257void QOhosPlatformBackingStore::BufferRegionHandler::storeRegionForBufferHandle(
258 ::BufferHandle *bufferHandle,
259 const QRegion ®ion)
261 m_buffersToFlushSequenceIds[bufferHandle] = m_flushSequenceId;
263 if (m_buffersToFlushSequenceIds.size() > m_bufferQueueSize) {
264 m_buffersToFlushSequenceIds.clear();
265 m_lastFlushedRegions.clear();
266 m_flushSequenceId = 0;
270 m_lastFlushedRegions.push_back(region);
271 if (m_lastFlushedRegions.size() > m_bufferQueueSize)
272 m_lastFlushedRegions.pop_front();
277QOhosPlatformBackingStore::FlushData::FlushData(
std::function<
void()> flushRequestFunc)
278 : m_flushRequestFunc(std::move(flushRequestFunc))
282std::pair<QRegion, QPoint> QOhosPlatformBackingStore::FlushData::fetchAndReset()
284 return std::make_pair(
285 std::exchange(m_mergedRegionForFlush, QRegion {}),
286 std::exchange(m_lastWindowOffset, QPoint {}));
289void QOhosPlatformBackingStore::FlushData::updateDirtyRegionAndScheduleFlush(
290 const QRegion ®ion,
const QPoint &rootWindowOffset)
292 m_mergedRegionForFlush = m_mergedRegionForFlush.united(region);
293 m_lastWindowOffset = rootWindowOffset;
294 m_flushRequestFunc();
297QOhosPlatformBackingStore::WindowContext::WindowContext(
298 ::OHNativeWindow *nativeWindow,
std::function<
void()> flushRequestFunc)
299 : m_bufferRegionHandler(std::make_unique<BufferRegionHandler>(nativeWindow))
300 , m_flushData(std::move(flushRequestFunc))
304QOhosPlatformBackingStore::BufferRegionHandler &
305QOhosPlatformBackingStore::WindowContext::bufferRegionHandler()
307 return *m_bufferRegionHandler;
310QOhosPlatformBackingStore::FlushData &QOhosPlatformBackingStore::WindowContext::flushData()
315QOhosPlatformBackingStore::WindowContextManager::WindowContextManager(
317 std::shared_ptr<std::function<
void(QWindow *)>> flushImmediateFunc)
318 : m_flushFunc(flushImmediateFunc)
319 , m_vsyncEnabled(vsyncEnabled)
323QOhosPlatformBackingStore::WindowContext &
324QOhosPlatformBackingStore::WindowContextManager::getOrCreateWindowContext(
326 ::OHNativeWindow *nativeWindow)
328 auto handlerIter = m_windowContexts.find(window);
329 if (handlerIter == m_windowContexts.end()) {
331 std::function<
void()> flushFunc;
333 if (m_vsyncEnabled) {
334 auto weakQtFlushFunc = QtOhos::makeWeakPtr(m_flushFunc);
335 auto qWindowRef = QtOhos::makeQThreadSafeRef(window);
336 flushFunc = makeVSyncFrameRequestFunc(
337 qWindowRef, nativeWindow,
338 [weakQtFlushFunc](QWindow *qWindow) {
339 auto sharedQtThreadFlushFunc = weakQtFlushFunc.lock();
340 if (sharedQtThreadFlushFunc)
341 (*sharedQtThreadFlushFunc)(qWindow);
344 flushFunc = [window, flushFunc = m_flushFunc]() {
345 (*flushFunc)(window);
349 std::tie(handlerIter, std::ignore) = m_windowContexts.emplace(
350 window, std::make_unique<WindowContext>(
351 nativeWindow, std::move(flushFunc)));
353 return *(handlerIter->second);
356bool QOhosPlatformBackingStore::scroll(
const QRegion &area,
int dx,
int dy)
360 const qreal devicePixelRatio = m_image.devicePixelRatio();
362 static_cast<
int>(dx * devicePixelRatio),
363 static_cast<
int>(dy * devicePixelRatio));
365 for (
const QRect &rect : area) {
366 qt_scrollRectInImage(
368 QRect(rect.topLeft() * devicePixelRatio, rect.size() * devicePixelRatio), delta);
374void QOhosPlatformBackingStore::beginPaint(
const QRegion ®ion)
376 auto previousImageSize = m_image.size();
377 QRasterBackingStore::beginPaint(region);
378 bool imageWasResized = previousImageSize != m_image.size();
380 m_windowContextManager = WindowContextManager(m_vsyncEnabled, m_flushFunc);
383void QOhosPlatformBackingStore::flushImmediate(QWindow *window)
385 auto *platformWindow = QOhosPlatformWindow::fromQWindowOrNull(window);
386 auto *surface = platformWindow !=
nullptr ? platformWindow->ownedSurfaceOrNull() :
nullptr;
388 if (surface ==
nullptr) {
389 qOhosPrintfDebug(
"Window %p has no surface", window);
393 bool isRootWindow = window ==
this->window();
395 ::OHNativeWindow *nativeWindow = surface->nativeWindow();
397 auto &windowContext = m_windowContextManager.getOrCreateWindowContext(window, nativeWindow);
399 QPoint rootWindowOffset;
400 std::tie(region, rootWindowOffset) = windowContext.flushData().fetchAndReset();
402 if (region.isEmpty())
405 auto &bufferRegionHandler = m_windowContextManager
406 .getOrCreateWindowContext(window, nativeWindow).bufferRegionHandler();
408 auto srcImageRect = isRootWindow
409 ? QRect({}, m_image.size())
410 : QRect(rootWindowOffset, platformWindow->geometry().size()).intersected(QRect({}, m_image.size()));
411 if (srcImageRect.isEmpty()) {
412 qOhosPrintfDebug(
"Cannot get source image rect, ignore flush call");
416 QImageView srcImage = QImageView(m_image, srcImageRect);
418 const auto rootWindowRegionToFlush = region
419 .translated(rootWindowOffset)
420 .intersected(srcImageRect)
421 .translated(-rootWindowOffset);
423 surface->paintOnNativeWindowSurface(
424 [&](QImage &dstImage, ::BufferHandle *bufferHandle) {
425 if (srcImage.bytesPerPixel() != qImageBytesPerPixel(dstImage)) {
426 qOhosReportFatalErrorAndAbort(
427 "%s: bytes per pixel in src and dst image mismatch. Image formats are not the same.", Q_FUNC_INFO);
430 const auto& mergedRegionOpt = bufferRegionHandler.mergeRegionForBufferHandle(bufferHandle, rootWindowRegionToFlush);
431 const auto dstImageOffset = extractNegativeOffset(rootWindowOffset);
432 const auto windowVisibleRegion = QRegion(QRect(dstImageOffset, srcImage.size()));
433 const auto requestedRegion = mergedRegionOpt.value_or(windowVisibleRegion);
434 const auto sourceRegionToFlush = requestedRegion.translated(-dstImageOffset);
435 copyImage(srcImage, dstImage, sourceRegionToFlush, dstImageOffset);
437 if (m_debugDrawFlushedRegion)
438 debugDrawFlushedQRegion(dstImage, rootWindowRegionToFlush);
440 return makeOhosRegionRectsForFlush(
441 mergedRegionOpt.value_or(QRegion()), isRootWindow ? QPoint{} : rootWindowOffset, dstImage.size());
443 [&](::BufferHandle *bufferHandle) {
444 bufferRegionHandler.storeRegionForBufferHandle(bufferHandle, rootWindowRegionToFlush);
448 auto *view = platformWindow->ownedViewOrNull();
450 view->handleSurfaceContentsUpdated();
constexpr std::int32_t ohNativeWindowErrorCodeSuccess
std::function< void()> makeVSyncFrameRequestFunc(QtOhos::QThreadSafeRef< QWindow > qWindowRef, ::OHNativeWindow *nativeWindow, QOhosConsumer< QWindow * > qtThreadFlushFunc)
void copyImageRow(QSpan< const uchar > srcRow, QSpan< uchar > dstRow)
std::vector<::Region::Rect > makeOhosRegionRectsForFlush(const QRegion ®ion, const QPoint &rootWindowOffset, const QSize &dstImageSize)
QSpan< uchar > qImageScanLine(QImage &image, int y)
void debugDrawFlushedQRegion(QImage &dstImage, const QRegion ®ion)
std::size_t qImageBytesPerPixel(const QImage &image)
QPoint extractNegativeOffset(const QPoint &offset)
void copyImage(QOhosPlatformBackingStore::QImageView srcImage, QImage &dstImage, const QRegion ®ion, const QPoint &dstOffset)
std::uint64_t getNativeWindowBufferQueueSize(::OHNativeWindow *nativeWindow)
QT_BEGIN_NAMESPACE void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &)