1/**************************************************************************** 2** 3** Copyright (C) 2019 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the plugins of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40#include "qiosurfacegraphicsbuffer.h" 41 42#include <QtCore/qdebug.h> 43#include <QtCore/qloggingcategory.h> 44 45#include <CoreGraphics/CoreGraphics.h> 46#include <IOSurface/IOSurface.h> 47 48// CGColorSpaceCopyPropertyList is available on 10.12 and above, 49// but was only added in the 10.14 SDK, so declare it just in case. 50extern "C" CFPropertyListRef CGColorSpaceCopyPropertyList(CGColorSpaceRef space); 51 52QT_BEGIN_NAMESPACE 53 54Q_LOGGING_CATEGORY(lcQpaIOSurface, "qt.qpa.backingstore.iosurface"); 55 56QIOSurfaceGraphicsBuffer::QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format) 57 : QPlatformGraphicsBuffer(size, format) 58{ 59 const size_t width = size.width(); 60 const size_t height = size.height(); 61 62 Q_ASSERT(width <= IOSurfaceGetPropertyMaximum(kIOSurfaceWidth)); 63 Q_ASSERT(height <= IOSurfaceGetPropertyMaximum(kIOSurfaceHeight)); 64 65 static const char bytesPerElement = 4; 66 67 const size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement); 68 const size_t totalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow); 69 70 NSDictionary *options = @{ 71 (id)kIOSurfaceWidth: @(width), 72 (id)kIOSurfaceHeight: @(height), 73 (id)kIOSurfacePixelFormat: @(unsigned('BGRA')), 74 (id)kIOSurfaceBytesPerElement: @(bytesPerElement), 75 (id)kIOSurfaceBytesPerRow: @(bytesPerRow), 76 (id)kIOSurfaceAllocSize: @(totalBytes), 77 }; 78 79 m_surface = IOSurfaceCreate((CFDictionaryRef)options); 80 Q_ASSERT(m_surface); 81 82 Q_ASSERT(size_t(bytesPerLine()) == bytesPerRow); 83 Q_ASSERT(size_t(byteCount()) == totalBytes); 84} 85 86QIOSurfaceGraphicsBuffer::~QIOSurfaceGraphicsBuffer() 87{ 88} 89 90void QIOSurfaceGraphicsBuffer::setColorSpace(QCFType<CGColorSpaceRef> colorSpace) 91{ 92 static const auto kIOSurfaceColorSpace = CFSTR("IOSurfaceColorSpace"); 93 94 qCDebug(lcQpaIOSurface) << "Tagging" << this << "with color space" << colorSpace; 95 96 if (colorSpace) { 97 IOSurfaceSetValue(m_surface, kIOSurfaceColorSpace, 98 QCFType<CFPropertyListRef>(CGColorSpaceCopyPropertyList(colorSpace))); 99 } else { 100 IOSurfaceRemoveValue(m_surface, kIOSurfaceColorSpace); 101 } 102} 103 104const uchar *QIOSurfaceGraphicsBuffer::data() const 105{ 106 return (const uchar *)IOSurfaceGetBaseAddress(m_surface); 107} 108 109uchar *QIOSurfaceGraphicsBuffer::data() 110{ 111 return (uchar *)IOSurfaceGetBaseAddress(m_surface); 112} 113 114int QIOSurfaceGraphicsBuffer::bytesPerLine() const 115{ 116 return IOSurfaceGetBytesPerRow(m_surface); 117} 118 119IOSurfaceRef QIOSurfaceGraphicsBuffer::surface() 120{ 121 return m_surface; 122} 123 124bool QIOSurfaceGraphicsBuffer::isInUse() const 125{ 126 return IOSurfaceIsInUse(m_surface); 127} 128 129IOSurfaceLockOptions lockOptionsForAccess(QPlatformGraphicsBuffer::AccessTypes access) 130{ 131 IOSurfaceLockOptions lockOptions = 0; 132 if (!(access & QPlatformGraphicsBuffer::SWWriteAccess)) 133 lockOptions |= kIOSurfaceLockReadOnly; 134 return lockOptions; 135} 136 137bool QIOSurfaceGraphicsBuffer::doLock(AccessTypes access, const QRect &rect) 138{ 139 Q_UNUSED(rect); 140 Q_ASSERT(!isLocked()); 141 142 qCDebug(lcQpaIOSurface) << "Locking" << this << "for" << access; 143 144 // FIXME: Teach QPlatformBackingStore::composeAndFlush about non-2D texture 145 // targets, so that we can use CGLTexImageIOSurface2D to support TextureAccess. 146 if (access & (TextureAccess | HWCompositor)) 147 return false; 148 149 auto lockOptions = lockOptionsForAccess(access); 150 151 // Try without read-back first 152 lockOptions |= kIOSurfaceLockAvoidSync; 153 kern_return_t ret = IOSurfaceLock(m_surface, lockOptions, nullptr); 154 if (ret == kIOSurfaceSuccess) 155 return true; 156 157 if (ret == kIOReturnCannotLock) { 158 qCWarning(lcQpaIOSurface) << "Locking of" << this << "requires read-back"; 159 lockOptions ^= kIOSurfaceLockAvoidSync; 160 ret = IOSurfaceLock(m_surface, lockOptions, nullptr); 161 } 162 163 if (ret != kIOSurfaceSuccess) { 164 qCWarning(lcQpaIOSurface) << "Failed to lock" << this << ret; 165 return false; 166 } 167 168 return true; 169} 170 171void QIOSurfaceGraphicsBuffer::doUnlock() 172{ 173 qCDebug(lcQpaIOSurface) << "Unlocking" << this << "from" << isLocked(); 174 175 auto lockOptions = lockOptionsForAccess(isLocked()); 176 bool success = IOSurfaceUnlock(m_surface, lockOptions, nullptr) == kIOSurfaceSuccess; 177 Q_ASSERT_X(success, "QIOSurfaceGraphicsBuffer", "Unlocking surface should succeed"); 178} 179 180#ifndef QT_NO_DEBUG_STREAM 181QDebug operator<<(QDebug debug, const QIOSurfaceGraphicsBuffer *graphicsBuffer) 182{ 183 QDebugStateSaver saver(debug); 184 debug.nospace(); 185 debug << "QIOSurfaceGraphicsBuffer(" << (const void *)graphicsBuffer; 186 if (graphicsBuffer) { 187 debug << ", surface=" << graphicsBuffer->m_surface; 188 debug << ", size=" << graphicsBuffer->size(); 189 debug << ", isLocked=" << bool(graphicsBuffer->isLocked()); 190 debug << ", isInUse=" << graphicsBuffer->isInUse(); 191 } 192 debug << ')'; 193 return debug; 194} 195#endif // !QT_NO_DEBUG_STREAM 196 197QT_END_NAMESPACE 198