Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qrhimetal.mm
Go to the documentation of this file.
1// Copyright (C) 2023 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 "qrhimetal_p.h"
5#include "qshader_p.h"
6#include <QGuiApplication>
7#include <QWindow>
8#include <QUrl>
9#include <QFile>
10#include <QTemporaryFile>
11#include <QFileInfo>
12#include <qmath.h>
13#include <QOperatingSystemVersion>
14
15#include <QtCore/private/qcore_mac_p.h>
16
17#ifdef Q_OS_MACOS
18#include <AppKit/AppKit.h>
19#else
20#include <UIKit/UIKit.h>
21#endif
22
23#include <Metal/Metal.h>
24#include <QuartzCore/CAMetalLayer.h>
25
27
28/*
29 Metal backend. Double buffers and throttles to vsync. "Dynamic" buffers are
30 Shared (host visible) and duplicated (to help having 2 frames in flight),
31 "static" and "immutable" are Managed on macOS and Shared on iOS/tvOS.
32 Textures are Private (device local) and a host visible staging buffer is
33 used to upload data to them. Does not rely on strong objects refs from
34 command buffers but does rely on the automatic resource tracking of the
35 command encoders. Assumes that an autorelease pool (ideally per frame) is
36 available on the thread on which QRhi is used.
37*/
38
39#if __has_feature(objc_arc)
40#error ARC not supported
41#endif
42
43// Note: we expect everything here pass the Metal API validation when running
44// in Debug mode in XCode (or with METAL_DEVICE_WRAPPER_TYPE=1). An exception
45// is the nextDrawable Called Early blah blah warning, which is plain and
46// simply false. This may not be present with newer XCode. There may also be
47// warnings about threading (e.g. about accessing view.layer), those are
48// expected for now.
49
135{
138 std::array<uint, 3> localSize = {};
143
144 void destroy() {
146 [lib release];
147 lib = nil;
148 [func release];
149 func = nil;
150 }
151};
152
154{
155 QRhiMetalData(QRhiMetal *rhi) : q(rhi), ofr(rhi) { }
156
160 API_AVAILABLE(macosx(11.0), ios(14.0)) id<MTLBinaryArchive> binArch = nil;
162
163 MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil,
164 const QColor &colorClearValue,
165 const QRhiDepthStencilClearValue &depthStencilClearValue,
166 int colorAttCount);
167 id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
168 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
169 id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
170 bool setupBinaryArchive(NSURL *sourceFileUrl = nil);
171 void addRenderPipelineToBinaryArchive(MTLRenderPipelineDescriptor *rpDesc);
172 void trySeedingRenderPipelineFromBinaryArchive(MTLRenderPipelineDescriptor *rpDesc);
173 void addComputePipelineToBinaryArchive(MTLComputePipelineDescriptor *cpDesc);
174 void trySeedingComputePipelineFromBinaryArchive(MTLComputePipelineDescriptor *cpDesc);
175
177 enum Type {
184 ComputePipeline
185 };
187 int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
188 union {
189 struct {
192 struct {
195 struct {
200 struct {
203 struct {
205 } stagingBuffer;
206 struct {
209 std::array<id<MTLComputePipelineState>, 3> tessVertexComputeState;
211 } graphicsPipeline;
212 struct {
214 } computePipeline;
215 };
216 };
218
221 bool active = false;
222 double lastGpuTime = 0;
225
234 };
236
238 {
244 };
245
247
248 MTLCaptureManager *captureMgr;
250
251 static const int TEXBUF_ALIGN = 256; // probably not accurate
252
254};
255
258
260{
267 };
269};
270
272{
273 MTLPixelFormat format;
275};
276
278{
280
282 MTLPixelFormat format;
285 bool owns = true;
287
289};
290
292{
294};
295
297 struct Stage {
298 struct Buffer {
302 };
303 struct Texture {
306 };
307 struct Sampler {
310 };
319 enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2, TESSCTRL = 3, TESSEVAL = 4 };
320};
321
323{
325 double lastGpuTime = 0;
329 MTLRenderPassDescriptor *currentPassRpDesc;
335};
336
338{
340 float dpr = 1;
341 int sampleCount = 1;
343 int dsAttCount = 0;
344
345 struct ColorAtt {
348 int arrayLayer = 0;
349 int slice = 0;
350 int level = 0;
355 };
356
357 struct {
360 bool hasStencil = false;
361 bool depthNeedsStore = false;
362 } fb;
363
365};
366
368{
372 MTLPrimitiveType primitiveType;
373 MTLWinding winding;
374 MTLCullMode cullMode;
375 MTLTriangleFillMode triangleFillMode;
381 enum class WorkBufType {
384 };
391 bool enabled = false;
392 bool failed = false;
396 std::array<id<MTLComputePipelineState>, 3> vertexComputeState = {};
401 {
402 // max vertex output components = resourceLimit(MaxVertexOutputs) * 4 = 60
403 return vertexOrIndexCount * instanceCount * sizeof(float) * 60;
404 }
406 {
407 return outControlPointCount * patchCount * sizeof(float) * 60;
408 }
410 {
411 // assume maxTessellationControlPerPatchOutputComponents is 128
412 return patchCount * sizeof(float) * 128;
413 }
415 {
416 return ((vertexOrIndexCount + inControlPointCount - 1) / inControlPointCount) * instanceCount;
417 }
418 static int vsCompVariantToIndex(QShader::Variant vertexCompVariant);
423 void setupVertexInputDescriptor(MTLVertexDescriptor *desc);
424 void setupStageInputDescriptor(MTLStageInputOutputDescriptor *desc);
425
426 // SPIRV-Cross buffer size buffers
428};
429
431{
434 MTLSize localSize;
435
436 // SPIRV-Cross buffer size buffers
438};
439
441{
442 CAMetalLayer *layer = nullptr;
444 dispatch_semaphore_t sem[QMTL_FRAMES_IN_FLIGHT];
446 MTLRenderPassDescriptor *rp = nullptr;
449 MTLPixelFormat colorFormat;
450#ifdef Q_OS_MACOS
451 bool liveResizeObserverSet = false;
452 QMacNotificationObserver liveResizeStartObserver;
453 QMacNotificationObserver liveResizeEndObserver;
454#endif
455};
456
458{
460
461 d = new QRhiMetalData(this);
462
463 importedDevice = importDevice != nullptr;
464 if (importedDevice) {
465 if (importDevice->dev) {
466 d->dev = (id<MTLDevice>) importDevice->dev;
467 importedCmdQueue = importDevice->cmdQueue != nullptr;
469 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
470 } else {
471 qWarning("No MTLDevice given, cannot import");
472 importedDevice = false;
473 }
474 }
475}
476
478{
479 delete d;
480}
481
482template <class Int>
483inline Int aligned(Int v, Int byteAlign)
484{
485 return (v + byteAlign - 1) & ~(byteAlign - 1);
486}
487
489{
491 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
492 if (dev) {
493 [dev release];
494 return true;
495 }
496 return false;
497}
498
499bool QRhiMetalData::setupBinaryArchive(NSURL *sourceFileUrl)
500{
501 if (@available(macOS 11.0, iOS 14.0, *)) {
502 [binArch release];
503 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor new];
504 binArchDesc.url = sourceFileUrl;
505 NSError *err = nil;
506 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
507 [binArchDesc release];
508 if (!binArch) {
509 const QString msg = QString::fromNSString(err.localizedDescription);
510 qWarning("newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
511 return false;
512 }
513 binArchWasEmpty = sourceFileUrl == nil;
514 return true;
515 }
516 return false;
517}
518
519bool QRhiMetal::create(QRhi::Flags flags)
520{
521 rhiFlags = flags;
522
523 if (importedDevice)
524 [d->dev retain];
525 else
526 d->dev = MTLCreateSystemDefaultDevice();
527
528 if (!d->dev) {
529 qWarning("No MTLDevice");
530 return false;
531 }
532
533 const QString deviceName = QString::fromNSString([d->dev name]);
534 qCDebug(QRHI_LOG_INFO, "Metal device: %s", qPrintable(deviceName));
535 driverInfoStruct.deviceName = deviceName.toUtf8();
536
537 // deviceId and vendorId stay unset for now. Note that registryID is not
538 // suitable as deviceId because it does not seem stable on macOS and can
539 // apparently change when the system is rebooted.
540
541#ifdef Q_OS_IOS
543#else
544 if (@available(macOS 10.15, *)) {
545 const MTLDeviceLocation deviceLocation = [d->dev location];
546 switch (deviceLocation) {
547 case MTLDeviceLocationBuiltIn:
549 break;
550 case MTLDeviceLocationSlot:
552 break;
553 case MTLDeviceLocationExternal:
555 break;
556 default:
557 break;
558 }
559 }
560#endif
561
563 osMajor = ver.majorVersion();
564 osMinor = ver.minorVersion();
565
567 [d->cmdQueue retain];
568 else
569 d->cmdQueue = [d->dev newCommandQueue];
570
571 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
572 // Have a custom capture scope as well which then shows up in XCode as
573 // an option when capturing, and becomes especially useful when having
574 // multiple windows with multiple QRhis.
575 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
576 const QString label = QString::asprintf("Qt capture scope for QRhi %p", this);
577 d->captureScope.label = label.toNSString();
578
579#if defined(Q_OS_MACOS)
580 caps.maxTextureSize = 16384;
581 caps.baseVertexAndInstance = true;
582 if (@available(macOS 10.15, *))
583 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
584 caps.maxThreadGroupSize = 1024;
585 caps.multiView = true;
586#elif defined(Q_OS_TVOS)
587 if ([d->dev supportsFeatureSet: MTLFeatureSet(30003)]) // MTLFeatureSet_tvOS_GPUFamily2_v1
588 caps.maxTextureSize = 16384;
589 else
590 caps.maxTextureSize = 8192;
591 caps.baseVertexAndInstance = false;
592 caps.isAppleGPU = true;
593#elif defined(Q_OS_IOS)
594 // welcome to feature set hell
595 if ([d->dev supportsFeatureSet: MTLFeatureSet(16)] // MTLFeatureSet_iOS_GPUFamily5_v1
596 || [d->dev supportsFeatureSet: MTLFeatureSet(11)] // MTLFeatureSet_iOS_GPUFamily4_v1
597 || [d->dev supportsFeatureSet: MTLFeatureSet(4)]) // MTLFeatureSet_iOS_GPUFamily3_v1
598 {
599 caps.maxTextureSize = 16384;
600 caps.baseVertexAndInstance = true;
601 } else if ([d->dev supportsFeatureSet: MTLFeatureSet(3)] // MTLFeatureSet_iOS_GPUFamily2_v2
602 || [d->dev supportsFeatureSet: MTLFeatureSet(2)]) // MTLFeatureSet_iOS_GPUFamily1_v2
603 {
604 caps.maxTextureSize = 8192;
605 caps.baseVertexAndInstance = false;
606 } else {
607 caps.maxTextureSize = 4096;
608 caps.baseVertexAndInstance = false;
609 }
610 caps.isAppleGPU = true;
611 if (@available(iOS 13, *)) {
612 if ([d->dev supportsFamily: MTLGPUFamilyApple4])
613 caps.maxThreadGroupSize = 1024;
614 if ([d->dev supportsFamily: MTLGPUFamilyApple5])
615 caps.multiView = true;
616 }
617#endif
618
619 caps.supportedSampleCounts = { 1 };
620 for (int sampleCount : { 2, 4, 8 }) {
621 if ([d->dev supportsTextureSampleCount: sampleCount])
622 caps.supportedSampleCounts.append(sampleCount);
623 }
624
627
628 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
629 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
630
631 return true;
632}
633
635{
638
639 for (QMetalShader &s : d->shaderCache)
640 s.destroy();
642
644 d->captureScope = nil;
645
646 if (@available(macOS 11.0, iOS 14.0, *)) {
647 [d->binArch release];
648 d->binArch = nil;
649 }
650
651 [d->cmdQueue release];
652 if (!importedCmdQueue)
653 d->cmdQueue = nil;
654
655 [d->dev release];
656 if (!importedDevice)
657 d->dev = nil;
658}
659
661{
662 return caps.supportedSampleCounts;
663}
664
665int QRhiMetal::effectiveSampleCount(int sampleCount) const
666{
667 // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
668 const int s = qBound(1, sampleCount, 64);
670 qWarning("Attempted to set unsupported sample count %d", sampleCount);
671 return 1;
672 }
673 return s;
674}
675
677{
678 return new QMetalSwapChain(this);
679}
680
682{
683 return new QMetalBuffer(this, type, usage, size);
684}
685
687{
688 return 256;
689}
690
692{
693 return false;
694}
695
697{
698 return true;
699}
700
702{
703 return true;
704}
705
707{
708 // depth range 0..1
709 static QMatrix4x4 m;
710 if (m.isIdentity()) {
711 // NB the ctor takes row-major
712 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
713 0.0f, 1.0f, 0.0f, 0.0f,
714 0.0f, 0.0f, 0.5f, 0.5f,
715 0.0f, 0.0f, 0.0f, 1.0f);
716 }
717 return m;
718}
719
721{
723
724 bool supportsFamilyMac2 = false; // needed for BC* formats
725 bool supportsFamilyApple3 = false;
726
727#ifdef Q_OS_MACOS
728 supportsFamilyMac2 = true;
729 if (caps.isAppleGPU)
730 supportsFamilyApple3 = true;
731#else
732 supportsFamilyApple3 = true;
733#endif
734
735 // BC5 is not available for any Apple hardare
737 return false;
738
739 if (!supportsFamilyApple3) {
741 return false;
743 return false;
744 }
745
746 if (!supportsFamilyMac2)
748 return false;
749
750 return true;
751}
752
754{
755 switch (feature) {
757 return true;
759 return true;
761 return true;
762 case QRhi::Timestamps:
763 return true;
764 case QRhi::Instancing:
765 return true;
767 return true;
769 return true;
771 return true;
773 return false;
775 return true;
777 return true;
779 return true;
780 case QRhi::Compute:
781 return true;
782 case QRhi::WideLines:
783 return false;
785 return true;
786 case QRhi::BaseVertex:
787 return caps.baseVertexAndInstance;
789 return caps.baseVertexAndInstance;
791 return false;
793 return true;
795 return true;
796 case QRhi::TexelFetch:
797 return true;
799 return true;
801 return true;
803 return true;
805 return true;
807 {
808 if (@available(macOS 11.0, iOS 14.0, *))
809 return true;
810 else
811 return false;
812 }
814 return true;
816 return false;
818 return true;
820 return true;
822 return true;
824 return true;
826 return false;
828 return false;
830 return true;
832 return true;
834 return false;
836 return true;
838 return false;
840 return true;
841 case QRhi::MultiView:
842 return caps.multiView;
843 default:
844 Q_UNREACHABLE();
845 return false;
846 }
847}
848
850{
851 switch (limit) {
853 return 1;
855 return caps.maxTextureSize;
857 return 8;
863 return 65535;
871 return caps.maxThreadGroupSize;
873 return 2048;
875 return 65536;
877 return 31;
879 return 15; // use the minimum from MTLGPUFamily1/2/3
880 default:
881 Q_UNREACHABLE();
882 return 0;
883 }
884}
885
887{
888 return &nativeHandlesStruct;
889}
890
892{
893 return driverInfoStruct;
894}
895
897{
899 result.totalPipelineCreationTime = totalPipelineCreationTime();
900 return result;
901}
902
904{
905 // not applicable
906 return false;
907}
908
910{
911 for (QMetalShader &s : d->shaderCache)
912 s.destroy();
913
915}
916
918{
919 return false;
920}
921
923{
929 char driver[236];
930};
931
933{
936 if (@available(macOS 11.0, iOS 14.0, *)) {
937 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
938 return data;
939
940 QTemporaryFile tmp;
941 if (!tmp.open()) {
942 qCDebug(QRHI_LOG_INFO, "pipelineCacheData: Failed to create temporary file for Metal");
943 return data;
944 }
945 tmp.close(); // the file exists until the tmp dtor runs
946
947 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
948 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
949 NSError *err = nil;
950 if (![d->binArch serializeToURL: url error: &err]) {
951 const QString msg = QString::fromNSString(err.localizedDescription);
952 // Some of these "errors" are not actual errors. (think of "Nothing to serialize")
953 qCDebug(QRHI_LOG_INFO, "Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
954 return data;
955 }
956
957 QFile f(fn);
958 if (!f.open(QIODevice::ReadOnly)) {
959 qCDebug(QRHI_LOG_INFO, "pipelineCacheData: Failed to reopen temporary file");
960 return data;
961 }
962 const QByteArray blob = f.readAll();
963 f.close();
964
965 const size_t headerSize = sizeof(QMetalPipelineCacheDataHeader);
966 const quint32 dataSize = quint32(blob.size());
967
968 data.resize(headerSize + dataSize);
969
971 header.rhiId = pipelineCacheRhiId();
972 header.arch = quint32(sizeof(void*));
973 header.dataSize = quint32(dataSize);
974 header.osMajor = osMajor;
975 header.osMinor = osMinor;
976 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
977 if (driverStrLen)
978 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
979 header.driver[driverStrLen] = '\0';
980
981 memcpy(data.data(), &header, headerSize);
982 memcpy(data.data() + headerSize, blob.constData(), dataSize);
983 }
984 return data;
985}
986
988{
989 if (data.isEmpty())
990 return;
991
992 const size_t headerSize = sizeof(QMetalPipelineCacheDataHeader);
993 if (data.size() < qsizetype(headerSize)) {
994 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
995 return;
996 }
997
998 const size_t dataOffset = headerSize;
1000 memcpy(&header, data.constData(), headerSize);
1001
1002 const quint32 rhiId = pipelineCacheRhiId();
1003 if (header.rhiId != rhiId) {
1004 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1005 rhiId, header.rhiId);
1006 return;
1007 }
1008
1009 const quint32 arch = quint32(sizeof(void*));
1010 if (header.arch != arch) {
1011 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
1012 arch, header.arch);
1013 return;
1014 }
1015
1016 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1017 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1018 osMajor, osMinor, header.osMajor, header.osMinor);
1019 return;
1020 }
1021
1022 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1023 if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1024 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Metal device name does not match");
1025 return;
1026 }
1027
1028 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1029 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
1030 return;
1031 }
1032
1033 if (@available(macOS 11.0, iOS 14.0, *)) {
1034 const char *p = data.constData() + dataOffset;
1035
1036 QTemporaryFile tmp;
1037 if (!tmp.open()) {
1038 qCDebug(QRHI_LOG_INFO, "pipelineCacheData: Failed to create temporary file for Metal");
1039 return;
1040 }
1041 tmp.write(p, header.dataSize);
1042 tmp.close(); // the file exists until the tmp dtor runs
1043
1044 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1045 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1046 if (d->setupBinaryArchive(url))
1047 qCDebug(QRHI_LOG_INFO, "Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1048 }
1049}
1050
1052 int sampleCount, QRhiRenderBuffer::Flags flags,
1053 QRhiTexture::Format backingFormatHint)
1054{
1055 return new QMetalRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
1056}
1057
1059 const QSize &pixelSize, int depth, int arraySize,
1060 int sampleCount, QRhiTexture::Flags flags)
1061{
1062 return new QMetalTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
1063}
1064
1066 QRhiSampler::Filter mipmapMode,
1068{
1069 return new QMetalSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
1070}
1071
1073 QRhiTextureRenderTarget::Flags flags)
1074{
1075 return new QMetalTextureRenderTarget(this, desc, flags);
1076}
1077
1079{
1080 return new QMetalGraphicsPipeline(this);
1081}
1082
1084{
1085 return new QMetalComputePipeline(this);
1086}
1087
1089{
1090 return new QMetalShaderResourceBindings(this);
1091}
1092
1093enum class BindingType {
1094 Buffer,
1095 Texture,
1096 Sampler
1097};
1098
1099static inline int mapBinding(int binding,
1100 int stageIndex,
1101 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1103{
1104 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1105 if (!map || map->isEmpty())
1106 return binding; // old QShader versions do not have this map, assume 1:1 mapping then
1107
1108 auto it = map->constFind(binding);
1109 if (it != map->cend())
1110 return type == BindingType::Sampler ? it->second : it->first; // may be -1, if the resource is inactive
1111
1112 // Hitting this path is normal too. It is not given that the resource (for
1113 // example, a uniform block) is present in the shaders for all the stages
1114 // specified by the visibility mask in the QRhiShaderResourceBinding.
1115 return -1;
1116}
1117
1119 int stage,
1120 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1121 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1122{
1123 switch (stage) {
1125 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1126 offsets: offsetBatch.resources.constData()
1127 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1128 break;
1130 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1131 offsets: offsetBatch.resources.constData()
1132 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1133 break;
1135 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1136 offsets: offsetBatch.resources.constData()
1137 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1138 break;
1141 // do nothing. These are used later for tessellation
1142 break;
1143 default:
1144 Q_UNREACHABLE();
1145 break;
1146 }
1147}
1148
1150 int stage,
1151 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1152{
1153 switch (stage) {
1155 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1156 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1157 break;
1159 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1160 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1161 break;
1163 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1164 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1165 break;
1168 // do nothing. These are used later for tessellation
1169 break;
1170 default:
1171 Q_UNREACHABLE();
1172 break;
1173 }
1174}
1175
1177 int encoderStage,
1178 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1179{
1180 switch (encoderStage) {
1182 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1183 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1184 break;
1186 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1187 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1188 break;
1190 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1191 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1192 break;
1195 // do nothing. These are used later for tessellation
1196 break;
1197 default:
1198 Q_UNREACHABLE();
1199 break;
1200 }
1201}
1202
1203// Helper that is not used during the common vertex+fragment and compute
1204// pipelines, but is necessary when tessellation is involved and so the
1205// graphics pipeline is under the hood a combination of multiple compute and
1206// render pipelines. We need to be able to set the buffers, textures, samplers
1207// when a switching between render and compute encoders.
1208static inline void rebindShaderResources(QMetalCommandBuffer *cbD, int resourceStage, int encoderStage,
1209 const QMetalShaderResourceBindingsData *customBindingState = nullptr)
1210{
1211 const QMetalShaderResourceBindingsData *bindingData = customBindingState ? customBindingState : &cbD->d->currentShaderResourceBindingState;
1212
1213 for (int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1214 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1215 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1216 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1217 }
1218
1219 for (int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1220 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1221 bindStageTextures(cbD, encoderStage, batch);
1222 }
1223
1224 for (int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1225 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1226 bindStageSamplers(cbD, encoderStage, batch);
1227 }
1228}
1229
1231{
1232 switch (stage) {
1243 }
1244
1246}
1247
1250 int dynamicOffsetCount,
1251 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1252 bool offsetOnlyChange,
1253 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
1254{
1256
1257 for (const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1259 switch (b->type) {
1261 {
1262 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1263 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1264 quint32 offset = b->u.ubuf.offset;
1265 for (int i = 0; i < dynamicOffsetCount; ++i) {
1266 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1267 if (dynOfs.first == b->binding) {
1268 offset = dynOfs.second;
1269 break;
1270 }
1271 }
1272
1273 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1274 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1275 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1276 if (nativeBinding >= 0)
1277 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1278 }
1279 }
1280 }
1281 break;
1285 {
1287 for (int elem = 0; elem < data->count; ++elem) {
1288 QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1289 QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1290
1291 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1292 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1293 // Must handle all three cases (combined, separate, separate):
1294 // first = texture binding, second = sampler binding
1295 // first = texture binding
1296 // first = sampler binding (i.e. BindingType::Texture...)
1297 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1298 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1299 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1300 if (textureBinding >= 0 && texD)
1301 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1302 if (samplerBinding >= 0)
1303 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1304 }
1305 }
1306 }
1307 }
1308 break;
1312 {
1313 QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
1314 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1315
1316 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1317 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1318 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1319 if (nativeBinding >= 0)
1320 bindingData.res[stage].textures.append({ nativeBinding, t });
1321 }
1322 }
1323 }
1324 break;
1328 {
1329 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1330 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1331 quint32 offset = b->u.sbuf.offset;
1332 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1333 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1334 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1335 if (nativeBinding >= 0)
1336 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1337 }
1338 }
1339 }
1340 break;
1341 default:
1342 Q_UNREACHABLE();
1343 break;
1344 }
1345 }
1346
1347 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1350 continue;
1352 continue;
1353
1354 // QRhiBatchedBindings works with the native bindings and expects
1355 // sorted input. The pre-sorted QRhiShaderResourceBinding list (based
1356 // on the QRhi (SPIR-V) binding) is not helpful in this regard, so we
1357 // have to sort here every time.
1358
1359 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](const QMetalShaderResourceBindingsData::Stage::Buffer &a, const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1360 return a.nativeBinding < b.nativeBinding;
1361 });
1362
1363 for (const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1364 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1365 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1366 }
1367
1368 bindingData.res[stage].bufferBatches.finish();
1369 bindingData.res[stage].bufferOffsetBatches.finish();
1370
1371 for (int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1372 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1373 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1374 // skip setting Buffer binding if the current state is already correct
1377 && bufferBatch == cbD->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1378 && offsetBatch == cbD->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1379 {
1380 continue;
1381 }
1382 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1383 }
1384
1385 if (offsetOnlyChange)
1386 continue;
1387
1388 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](const QMetalShaderResourceBindingsData::Stage::Texture &a, const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1389 return a.nativeBinding < b.nativeBinding;
1390 });
1391
1392 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](const QMetalShaderResourceBindingsData::Stage::Sampler &a, const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1393 return a.nativeBinding < b.nativeBinding;
1394 });
1395
1396 for (const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1397 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1398
1399 for (const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1400 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1401
1402 bindingData.res[stage].textureBatches.finish();
1403 bindingData.res[stage].samplerBatches.finish();
1404
1405 for (int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1406 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1407 // skip setting Texture binding if the current state is already correct
1410 {
1411 continue;
1412 }
1413 bindStageTextures(cbD, stage, batch);
1414 }
1415
1416 for (int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1417 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1418 // skip setting Sampler State if the current state is already correct
1421 {
1422 continue;
1423 }
1424 bindStageSamplers(cbD, stage, batch);
1425 }
1426 }
1427
1428 cbD->d->currentShaderResourceBindingState = bindingData;
1429}
1430
1432{
1433 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1434
1435 if (cbD->d->currentDepthStencilState != d->ds) {
1436 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1437 cbD->d->currentDepthStencilState = d->ds;
1438 }
1439
1440 if (cbD->currentCullMode == -1 || d->cullMode != uint(cbD->currentCullMode)) {
1442 cbD->currentCullMode = int(d->cullMode);
1443 }
1445 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1447 }
1448 if (cbD->currentFrontFaceWinding == -1 || d->winding != uint(cbD->currentFrontFaceWinding)) {
1449 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1450 cbD->currentFrontFaceWinding = int(d->winding);
1451 }
1454 {
1456 slopeScale: d->slopeScaledDepthBias
1457 clamp: 0.0f];
1459 }
1460}
1461
1463{
1467
1468 if (cbD->currentGraphicsPipeline == psD && cbD->currentPipelineGeneration == psD->generation)
1469 return;
1470
1471 cbD->currentGraphicsPipeline = psD;
1472 cbD->currentComputePipeline = nullptr;
1474
1475 if (!psD->d->tess.enabled && !psD->d->tess.failed) {
1477 } else {
1478 // mark work buffers that can now be safely reused as reusable
1479 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1480 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1481 workBuf->lastActiveFrameSlot = -1;
1482 }
1483 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1484 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1485 workBuf->lastActiveFrameSlot = -1;
1486 }
1487 }
1488
1490}
1491
1493 int dynamicOffsetCount,
1494 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1495{
1500
1501 if (!srb) {
1502 if (gfxPsD)
1503 srb = gfxPsD->m_shaderResourceBindings;
1504 else
1505 srb = compPsD->m_shaderResourceBindings;
1506 }
1507
1509 bool hasSlottedResourceInSrb = false;
1510 bool hasDynamicOffsetInSrb = false;
1511 bool resNeedsRebind = false;
1512
1513 // SPIRV-Cross buffer size buffers
1514 // Need to determine storage buffer sizes here as this is the last opportunity for storage
1515 // buffer bindings (offset, size) to be specified before draw / dispatch call
1516 const bool needsBufferSizeBuffer = (compPsD && compPsD->d->bufferSizeBuffer) || (gfxPsD && gfxPsD->d->bufferSizeBuffer);
1518
1519 // do buffer writes, figure out if we need to rebind, and mark as in-use
1520 for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1523 switch (b->type) {
1525 {
1526 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1529 if (bufD->d->slotted)
1530 hasSlottedResourceInSrb = true;
1531 if (b->u.ubuf.hasDynamicOffset)
1532 hasDynamicOffsetInSrb = true;
1533 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1534 resNeedsRebind = true;
1535 bd.ubuf.id = bufD->m_id;
1536 bd.ubuf.generation = bufD->generation;
1537 }
1539 }
1540 break;
1544 {
1546 if (bd.stex.count != data->count) {
1547 bd.stex.count = data->count;
1548 resNeedsRebind = true;
1549 }
1550 for (int elem = 0; elem < data->count; ++elem) {
1551 QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex);
1552 QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler);
1553 Q_ASSERT(texD || samplerD);
1554 const quint64 texId = texD ? texD->m_id : 0;
1555 const uint texGen = texD ? texD->generation : 0;
1556 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1557 const uint samplerGen = samplerD ? samplerD->generation : 0;
1558 if (texGen != bd.stex.d[elem].texGeneration
1559 || texId != bd.stex.d[elem].texId
1560 || samplerGen != bd.stex.d[elem].samplerGeneration
1561 || samplerId != bd.stex.d[elem].samplerId)
1562 {
1563 resNeedsRebind = true;
1564 bd.stex.d[elem].texId = texId;
1565 bd.stex.d[elem].texGeneration = texGen;
1566 bd.stex.d[elem].samplerId = samplerId;
1567 bd.stex.d[elem].samplerGeneration = samplerGen;
1568 }
1569 if (texD)
1571 if (samplerD)
1573 }
1574 }
1575 break;
1579 {
1580 QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
1581 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1582 resNeedsRebind = true;
1583 bd.simage.id = texD->m_id;
1584 bd.simage.generation = texD->generation;
1585 }
1587 }
1588 break;
1592 {
1593 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1595
1596 if (needsBufferSizeBuffer) {
1597 for (int i = 0; i < 6; ++i) {
1600 if (b->stage.testFlag(stage)) {
1601 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1602 }
1603 }
1604 }
1605
1607 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1608 resNeedsRebind = true;
1609 bd.sbuf.id = bufD->m_id;
1610 bd.sbuf.generation = bufD->generation;
1611 }
1613 }
1614 break;
1615 default:
1616 Q_UNREACHABLE();
1617 break;
1618 }
1619 }
1620
1621 if (needsBufferSizeBuffer) {
1622 QMetalBuffer *bufD = nullptr;
1624
1625 if (compPsD) {
1626 bufD = compPsD->d->bufferSizeBuffer;
1629 } else {
1630 bufD = gfxPsD->d->bufferSizeBuffer;
1631 if (gfxPsD->d->tess.enabled) {
1632
1633 // Assumptions
1634 // * We only use one of the compute vertex shader variants in a pipeline at any one time
1635 // * The vertex shader variants all have the same storage block bindings
1636 // * The vertex shader variants all have the same native resource binding map
1637 // * The vertex shader variants all have the same MslBufferSizeBufferBinding requirement
1638 // * The vertex shader variants all have the same MslBufferSizeBufferBinding binding
1639 // => We only need to use one vertex shader variant to generate the identical shader
1640 // resource bindings
1641 Q_ASSERT(gfxPsD->d->tess.compVs[0].desc.storageBlocks() == gfxPsD->d->tess.compVs[1].desc.storageBlocks());
1642 Q_ASSERT(gfxPsD->d->tess.compVs[0].desc.storageBlocks() == gfxPsD->d->tess.compVs[2].desc.storageBlocks());
1653
1656
1659
1662
1663 } else {
1666 }
1669 }
1670
1671 quint32 offset = 0;
1673
1674 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1675
1676 // if we don't have a srb entry for the buffer size buffer
1677 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1678
1679 int maxNativeBinding = 0;
1680 for (const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1681 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1682
1683 const int size = (maxNativeBinding + 1) * sizeof(int);
1684
1685 Q_ASSERT(offset + size <= bufD->size());
1687
1689 bd.sbuf.id = bufD->m_id;
1690 bd.sbuf.generation = bufD->generation;
1691 srbD->boundResourceData.append(bd);
1692 }
1693
1694 // create the buffer size buffer data
1695 QVarLengthArray<int, 8> bufferSizeBufferData;
1696 Q_ASSERT(storageBufferSizes.contains(shader.second));
1697 const QMap<int, quint32> &sizes(storageBufferSizes[shader.second]);
1698 for (const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1699 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1700
1701 // if the native binding is -1, the buffer is present but not accessed in the shader
1702 if (index < 0)
1703 continue;
1704
1705 if (bufferSizeBufferData.size() <= index)
1706 bufferSizeBufferData.resize(index + 1);
1707
1708 Q_ASSERT(sizes.contains(block.binding));
1709 bufferSizeBufferData[index] = sizes[block.binding];
1710 }
1711
1713 const quint32 size = bufferSizeBufferData.size() * sizeof(int);
1714 data.assign(reinterpret_cast<const char *>(bufferSizeBufferData.constData()), size);
1715 Q_ASSERT(offset + size <= bufD->size());
1716 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1717
1718 // buffer offsets must be 32byte aligned
1719 offset += ((size + 31) / 32) * 32;
1720 }
1721
1724 }
1725
1726 // make sure the resources for the correct slot get bound
1727 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1728 if (hasSlottedResourceInSrb && cbD->currentResSlot != resSlot)
1729 resNeedsRebind = true;
1730
1731 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srbD) : (cbD->currentComputeSrb != srbD);
1732 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1733
1734 // dynamic uniform buffer offsets always trigger a rebind
1735 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) {
1736 const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = { nullptr, nullptr, nullptr, nullptr, nullptr };
1737 if (gfxPsD) {
1738 cbD->currentGraphicsSrb = srbD;
1739 cbD->currentComputeSrb = nullptr;
1740 if (gfxPsD->d->tess.enabled) {
1741 // If tessellating, we don't know which compVs shader to use until the draw call is
1742 // made. They should all have the same native resource binding map, so pick one.
1748 } else {
1750 }
1752 } else {
1753 cbD->currentGraphicsSrb = nullptr;
1754 cbD->currentComputeSrb = srbD;
1756 }
1757 cbD->currentSrbGeneration = srbD->generation;
1758 cbD->currentResSlot = resSlot;
1759
1760 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
1761 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1762 }
1763}
1764
1766 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
1767 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1768{
1771
1774 for (int i = 0; i < bindingCount; ++i) {
1775 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, bindings[i].first);
1778 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1779 buffers.feed(startBinding + i, mtlbuf);
1780 offsets.feed(startBinding + i, bindings[i].second);
1781 }
1782 buffers.finish();
1783 offsets.finish();
1784
1785 // same binding space for vertex and constant buffers - work it around
1787 // There's nothing guaranteeing setShaderResources() was called before
1788 // setVertexInput()... but whatever srb will get bound will have to be
1789 // layout-compatible anyways so maxBinding is the same.
1790 if (!srbD)
1792 const int firstVertexBinding = srbD->maxBinding + 1;
1793
1794 if (firstVertexBinding != cbD->d->currentFirstVertexBinding
1797 {
1798 cbD->d->currentFirstVertexBinding = firstVertexBinding;
1801
1802 for (int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1803 const auto &bufferBatch(buffers.batches[i]);
1804 const auto &offsetBatch(offsets.batches[i]);
1805 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1806 bufferBatch.resources.constData()
1807 offsets: offsetBatch.resources.constData()
1808 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1809 }
1810 }
1811
1812 if (indexBuf) {
1813 QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, indexBuf);
1816 cbD->currentIndexBuffer = ibufD;
1817 cbD->currentIndexOffset = indexOffset;
1818 cbD->currentIndexFormat = indexFormat;
1819 } else {
1820 cbD->currentIndexBuffer = nullptr;
1821 }
1822}
1823
1825{
1828 const QSize outputSize = cbD->currentTarget->pixelSize();
1829
1830 // x,y is top-left in MTLViewportRect but bottom-left in QRhiViewport
1831 float x, y, w, h;
1832 if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1833 return;
1834
1835 MTLViewport vp;
1836 vp.originX = double(x);
1837 vp.originY = double(y);
1838 vp.width = double(w);
1839 vp.height = double(h);
1840 vp.znear = double(viewport.minDepth());
1841 vp.zfar = double(viewport.maxDepth());
1842
1844
1847 MTLScissorRect s;
1848 qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
1849 s.x = NSUInteger(x);
1850 s.y = NSUInteger(y);
1851 s.width = NSUInteger(w);
1852 s.height = NSUInteger(h);
1853 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1854 }
1855}
1856
1858{
1862 const QSize outputSize = cbD->currentTarget->pixelSize();
1863
1864 // x,y is top-left in MTLScissorRect but bottom-left in QRhiScissor
1865 int x, y, w, h;
1866 if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
1867 return;
1868
1869 MTLScissorRect s;
1870 s.x = NSUInteger(x);
1871 s.y = NSUInteger(y);
1872 s.width = NSUInteger(w);
1873 s.height = NSUInteger(h);
1874
1875 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1876}
1877
1879{
1882
1883 [cbD->d->currentRenderPassEncoder setBlendColorRed: float(c.redF())
1884 green: float(c.greenF()) blue: float(c.blueF()) alpha: float(c.alphaF())];
1885}
1886
1888{
1891
1892 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
1893}
1894
1896{
1897 if (cbD->d->currentRenderPassEncoder) {
1898 [cbD->d->currentRenderPassEncoder endEncoding];
1899 cbD->d->currentRenderPassEncoder = nil;
1900 }
1901
1902 if (!cbD->d->tessellationComputeEncoder)
1903 cbD->d->tessellationComputeEncoder = [cbD->d->cb computeCommandEncoder];
1904
1905 return cbD->d->tessellationComputeEncoder;
1906}
1907
1909{
1910 if (cbD->d->tessellationComputeEncoder) {
1911 [cbD->d->tessellationComputeEncoder endEncoding];
1912 cbD->d->tessellationComputeEncoder = nil;
1913 }
1914
1915 QMetalRenderTargetData * rtD = nullptr;
1916
1917 switch (cbD->currentTarget->resourceType()) {
1920 break;
1923 break;
1924 default:
1925 break;
1926 }
1927
1928 Q_ASSERT(rtD);
1929
1931 for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
1932 oldColorLoad.append(cbD->d->currentPassRpDesc.colorAttachments[i].loadAction);
1933 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
1934 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
1935 }
1936
1937 MTLLoadAction oldDepthLoad;
1938 MTLLoadAction oldStencilLoad;
1939 if (rtD->dsAttCount) {
1940 oldDepthLoad = cbD->d->currentPassRpDesc.depthAttachment.loadAction;
1941 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
1942 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
1943
1944 oldStencilLoad = cbD->d->currentPassRpDesc.stencilAttachment.loadAction;
1945 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
1946 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
1947 }
1948
1949 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
1951
1952 for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
1953 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
1954 }
1955
1956 if (rtD->dsAttCount) {
1957 cbD->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
1958 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
1959 }
1960
1961}
1962
1964{
1965 QMetalCommandBuffer *cbD = args.cbD;
1966 QMetalGraphicsPipeline *graphicsPipeline = cbD->currentGraphicsPipeline;
1967 if (graphicsPipeline->d->tess.failed)
1968 return;
1969
1970 const bool indexed = args.type != TessDrawArgs::NonIndexed;
1971 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
1972 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
1973
1974 QMetalGraphicsPipelineData::Tessellation &tess(graphicsPipeline->d->tess);
1975 QMetalGraphicsPipelineData::ExtraBufferManager &extraBufMgr(graphicsPipeline->d->extraBufMgr);
1976 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
1977 QMetalBuffer *vertOutBuf = nullptr;
1978 QMetalBuffer *tescOutBuf = nullptr;
1979 QMetalBuffer *tescPatchOutBuf = nullptr;
1980 QMetalBuffer *tescFactorBuf = nullptr;
1981 QMetalBuffer *tescParamsBuf = nullptr;
1982 id<MTLComputeCommandEncoder> vertTescComputeEncoder = tessellationComputeEncoder(cbD);
1983
1984 // Step 1: vertex shader (as compute)
1985 {
1986 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
1988 if (args.type == TessDrawArgs::U16Indexed)
1990 else if (args.type == TessDrawArgs::U32Indexed)
1992 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
1993 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(this, shaderVariant);
1994 [computeEncoder setComputePipelineState: computePipelineState];
1995
1996 // Make uniform buffers, textures, and samplers (meant for the
1997 // vertex stage from the client's point of view) visible in the
1998 // "vertex as compute" shader
1999 cbD->d->currentComputePassEncoder = computeEncoder;
2001 cbD->d->currentComputePassEncoder = nil;
2002
2003 const QMap<int, int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2004 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2005 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2006
2007 if (outputBufferBinding >= 0) {
2008 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2009 vertOutBuf = extraBufMgr.acquireWorkBuffer(this, workBufSize);
2010 if (!vertOutBuf)
2011 return;
2012 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2013 }
2014
2015 if (indexBufferBinding >= 0)
2016 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2017
2018 for (int i = 0, ie = cbD->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2019 const auto &bufferBatch(cbD->d->currentVertexInputsBuffers.batches[i]);
2020 const auto &offsetBatch(cbD->d->currentVertexInputOffsets.batches[i]);
2021 [computeEncoder setBuffers: bufferBatch.resources.constData()
2022 offsets: offsetBatch.resources.constData()
2023 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2024 }
2025
2026 if (indexed) {
2027 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2028 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2029 } else {
2030 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2031 args.draw.vertexCount, args.draw.instanceCount)];
2032 }
2033
2034 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2035 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2036 }
2037
2038 // Step 2: tessellation control shader (as compute)
2039 {
2040 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2041 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(this);
2042 [computeEncoder setComputePipelineState: computePipelineState];
2043
2044 cbD->d->currentComputePassEncoder = computeEncoder;
2046 cbD->d->currentComputePassEncoder = nil;
2047
2049 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2050 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2051 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2052 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2053 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2054
2055 if (outputBufferBinding >= 0) {
2056 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2057 tescOutBuf = extraBufMgr.acquireWorkBuffer(this, workBufSize);
2058 if (!tescOutBuf)
2059 return;
2060 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2061 }
2062
2063 if (patchOutputBufferBinding >= 0) {
2064 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2065 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(this, workBufSize);
2066 if (!tescPatchOutBuf)
2067 return;
2068 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2069 }
2070
2071 if (tessFactorBufferBinding >= 0) {
2072 tescFactorBuf = extraBufMgr.acquireWorkBuffer(this, patchCount * sizeof(MTLQuadTessellationFactorsHalf));
2073 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2074 }
2075
2076 if (paramsBufferBinding >= 0) {
2077 struct {
2078 quint32 inControlPointCount;
2079 quint32 patchCount;
2080 } params;
2082 if (!tescParamsBuf)
2083 return;
2084 params.inControlPointCount = tess.inControlPointCount;
2085 params.patchCount = patchCount;
2086 id<MTLBuffer> paramsBuf = tescParamsBuf->d->buf[0];
2087 char *p = reinterpret_cast<char *>([paramsBuf contents]);
2088 memcpy(p, &params, sizeof(params));
2089 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2090 }
2091
2092 if (vertOutBuf && inputBufferBinding >= 0)
2093 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2094
2095 int sgSize = int(computePipelineState.threadExecutionWidth);
2096 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2097 while (wgSize > caps.maxThreadGroupSize) {
2098 sgSize /= 2;
2099 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2100 }
2101 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2102 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2103 }
2104
2105 // Much of the state in the QMetalCommandBuffer is going to be reset
2106 // when we get a new render encoder. Save what we need. (cheaper than
2107 // starting to walk over the srb again)
2109
2111
2112 // Step 3: tessellation evaluation (as vertex) + fragment shader
2113 {
2114 // No need to call tess.teseFragRenderPipeline because it was done
2115 // once and we know the result is stored in the standard place
2116 // (graphicsPipeline->d->ps).
2117
2118 graphicsPipeline->makeActiveForCurrentRenderPassEncoder(cbD);
2120
2123
2125 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2126 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2127 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2128
2129 if (outputBufferBinding >= 0 && tescOutBuf)
2130 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2131
2132 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2133 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2134
2135 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2136 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2137 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2138 }
2139
2140 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2141 patchStart: 0
2142 patchCount: patchCount
2143 patchIndexBuffer: nil
2144 patchIndexBufferOffset: 0
2145 instanceCount: 1
2146 baseInstance: 0];
2147 }
2148}
2149
2151{
2153 const int multiViewCount = cbD->currentGraphicsPipeline->m_multiViewCount;
2154 if (multiViewCount <= 1)
2155 return;
2156
2158 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2159 if (viewMaskBufBinding == -1) {
2160 qWarning("No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2161 return;
2162 }
2163 struct {
2164 quint32 viewOffset;
2165 quint32 viewCount;
2166 } multiViewInfo;
2167 multiViewInfo.viewOffset = 0;
2168 multiViewInfo.viewCount = quint32(multiViewCount);
2169 QMetalBuffer *buf = cbD->currentGraphicsPipeline->d->extraBufMgr.acquireWorkBuffer(this, sizeof(multiViewInfo),
2171 if (buf) {
2172 id<MTLBuffer> mtlbuf = buf->d->buf[0];
2173 char *p = reinterpret_cast<char *>([mtlbuf contents]);
2174 memcpy(p, &multiViewInfo, sizeof(multiViewInfo));
2175 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2176 // The instance count is adjusted for layered rendering. The vertex shader is expected to contain something like:
2177 // uint gl_ViewIndex = spvViewMask[0] + (gl_InstanceIndex - gl_BaseInstance) % spvViewMask[1];
2178 // where spvViewMask is the buffer with multiViewInfo passed in above.
2179 *instanceCount *= multiViewCount;
2180 }
2181}
2182
2184 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2185{
2188
2191 a.cbD = cbD;
2193 a.draw.vertexCount = vertexCount;
2194 a.draw.instanceCount = instanceCount;
2195 a.draw.firstVertex = firstVertex;
2196 a.draw.firstInstance = firstInstance;
2198 return;
2199 }
2200
2202
2203 if (caps.baseVertexAndInstance) {
2205 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2206 } else {
2208 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2209 }
2210}
2211
2213 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2214{
2217
2218 if (!cbD->currentIndexBuffer)
2219 return;
2220
2221 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2222 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2223
2224 QMetalBuffer *ibufD = cbD->currentIndexBuffer;
2225 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2226
2229 a.cbD = cbD;
2231 a.drawIndexed.indexCount = indexCount;
2232 a.drawIndexed.instanceCount = instanceCount;
2233 a.drawIndexed.firstIndex = firstIndex;
2234 a.drawIndexed.vertexOffset = vertexOffset;
2235 a.drawIndexed.firstInstance = firstInstance;
2236 a.drawIndexed.indexBuffer = mtlibuf;
2238 return;
2239 }
2240
2242
2243 if (caps.baseVertexAndInstance) {
2244 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2245 indexCount: indexCount
2246 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2247 indexBuffer: mtlibuf
2248 indexBufferOffset: indexOffset
2250 baseVertex: vertexOffset
2251 baseInstance: firstInstance];
2252 } else {
2253 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2254 indexCount: indexCount
2255 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2256 indexBuffer: mtlibuf
2257 indexBufferOffset: indexOffset
2259 }
2260}
2261
2263{
2264 if (!debugMarkers)
2265 return;
2266
2267 NSString *str = [NSString stringWithUTF8String: name.constData()];
2270 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2271 else
2272 [cbD->d->cb pushDebugGroup: str];
2273}
2274
2276{
2277 if (!debugMarkers)
2278 return;
2279
2282 [cbD->d->currentRenderPassEncoder popDebugGroup];
2283 else
2284 [cbD->d->cb popDebugGroup];
2285}
2286
2288{
2289 if (!debugMarkers)
2290 return;
2291
2294 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2295}
2296
2298{
2299 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
2300}
2301
2303{
2304 Q_UNUSED(cb);
2305}
2306
2308{
2311}
2312
2314{
2316 return cbD->d->lastGpuTime;
2317}
2318
2320{
2321 Q_UNUSED(flags);
2322
2323 QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
2324 currentSwapChain = swapChainD;
2325 currentFrameSlot = swapChainD->currentFrameSlot;
2326
2327 // If we are too far ahead, block. This is also what ensures that any
2328 // resource used in the previous frame for this slot is now not in use
2329 // anymore by the GPU.
2330 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2331
2332 // Do this also for any other swapchain's commands with the same frame slot
2333 // While this reduces concurrency, it keeps resource usage safe: swapchain
2334 // A starting its frame 0, followed by swapchain B starting its own frame 0
2335 // will make B wait for A's frame 0 commands, so if a resource is written
2336 // in B's frame or when B checks for pending resource releases, that won't
2337 // mess up A's in-flight commands (as they are not in flight anymore).
2338 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2339 if (sc != swapChainD)
2340 sc->waitUntilCompleted(currentFrameSlot); // wait+signal
2341 }
2342
2343 [d->captureScope beginScope];
2344
2345 // Do not let the command buffer mess with the refcount of objects. We do
2346 // have a proper render loop and will manage lifetimes similarly to other
2347 // backends (Vulkan).
2348 swapChainD->cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
2349
2351 if (swapChainD->samples > 1) {
2352 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2353 colorAtt.needsDrawableForResolveTex = true;
2354 } else {
2355 colorAtt.needsDrawableForTex = true;
2356 }
2357
2358 swapChainD->rtWrapper.d->fb.colorAtt[0] = colorAtt;
2359 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2360 swapChainD->rtWrapper.d->fb.hasStencil = swapChainD->ds ? true : false;
2361 swapChainD->rtWrapper.d->fb.depthNeedsStore = false;
2362
2363 if (swapChainD->ds)
2365
2367 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2368 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2370
2371 return QRhi::FrameOpSuccess;
2372}
2373
2375{
2376 QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
2377 Q_ASSERT(currentSwapChain == swapChainD);
2378
2379 __block int thisFrameSlot = currentFrameSlot;
2380 [swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2381 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2382 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2383 }];
2384
2385 const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
2386 const bool presentsWithTransaction = swapChainD->d->layer.presentsWithTransaction;
2387 if (!presentsWithTransaction && needsPresent) {
2388 // beginFrame-endFrame without a render pass inbetween means there is no drawable.
2389 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable)
2390 [swapChainD->cbWrapper.d->cb presentDrawable: drawable];
2391 }
2392
2393 [swapChainD->cbWrapper.d->cb commit];
2394
2395 if (presentsWithTransaction && needsPresent) {
2396 // beginFrame-endFrame without a render pass inbetween means there is no drawable.
2397 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2398 // The layer has presentsWithTransaction set to true to avoid flicker on resizing,
2399 // so here it is important to follow what the Metal docs say when it comes to the
2400 // issuing the present.
2401 [swapChainD->cbWrapper.d->cb waitUntilScheduled];
2402 [drawable present];
2403 }
2404 }
2405
2406 // Must not hold on to the drawable, regardless of needsPresent
2407 [swapChainD->d->curDrawable release];
2408 swapChainD->d->curDrawable = nil;
2409
2410 [d->captureScope endScope];
2411
2412 if (needsPresent)
2413 swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2414
2415 swapChainD->frameCount += 1;
2416 currentSwapChain = nullptr;
2417 return QRhi::FrameOpSuccess;
2418}
2419
2421{
2422 Q_UNUSED(flags);
2423
2425
2426 for (QMetalSwapChain *sc : std::as_const(swapchains))
2427 sc->waitUntilCompleted(currentFrameSlot);
2428
2429 d->ofr.active = true;
2430 *cb = &d->ofr.cbWrapper;
2431 d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
2432
2435 d->ofr.lastGpuTime = 0;
2437
2438 return QRhi::FrameOpSuccess;
2439}
2440
2442{
2443 Q_UNUSED(flags);
2444 Q_ASSERT(d->ofr.active);
2445 d->ofr.active = false;
2446
2448 [cb commit];
2449
2450 // offscreen frames wait for completion, unlike swapchain ones
2451 [cb waitUntilCompleted];
2452
2453 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2454
2456
2457 return QRhi::FrameOpSuccess;
2458}
2459
2461{
2463 QMetalSwapChain *swapChainD = nullptr;
2464 if (inFrame) {
2465 if (d->ofr.active) {
2468 cb = d->ofr.cbWrapper.d->cb;
2469 } else {
2471 swapChainD = currentSwapChain;
2473 cb = swapChainD->cbWrapper.d->cb;
2474 }
2475 }
2476
2477 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2478 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2480 // no wait as this is the thing we're going to be commit below and
2481 // beginFrame decremented sem already and going to be signaled by endFrame
2482 continue;
2483 }
2484 sc->waitUntilCompleted(i);
2485 }
2486 }
2487
2488 if (cb) {
2489 [cb commit];
2490 [cb waitUntilCompleted];
2491 }
2492
2493 if (inFrame) {
2494 if (d->ofr.active) {
2495 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2496 d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
2497 } else {
2498 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2499 swapChainD->cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
2500 }
2501 }
2502
2504
2506
2507 return QRhi::FrameOpSuccess;
2508}
2509
2510MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthStencil,
2511 const QColor &colorClearValue,
2512 const QRhiDepthStencilClearValue &depthStencilClearValue,
2513 int colorAttCount)
2514{
2515 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2516 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2517 colorClearValue.alphaF());
2518
2519 for (uint i = 0; i < uint(colorAttCount); ++i) {
2520 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2521 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2522 rp.colorAttachments[i].clearColor = c;
2523 }
2524
2525 if (hasDepthStencil) {
2526 rp.depthAttachment.loadAction = MTLLoadActionClear;
2527 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2528 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2529 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2530 rp.depthAttachment.clearDepth = double(depthStencilClearValue.depthClearValue());
2531 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2532 }
2533
2534 return rp;
2535}
2536
2538{
2539 qsizetype size = 0;
2540 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
2541 subresDesc.data().size() : subresDesc.image().sizeInBytes();
2542 if (imageSizeBytes > 0)
2543 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2544 return size;
2545}
2546
2547void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr,
2548 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc,
2549 qsizetype *curOfs)
2550{
2551 const QPoint dp = subresDesc.destinationTopLeft();
2552 const QByteArray rawData = subresDesc.data();
2553 QImage img = subresDesc.image();
2554 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2556
2557 if (!img.isNull()) {
2558 const qsizetype fullImageSizeBytes = img.sizeInBytes();
2559 int w = img.width();
2560 int h = img.height();
2561 int bpl = img.bytesPerLine();
2562 int srcOffset = 0;
2563
2564 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2565 const int sx = subresDesc.sourceTopLeft().x();
2566 const int sy = subresDesc.sourceTopLeft().y();
2567 if (!subresDesc.sourceSize().isEmpty()) {
2568 w = subresDesc.sourceSize().width();
2569 h = subresDesc.sourceSize().height();
2570 }
2571 if (img.depth() == 32) {
2572 memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
2573 srcOffset = sy * bpl + sx * 4;
2574 // bpl remains set to the original image's row stride
2575 } else {
2576 img = img.copy(sx, sy, w, h);
2577 bpl = img.bytesPerLine();
2578 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
2579 memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
2580 }
2581 } else {
2582 memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
2583 }
2584
2585 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2586 sourceOffset: NSUInteger(*curOfs + srcOffset)
2587 sourceBytesPerRow: NSUInteger(bpl)
2588 sourceBytesPerImage: 0
2589 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2590 toTexture: texD->d->tex
2591 destinationSlice: NSUInteger(is3D ? 0 : layer)
2592 destinationLevel: NSUInteger(level)
2593 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2594 options: MTLBlitOptionNone];
2595
2596 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2597 } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
2598 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2599 const int subresw = subresSize.width();
2600 const int subresh = subresSize.height();
2601 int w, h;
2602 if (subresDesc.sourceSize().isEmpty()) {
2603 w = subresw;
2604 h = subresh;
2605 } else {
2606 w = subresDesc.sourceSize().width();
2607 h = subresDesc.sourceSize().height();
2608 }
2609
2610 quint32 bpl = 0;
2611 QSize blockDim;
2612 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr, &blockDim);
2613
2614 const int dx = aligned(dp.x(), blockDim.width());
2615 const int dy = aligned(dp.y(), blockDim.height());
2616 if (dx + w != subresw)
2617 w = aligned(w, blockDim.width());
2618 if (dy + h != subresh)
2619 h = aligned(h, blockDim.height());
2620
2621 memcpy(reinterpret_cast<char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2622
2623 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2624 sourceOffset: NSUInteger(*curOfs)
2625 sourceBytesPerRow: bpl
2626 sourceBytesPerImage: 0
2627 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2628 toTexture: texD->d->tex
2629 destinationSlice: NSUInteger(is3D ? 0 : layer)
2630 destinationLevel: NSUInteger(level)
2631 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
2632 options: MTLBlitOptionNone];
2633
2634 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2635 } else if (!rawData.isEmpty()) {
2636 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2637 const int subresw = subresSize.width();
2638 const int subresh = subresSize.height();
2639 int w, h;
2640 if (subresDesc.sourceSize().isEmpty()) {
2641 w = subresw;
2642 h = subresh;
2643 } else {
2644 w = subresDesc.sourceSize().width();
2645 h = subresDesc.sourceSize().height();
2646 }
2647
2648 quint32 bpl = 0;
2649 if (subresDesc.dataStride())
2650 bpl = subresDesc.dataStride();
2651 else
2652 textureFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr, nullptr);
2653
2654 memcpy(reinterpret_cast<char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2655
2656 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2657 sourceOffset: NSUInteger(*curOfs)
2658 sourceBytesPerRow: bpl
2659 sourceBytesPerImage: 0
2660 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2661 toTexture: texD->d->tex
2662 destinationSlice: NSUInteger(is3D ? 0 : layer)
2663 destinationLevel: NSUInteger(level)
2664 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2665 options: MTLBlitOptionNone];
2666
2667 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2668 } else {
2669 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2670 }
2671}
2672
2674{
2677
2678 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
2683 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2684 if (u.offset == 0 && u.data.size() == bufD->m_size)
2685 bufD->d->pendingUpdates[i].clear();
2686 bufD->d->pendingUpdates[i].append({ u.offset, u.data });
2687 }
2689 // Due to the Metal API the handling of static and dynamic buffers is
2690 // basically the same. So go through the same pendingUpdates machinery.
2693 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
2694 for (int i = 0, ie = bufD->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i)
2695 bufD->d->pendingUpdates[i].append({ u.offset, u.data });
2697 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
2699 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
2700 if (bufD->m_type == QRhiBuffer::Dynamic) {
2701 char *p = reinterpret_cast<char *>([bufD->d->buf[idx] contents]);
2702 if (p) {
2703 u.result->data.resize(u.readSize);
2704 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
2705 }
2706 if (u.result->completed)
2707 u.result->completed();
2708 } else {
2710 readback.activeFrameSlot = idx;
2711 readback.buf = bufD->d->buf[idx];
2712 readback.offset = u.offset;
2713 readback.readSize = u.readSize;
2714 readback.result = u.result;
2715 d->activeBufferReadbacks.append(readback);
2716 }
2717 }
2718 }
2719
2720 id<MTLBlitCommandEncoder> blitEnc = nil;
2721 auto ensureBlit = [&blitEnc, cbD, this] {
2722 if (!blitEnc) {
2723 blitEnc = [cbD->d->cb blitCommandEncoder];
2724 if (debugMarkers)
2725 [blitEnc pushDebugGroup: @"Texture upload/copy"];
2726 }
2727 };
2728
2729 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
2730 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
2732 QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst);
2733 qsizetype stagingSize = 0;
2734 for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2735 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2736 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2737 stagingSize += subresUploadByteSize(subresDesc);
2738 }
2739 }
2740
2741 ensureBlit();
2742 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
2743 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
2744 options: MTLResourceStorageModeShared];
2745
2746 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
2747 qsizetype curOfs = 0;
2748 for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2749 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2750 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2751 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
2752 }
2753 }
2754
2755 utexD->lastActiveFrameSlot = currentFrameSlot;
2756
2759 e.lastActiveFrameSlot = currentFrameSlot;
2760 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
2761 utexD->d->stagingBuf[currentFrameSlot] = nil;
2762 d->releaseQueue.append(e);
2764 Q_ASSERT(u.src && u.dst);
2765 QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.src);
2766 QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.dst);
2767 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2768 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2769 const QPoint dp = u.desc.destinationTopLeft();
2770 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2771 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2772 const QPoint sp = u.desc.sourceTopLeft();
2773
2774 ensureBlit();
2775 [blitEnc copyFromTexture: srcD->d->tex
2776 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
2777 sourceLevel: NSUInteger(u.desc.sourceLevel())
2778 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
2779 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
2780 toTexture: dstD->d->tex
2781 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
2782 destinationLevel: NSUInteger(u.desc.destinationLevel())
2783 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
2784
2785 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
2788 readback.activeFrameSlot = currentFrameSlot;
2789 readback.desc = u.rb;
2790 readback.result = u.result;
2791
2792 QMetalTexture *texD = QRHI_RES(QMetalTexture, u.rb.texture());
2793 QMetalSwapChain *swapChainD = nullptr;
2795 QSize srcSize;
2796 bool is3D = false;
2797 if (texD) {
2798 if (texD->samples > 1) {
2799 qWarning("Multisample texture cannot be read back");
2800 continue;
2801 }
2802 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2803 readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
2804 readback.format = texD->m_format;
2805 src = texD->d->tex;
2806 srcSize = readback.pixelSize;
2807 texD->lastActiveFrameSlot = currentFrameSlot;
2808 } else {
2809 Q_ASSERT(currentSwapChain);
2810 swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain);
2811 readback.pixelSize = swapChainD->pixelSize;
2812 readback.format = swapChainD->d->rhiColorFormat;
2813 // Multisample swapchains need nothing special since resolving
2814 // happens when ending a renderpass.
2815 const QMetalRenderTargetData::ColorAtt &colorAtt(swapChainD->rtWrapper.d->fb.colorAtt[0]);
2816 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
2817 srcSize = swapChainD->rtWrapper.d->pixelSize;
2818 }
2819
2820 quint32 bpl = 0;
2821 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize, nullptr);
2822 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
2823
2824 ensureBlit();
2825 [blitEnc copyFromTexture: src
2826 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
2827 sourceLevel: NSUInteger(u.rb.level())
2828 sourceOrigin: MTLOriginMake(0, 0, is3D ? u.rb.layer() : 0)
2829 sourceSize: MTLSizeMake(NSUInteger(srcSize.width()), NSUInteger(srcSize.height()), 1)
2830 toBuffer: readback.buf
2831 destinationOffset: 0
2832 destinationBytesPerRow: bpl
2833 destinationBytesPerImage: 0
2834 options: MTLBlitOptionNone];
2835
2836 d->activeTextureReadbacks.append(readback);
2838 QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst);
2839 ensureBlit();
2840 [blitEnc generateMipmapsForTexture: utexD->d->tex];
2841 utexD->lastActiveFrameSlot = currentFrameSlot;
2842 }
2843 }
2844
2845 if (blitEnc) {
2846 if (debugMarkers)
2847 [blitEnc popDebugGroup];
2848 [blitEnc endEncoding];
2849 }
2850
2851 ud->free();
2852}
2853
2854// this handles all types of buffers, not just Dynamic
2856{
2857 if (bufD->d->pendingUpdates[slot].isEmpty())
2858 return;
2859
2860 void *p = [bufD->d->buf[slot] contents];
2861 quint32 changeBegin = UINT32_MAX;
2862 quint32 changeEnd = 0;
2863 for (const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
2864 memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
2865 if (u.offset < changeBegin)
2866 changeBegin = u.offset;
2867 if (u.offset + u.data.size() > changeEnd)
2868 changeEnd = u.offset + u.data.size();
2869 }
2870#ifdef Q_OS_MACOS
2871 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
2872 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
2873#endif
2874
2875 bufD->d->pendingUpdates[slot].clear();
2876}
2877
2879{
2881}
2882
2884{
2886
2887 enqueueResourceUpdates(cb, resourceUpdates);
2888}
2889
2891 QRhiRenderTarget *rt,
2892 const QColor &colorClearValue,
2893 const QRhiDepthStencilClearValue &depthStencilClearValue,
2894 QRhiResourceUpdateBatch *resourceUpdates,
2895 QRhiCommandBuffer::BeginPassFlags)
2896{
2899
2900 if (resourceUpdates)
2901 enqueueResourceUpdates(cb, resourceUpdates);
2902
2903 QMetalRenderTargetData *rtD = nullptr;
2904 switch (rt->resourceType()) {
2907 cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
2908 if (rtD->colorAttCount) {
2910 if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
2913 if (!swapChainD->d->curDrawable) {
2915 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
2916 }
2917 if (!swapChainD->d->curDrawable) {
2918 qWarning("No drawable");
2919 return;
2920 }
2921 id<MTLTexture> scTex = swapChainD->d->curDrawable.texture;
2922 if (color0.needsDrawableForTex) {
2923 color0.tex = scTex;
2924 color0.needsDrawableForTex = false;
2925 } else {
2926 color0.resolveTex = scTex;
2927 color0.needsDrawableForResolveTex = false;
2928 }
2929 }
2930 }
2931 break;
2933 {
2935 rtD = rtTex->d;
2936 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
2937 rtTex->create();
2938 cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
2940 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
2941 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2942 }
2944 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2945 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2946 }
2947 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
2948 it != itEnd; ++it)
2949 {
2950 if (it->texture()) {
2951 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
2952 if (it->multiViewCount() >= 2)
2953 cbD->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
2954 } else if (it->renderBuffer()) {
2955 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
2956 }
2957 if (it->resolveTexture())
2958 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
2959 }
2960 if (rtTex->m_desc.depthStencilBuffer())
2961 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
2962 if (rtTex->m_desc.depthTexture())
2963 QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture())->lastActiveFrameSlot = currentFrameSlot;
2964 }
2965 break;
2966 default:
2967 Q_UNREACHABLE();
2968 break;
2969 }
2970
2971 for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
2972 cbD->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
2973 cbD->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
2974 cbD->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
2975 cbD->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
2976 if (rtD->fb.colorAtt[i].resolveTex) {
2977 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionMultisampleResolve;
2978 cbD->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
2979 cbD->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
2980 cbD->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
2981 }
2982 }
2983
2984 if (rtD->dsAttCount) {
2985 Q_ASSERT(rtD->fb.dsTex);
2986 cbD->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
2987 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
2988 if (rtD->fb.depthNeedsStore) // Depth/Stencil is set to DontCare by default, override if needed
2989 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
2990 }
2991
2992 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2993
2994 cbD->resetPerPassState();
2995
2997 cbD->currentTarget = rt;
2998}
2999
3001{
3004
3005 [cbD->d->currentRenderPassEncoder endEncoding];
3006
3008 cbD->currentTarget = nullptr;
3009
3010 if (resourceUpdates)
3011 enqueueResourceUpdates(cb, resourceUpdates);
3012}
3013
3015 QRhiResourceUpdateBatch *resourceUpdates,
3016 QRhiCommandBuffer::BeginPassFlags)
3017{
3020
3021 if (resourceUpdates)
3022 enqueueResourceUpdates(cb, resourceUpdates);
3023
3024 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3025 cbD->resetPerPassState();
3027}
3028
3030{
3033
3034 [cbD->d->currentComputePassEncoder endEncoding];
3036
3037 if (resourceUpdates)
3038 enqueueResourceUpdates(cb, resourceUpdates);
3039}
3040
3042{
3046
3047 if (cbD->currentComputePipeline != psD || cbD->currentPipelineGeneration != psD->generation) {
3048 cbD->currentGraphicsPipeline = nullptr;
3049 cbD->currentComputePipeline = psD;
3051
3052 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3053 }
3054
3056}
3057
3059{
3063
3064 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3065 threadsPerThreadgroup: psD->d->localSize];
3066}
3067
3069{
3070 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3071 [e.buffer.buffers[i] release];
3072}
3073
3075{
3076 [e.renderbuffer.texture release];
3077}
3078
3080{
3081 [e.texture.texture release];
3082 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3083 [e.texture.stagingBuffers[i] release];
3084 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3085 [e.texture.views[i] release];
3086}
3087
3089{
3090 [e.sampler.samplerState release];
3091}
3092
3094{
3095 for (int i = d->releaseQueue.count() - 1; i >= 0; --i) {
3097 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3098 switch (e.type) {
3101 break;
3104 break;
3107 break;
3110 break;
3112 [e.stagingBuffer.buffer release];
3113 break;
3115 [e.graphicsPipeline.pipelineState release];
3116 [e.graphicsPipeline.depthStencilState release];
3117 [e.graphicsPipeline.tessVertexComputeState[0] release];
3118 [e.graphicsPipeline.tessVertexComputeState[1] release];
3119 [e.graphicsPipeline.tessVertexComputeState[2] release];
3120 [e.graphicsPipeline.tessTessControlComputeState release];
3121 break;
3123 [e.computePipeline.pipelineState release];
3124 break;
3125 default:
3126 break;
3127 }
3128 d->releaseQueue.removeAt(i);
3129 }
3130 }
3131}
3132
3134{
3135 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
3136
3137 for (int i = d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3139 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3140 readback.result->format = readback.format;
3141 readback.result->pixelSize = readback.pixelSize;
3142 readback.result->data.resize(int(readback.bufSize));
3143 void *p = [readback.buf contents];
3144 memcpy(readback.result->data.data(), p, readback.bufSize);
3145 [readback.buf release];
3146
3147 if (readback.result->completed)
3148 completedCallbacks.append(readback.result->completed);
3149
3150 d->activeTextureReadbacks.remove(i);
3151 }
3152 }
3153
3154 for (int i = d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3156 if (forced || currentFrameSlot == readback.activeFrameSlot
3157 || readback.activeFrameSlot < 0) {
3158 readback.result->data.resize(readback.readSize);
3159 char *p = reinterpret_cast<char *>([readback.buf contents]);
3160 Q_ASSERT(p);
3161 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3162
3163 if (readback.result->completed)
3164 completedCallbacks.append(readback.result->completed);
3165
3166 d->activeBufferReadbacks.remove(i);
3167 }
3168 }
3169
3170 for (auto f : completedCallbacks)
3171 f();
3172}
3173
3175 : QRhiBuffer(rhi, type, usage, size),
3176 d(new QMetalBufferData)
3177{
3178 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3179 d->buf[i] = nil;
3180}
3181
3183{
3184 destroy();
3185 delete d;
3186}
3187
3189{
3190 if (!d->buf[0])
3191 return;
3192
3195 e.lastActiveFrameSlot = lastActiveFrameSlot;
3196
3197 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3198 e.buffer.buffers[i] = d->buf[i];
3199 d->buf[i] = nil;
3200 d->pendingUpdates[i].clear();
3201 }
3202
3204 if (rhiD) {
3205 rhiD->d->releaseQueue.append(e);
3206 rhiD->unregisterResource(this);
3207 }
3208}
3209
3211{
3212 if (d->buf[0])
3213 destroy();
3214
3215 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3216 qWarning("StorageBuffer cannot be combined with Dynamic");
3217 return false;
3218 }
3219
3220 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3221 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3222
3223 d->managed = false;
3224 MTLResourceOptions opts = MTLResourceStorageModeShared;
3225
3227#ifdef Q_OS_MACOS
3228 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3229 opts = MTLResourceStorageModeManaged;
3230 d->managed = true;
3231 }
3232#endif
3233
3234 // Have QMTL_FRAMES_IN_FLIGHT versions regardless of the type, for now.
3235 // This is because writing to a Managed buffer (which is what Immutable and
3236 // Static maps to on macOS) is not safe when another frame reading from the
3237 // same buffer is still in flight.
3238 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer); // except for SSBOs written in the shader
3239 // and a special case for internal work buffers
3240 if (int(m_usage) == WorkBufPoolUsage)
3241 d->slotted = false;
3242
3243 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3244 if (i == 0 || d->slotted) {
3245 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3246 if (!m_objectName.isEmpty()) {
3247 if (!d->slotted) {
3248 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3249 } else {
3251 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3252 }
3253 }
3254 }
3255 }
3256
3258 generation += 1;
3259 rhiD->registerResource(this);
3260 return true;
3261}
3262
3264{
3265 if (d->slotted) {
3267 Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QMTL_FRAMES_IN_FLIGHT));
3268 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3270 rhiD->executeBufferHostWritesForSlot(this, i);
3271 b.objects[i] = &d->buf[i];
3272 }
3273 b.slotCount = QMTL_FRAMES_IN_FLIGHT;
3274 return b;
3275 }
3276 return { { &d->buf[0] }, 1 };
3277}
3278
3280{
3281 // Shortcut the entire buffer update mechanism and allow the client to do
3282 // the host writes directly to the buffer. This will lead to unexpected
3283 // results when combined with QRhiResourceUpdateBatch-based updates for the
3284 // buffer, but provides a fast path for dynamic buffers that have all their
3285 // content changed in every frame.
3288 Q_ASSERT(rhiD->inFrame);
3289 const int slot = rhiD->currentFrameSlot;
3290 void *p = [d->buf[slot] contents];
3291 return static_cast<char *>(p);
3292}
3293
3295{
3296#ifdef Q_OS_MACOS
3297 if (d->managed) {
3299 const int slot = rhiD->currentFrameSlot;
3300 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3301 }
3302#endif
3303}
3304
3305static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags, const QRhiMetal *d)
3306{
3307#ifndef Q_OS_MACOS
3308 Q_UNUSED(d);
3309#endif
3310
3311 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3312 switch (format) {
3313 case QRhiTexture::RGBA8:
3314 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3315 case QRhiTexture::BGRA8:
3316 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3317 case QRhiTexture::R8:
3318#ifdef Q_OS_MACOS
3319 return MTLPixelFormatR8Unorm;
3320#else
3321 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3322#endif
3323 case QRhiTexture::RG8:
3324#ifdef Q_OS_MACOS
3325 return MTLPixelFormatRG8Unorm;
3326#else
3327 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3328#endif
3329 case QRhiTexture::R16:
3330 return MTLPixelFormatR16Unorm;
3331 case QRhiTexture::RG16:
3332 return MTLPixelFormatRG16Unorm;
3334 return MTLPixelFormatR8Unorm;
3335
3337 return MTLPixelFormatRGBA16Float;
3339 return MTLPixelFormatRGBA32Float;
3340 case QRhiTexture::R16F:
3341 return MTLPixelFormatR16Float;
3342 case QRhiTexture::R32F:
3343 return MTLPixelFormatR32Float;
3344
3346 return MTLPixelFormatRGB10A2Unorm;
3347
3348#ifdef Q_OS_MACOS
3349 case QRhiTexture::D16:
3350 return MTLPixelFormatDepth16Unorm;
3351 case QRhiTexture::D24:
3352 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3353 case QRhiTexture::D24S8:
3354 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3355#else
3356 case QRhiTexture::D16:
3357 return MTLPixelFormatDepth32Float;
3358 case QRhiTexture::D24:
3359 return MTLPixelFormatDepth32Float;
3360 case QRhiTexture::D24S8:
3361 return MTLPixelFormatDepth32Float_Stencil8;
3362#endif
3363 case QRhiTexture::D32F:
3364 return MTLPixelFormatDepth32Float;
3365
3366#ifdef Q_OS_MACOS
3367 case QRhiTexture::BC1:
3368 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3369 case QRhiTexture::BC2:
3370 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3371 case QRhiTexture::BC3:
3372 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3373 case QRhiTexture::BC4:
3374 return MTLPixelFormatBC4_RUnorm;
3375 case QRhiTexture::BC5:
3376 qWarning("QRhiMetal does not support BC5");
3377 return MTLPixelFormatInvalid;
3378 case QRhiTexture::BC6H:
3379 return MTLPixelFormatBC6H_RGBUfloat;
3380 case QRhiTexture::BC7:
3381 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3382#else
3383 case QRhiTexture::BC1:
3384 case QRhiTexture::BC2:
3385 case QRhiTexture::BC3:
3386 case QRhiTexture::BC4:
3387 case QRhiTexture::BC5:
3388 case QRhiTexture::BC6H:
3389 case QRhiTexture::BC7:
3390 qWarning("QRhiMetal: BCx compression not supported on this platform");
3391 return MTLPixelFormatInvalid;
3392#endif
3393
3394#ifndef Q_OS_MACOS
3396 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3398 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3400 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3401
3403 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3405 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3407 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3409 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3411 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3413 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3415 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3417 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3419 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3421 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3423 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3425 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3427 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3429 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3430#else
3432 if (d->caps.isAppleGPU) {
3433 if (@available(macOS 11.0, *))
3434 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3435 }
3436 qWarning("QRhiMetal: ETC2 compression not supported on this platform");
3437 return MTLPixelFormatInvalid;
3439 if (d->caps.isAppleGPU) {
3440 if (@available(macOS 11.0, *))
3441 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3442 }
3443 qWarning("QRhiMetal: ETC2 compression not supported on this platform");
3444 return MTLPixelFormatInvalid;
3446 if (d->caps.isAppleGPU) {
3447 if (@available(macOS 11.0, *))
3448 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3449 }
3450 qWarning("QRhiMetal: ETC2 compression not supported on this platform");
3451 return MTLPixelFormatInvalid;
3453 if (d->caps.isAppleGPU) {
3454 if (@available(macOS 11.0, *))
3455 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3456 }
3457 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3458 return MTLPixelFormatInvalid;
3460 if (d->caps.isAppleGPU) {
3461 if (@available(macOS 11.0, *))
3462 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3463 }
3464 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3465 return MTLPixelFormatInvalid;
3467 if (d->caps.isAppleGPU) {
3468 if (@available(macOS 11.0, *))
3469 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3470 }
3471 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3472 return MTLPixelFormatInvalid;
3474 if (d->caps.isAppleGPU) {
3475 if (@available(macOS 11.0, *))
3476 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3477 }
3478 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3479 return MTLPixelFormatInvalid;
3481 if (d->caps.isAppleGPU) {
3482 if (@available(macOS 11.0, *))
3483 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3484 }
3485 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3486 return MTLPixelFormatInvalid;
3488 if (d->caps.isAppleGPU) {
3489 if (@available(macOS 11.0, *))
3490 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3491 }
3492 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3493 return MTLPixelFormatInvalid;
3495 if (d->caps.isAppleGPU) {
3496 if (@available(macOS 11.0, *))
3497 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3498 }
3499 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3500 return MTLPixelFormatInvalid;
3502 if (d->caps.isAppleGPU) {
3503 if (@available(macOS 11.0, *))
3504 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3505 }
3506 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3507 return MTLPixelFormatInvalid;
3509 if (d->caps.isAppleGPU) {
3510 if (@available(macOS 11.0, *))
3511 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3512 }
3513 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3514 return MTLPixelFormatInvalid;
3516 if (d->caps.isAppleGPU) {
3517 if (@available(macOS 11.0, *))
3518 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3519 }
3520 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3521 return MTLPixelFormatInvalid;
3523 if (d->caps.isAppleGPU) {
3524 if (@available(macOS 11.0, *))
3525 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3526 }
3527 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3528 return MTLPixelFormatInvalid;
3530 if (d->caps.isAppleGPU) {
3531 if (@available(macOS 11.0, *))
3532 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3533 }
3534 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3535 return MTLPixelFormatInvalid;
3537 if (d->caps.isAppleGPU) {
3538 if (@available(macOS 11.0, *))
3539 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3540 }
3541 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3542 return MTLPixelFormatInvalid;
3544 if (d->caps.isAppleGPU) {
3545 if (@available(macOS 11.0, *))
3546 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3547 }
3548 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3549 return MTLPixelFormatInvalid;
3550#endif
3551
3552 default:
3553 Q_UNREACHABLE();
3554 return MTLPixelFormatInvalid;
3555 }
3556}
3557
3559 int sampleCount, QRhiRenderBuffer::Flags flags,
3560 QRhiTexture::Format backingFormatHint)
3561 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint),
3563{
3564}
3565
3567{
3568 destroy();
3569 delete d;
3570}
3571
3573{
3574 if (!d->tex)
3575 return;
3576
3579 e.lastActiveFrameSlot = lastActiveFrameSlot;
3580
3581 e.renderbuffer.texture = d->tex;
3582 d->tex = nil;
3583
3585 if (rhiD) {
3586 rhiD->d->releaseQueue.append(e);
3587 rhiD->unregisterResource(this);
3588 }
3589}
3590
3592{
3593 if (d->tex)
3594 destroy();
3595
3596 if (m_pixelSize.isEmpty())
3597 return false;
3598
3600 samples = rhiD->effectiveSampleCount(m_sampleCount);
3601
3602 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3603 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3604 desc.width = NSUInteger(m_pixelSize.width());
3605 desc.height = NSUInteger(m_pixelSize.height());
3606 if (samples > 1)
3607 desc.sampleCount = NSUInteger(samples);
3608 desc.resourceOptions = MTLResourceStorageModePrivate;
3609 desc.usage = MTLTextureUsageRenderTarget;
3610
3611 switch (m_type) {
3612 case DepthStencil:
3613#ifdef Q_OS_MACOS
3614 if (rhiD->caps.isAppleGPU) {
3615 if (@available(macOS 11.0, *)) {
3616 desc.storageMode = MTLStorageModeMemoryless;
3617 d->format = MTLPixelFormatDepth32Float_Stencil8;
3618 } else {
3619 Q_UNREACHABLE();
3620 }
3621 } else {
3622 desc.storageMode = MTLStorageModePrivate;
3623 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
3624 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3625 }
3626#else
3627 desc.storageMode = MTLStorageModeMemoryless;
3628 d->format = MTLPixelFormatDepth32Float_Stencil8;
3629#endif
3630 desc.pixelFormat = d->format;
3631 break;
3632 case Color:
3633 desc.storageMode = MTLStorageModePrivate;
3636 else
3637 d->format = MTLPixelFormatRGBA8Unorm;
3638 desc.pixelFormat = d->format;
3639 break;
3640 default:
3641 Q_UNREACHABLE();
3642 break;
3643 }
3644
3645 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3646 [desc release];
3647
3648 if (!m_objectName.isEmpty())
3649 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3650
3652 generation += 1;
3653 rhiD->registerResource(this);
3654 return true;
3655}
3656
3658{
3660 return m_backingFormatHint;
3661 else
3663}
3664
3666 int arraySize, int sampleCount, Flags flags)
3667 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags),
3669{
3670 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3671 d->stagingBuf[i] = nil;
3672
3673 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3674 d->perLevelViews[i] = nil;
3675}
3676
3678{
3679 destroy();
3680 delete d;
3681}
3682
3684{
3685 if (!d->tex)
3686 return;
3687
3690 e.lastActiveFrameSlot = lastActiveFrameSlot;
3691
3692 e.texture.texture = d->owns ? d->tex : nil;
3693 d->tex = nil;
3694
3695 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3696 e.texture.stagingBuffers[i] = d->stagingBuf[i];
3697 d->stagingBuf[i] = nil;
3698 }
3699
3700 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
3701 e.texture.views[i] = d->perLevelViews[i];
3702 d->perLevelViews[i] = nil;
3703 }
3704
3706 if (rhiD) {
3707 rhiD->d->releaseQueue.append(e);
3708 rhiD->unregisterResource(this);
3709 }
3710}
3711
3713{
3714 if (d->tex)
3715 destroy();
3716
3717 const bool isCube = m_flags.testFlag(CubeMap);
3718 const bool is3D = m_flags.testFlag(ThreeDimensional);
3719 const bool isArray = m_flags.testFlag(TextureArray);
3720 const bool hasMipMaps = m_flags.testFlag(MipMapped);
3721 const bool is1D = m_flags.testFlag(OneDimensional);
3722
3723 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
3724 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
3725
3728 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
3729 samples = rhiD->effectiveSampleCount(m_sampleCount);
3730 if (samples > 1) {
3731 if (isCube) {
3732 qWarning("Cubemap texture cannot be multisample");
3733 return false;
3734 }
3735 if (is3D) {
3736 qWarning("3D texture cannot be multisample");
3737 return false;
3738 }
3739 if (hasMipMaps) {
3740 qWarning("Multisample texture cannot have mipmaps");
3741 return false;
3742 }
3743 }
3744 if (isCube && is3D) {
3745 qWarning("Texture cannot be both cube and 3D");
3746 return false;
3747 }
3748 if (isArray && is3D) {
3749 qWarning("Texture cannot be both array and 3D");
3750 return false;
3751 }
3752 if (is1D && is3D) {
3753 qWarning("Texture cannot be both 1D and 3D");
3754 return false;
3755 }
3756 if (is1D && isCube) {
3757 qWarning("Texture cannot be both 1D and cube");
3758 return false;
3759 }
3760 if (m_depth > 1 && !is3D) {
3761 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
3762 return false;
3763 }
3764 if (m_arraySize > 0 && !isArray) {
3765 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
3766 return false;
3767 }
3768 if (m_arraySize < 1 && isArray) {
3769 qWarning("Texture is an array but array size is %d", m_arraySize);
3770 return false;
3771 }
3772
3773 if (adjustedSize)
3774 *adjustedSize = size;
3775
3776 return true;
3777}
3778
3780{
3781 QSize size;
3782 if (!prepareCreate(&size))
3783 return false;
3784
3785 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3786
3787 const bool isCube = m_flags.testFlag(CubeMap);
3788 const bool is3D = m_flags.testFlag(ThreeDimensional);
3789 const bool isArray = m_flags.testFlag(TextureArray);
3790 const bool is1D = m_flags.testFlag(OneDimensional);
3791 if (isCube) {
3792 desc.textureType = MTLTextureTypeCube;
3793 } else if (is3D) {
3794 desc.textureType = MTLTextureType3D;
3795 } else if (is1D) {
3796 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
3797 } else if (isArray) {
3798#ifdef Q_OS_IOS
3799 if (@available(iOS 14, *)) {
3800 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
3801 } else {
3802 desc.textureType = MTLTextureType2DArray;
3803 }
3804#else
3805 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
3806#endif
3807 } else {
3808 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3809 }
3810 desc.pixelFormat = d->format;
3811 desc.width = NSUInteger(size.width());
3812 desc.height = NSUInteger(size.height());
3813 desc.depth = is3D ? qMax(1, m_depth) : 1;
3814 desc.mipmapLevelCount = NSUInteger(mipLevelCount);
3815 if (samples > 1)
3816 desc.sampleCount = NSUInteger(samples);
3817 if (isArray)
3818 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
3819 desc.resourceOptions = MTLResourceStorageModePrivate;
3820 desc.storageMode = MTLStorageModePrivate;
3821 desc.usage = MTLTextureUsageShaderRead;
3822 if (m_flags.testFlag(RenderTarget))
3823 desc.usage |= MTLTextureUsageRenderTarget;
3824 if (m_flags.testFlag(UsedWithLoadStore))
3825 desc.usage |= MTLTextureUsageShaderWrite;
3826
3828 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3829 [desc release];
3830
3831 if (!m_objectName.isEmpty())
3832 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3833
3834 d->owns = true;
3835
3837 generation += 1;
3838 rhiD->registerResource(this);
3839 return true;
3840}
3841
3843{
3844 id<MTLTexture> tex = id<MTLTexture>(src.object);
3845 if (tex == 0)
3846 return false;
3847
3848 if (!prepareCreate())
3849 return false;
3850
3851 d->tex = tex;
3852
3853 d->owns = false;
3854
3856 generation += 1;
3858 rhiD->registerResource(this);
3859 return true;
3860}
3861
3863{
3864 return {quint64(d->tex), 0};
3865}
3866
3868{
3869 Q_ASSERT(level >= 0 && level < int(q->mipLevelCount));
3870 if (perLevelViews[level])
3871 return perLevelViews[level];
3872
3873 const MTLTextureType type = [tex textureType];
3874 const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap);
3875 const bool isArray = q->m_flags.testFlag(QRhiTexture::TextureArray);
3876 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
3877 levels: NSMakeRange(NSUInteger(level), 1)
3878 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
3879
3881 return view;
3882}
3883
3886 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w),
3887 d(new QMetalSamplerData)
3888{
3889}
3890
3892{
3893 destroy();
3894 delete d;
3895}
3896
3898{
3899 if (!d->samplerState)
3900 return;
3901
3904 e.lastActiveFrameSlot = lastActiveFrameSlot;
3905
3906 e.sampler.samplerState = d->samplerState;
3907 d->samplerState = nil;
3908
3910 if (rhiD) {
3911 rhiD->d->releaseQueue.append(e);
3912 rhiD->unregisterResource(this);
3913 }
3914}
3915
3916static inline MTLSamplerMinMagFilter toMetalFilter(QRhiSampler::Filter f)
3917{
3918 switch (f) {
3920 return MTLSamplerMinMagFilterNearest;
3922 return MTLSamplerMinMagFilterLinear;
3923 default:
3924 Q_UNREACHABLE();
3925 return MTLSamplerMinMagFilterNearest;
3926 }
3927}
3928
3929static inline MTLSamplerMipFilter toMetalMipmapMode(QRhiSampler::Filter f)
3930{
3931 switch (f) {
3932 case QRhiSampler::None:
3933 return MTLSamplerMipFilterNotMipmapped;
3935 return MTLSamplerMipFilterNearest;
3937 return MTLSamplerMipFilterLinear;
3938 default:
3939 Q_UNREACHABLE();
3940 return MTLSamplerMipFilterNotMipmapped;
3941 }
3942}
3943
3944static inline MTLSamplerAddressMode toMetalAddressMode(QRhiSampler::AddressMode m)
3945{
3946 switch (m) {
3948 return MTLSamplerAddressModeRepeat;
3950 return MTLSamplerAddressModeClampToEdge;
3952 return MTLSamplerAddressModeMirrorRepeat;
3953 default:
3954 Q_UNREACHABLE();
3955 return MTLSamplerAddressModeClampToEdge;
3956 }
3957}
3958
3959static inline MTLCompareFunction toMetalTextureCompareFunction(QRhiSampler::CompareOp op)
3960{
3961 switch (op) {
3962 case QRhiSampler::Never:
3963 return MTLCompareFunctionNever;
3964 case QRhiSampler::Less:
3965 return MTLCompareFunctionLess;
3966 case QRhiSampler::Equal:
3967 return MTLCompareFunctionEqual;
3969 return MTLCompareFunctionLessEqual;
3971 return MTLCompareFunctionGreater;
3973 return MTLCompareFunctionNotEqual;
3975 return MTLCompareFunctionGreaterEqual;
3977 return MTLCompareFunctionAlways;
3978 default:
3979 Q_UNREACHABLE();
3980 return MTLCompareFunctionNever;
3981 }
3982}
3983
3985{
3986 if (d->samplerState)
3987 destroy();
3988
3989 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
3990 desc.minFilter = toMetalFilter(m_minFilter);
3991 desc.magFilter = toMetalFilter(m_magFilter);
3992 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
3993 desc.sAddressMode = toMetalAddressMode(m_addressU);
3994 desc.tAddressMode = toMetalAddressMode(m_addressV);
3995 desc.rAddressMode = toMetalAddressMode(m_addressW);
3997
3999 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4000 [desc release];
4001
4003 generation += 1;
4004 rhiD->registerResource(this);
4005 return true;
4006}
4007
4008// dummy, no Vulkan-style RenderPass+Framebuffer concept here.
4009// We do have MTLRenderPassDescriptor of course, but it will be created on the fly for each pass.
4012{
4013 serializedFormatData.reserve(16);
4014}
4015
4017{
4018 destroy();
4019}
4020
4022{
4024 if (rhiD)
4025 rhiD->unregisterResource(this);
4026}
4027
4029{
4030 if (!other)
4031 return false;
4032
4034
4035 if (colorAttachmentCount != o->colorAttachmentCount)
4036 return false;
4037
4038 if (hasDepthStencil != o->hasDepthStencil)
4039 return false;
4040
4041 for (int i = 0; i < colorAttachmentCount; ++i) {
4042 if (colorFormat[i] != o->colorFormat[i])
4043 return false;
4044 }
4045
4046 if (hasDepthStencil) {
4047 if (dsFormat != o->dsFormat)
4048 return false;
4049 }
4050
4051 return true;
4052}
4053
4055{
4056 serializedFormatData.clear();
4057 auto p = std::back_inserter(serializedFormatData);
4058
4060 *p++ = hasDepthStencil;
4061 for (int i = 0; i < colorAttachmentCount; ++i)
4062 *p++ = colorFormat[i];
4063 *p++ = hasDepthStencil ? dsFormat : 0;
4064}
4065
4067{
4071 memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
4072 rpD->dsFormat = dsFormat;
4073
4075
4077 rhiD->registerResource(rpD, false);
4078 return rpD;
4079}
4080
4082{
4083 return serializedFormatData;
4084}
4085
4087 : QRhiSwapChainRenderTarget(rhi, swapchain),
4089{
4090}
4091
4093{
4094 destroy();
4095 delete d;
4096}
4097
4099{
4100 // nothing to do here
4101}
4102
4104{
4105 return d->pixelSize;
4106}
4107
4109{
4110 return d->dpr;
4111}
4112
4114{
4115 return d->sampleCount;
4116}
4117
4120 Flags flags)
4123{
4124}
4125
4127{
4128 destroy();
4129 delete d;
4130}
4131
4133{
4135 if (rhiD)
4136 rhiD->unregisterResource(this);
4137}
4138
4140{
4141 const int colorAttachmentCount = int(m_desc.colorAttachmentCount());
4143 rpD->colorAttachmentCount = colorAttachmentCount;
4145
4146 for (int i = 0; i < colorAttachmentCount; ++i) {
4147 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4148 QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAtt->texture());
4150 rpD->colorFormat[i] = int(texD ? texD->d->format : rbD->d->format);
4151 }
4152
4153 if (m_desc.depthTexture())
4154 rpD->dsFormat = int(QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4155 else if (m_desc.depthStencilBuffer())
4157
4159
4161 rhiD->registerResource(rpD, false);
4162 return rpD;
4163}
4164
4166{
4170 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4171
4172 d->colorAttCount = 0;
4173 int attIndex = 0;
4174 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4175 d->colorAttCount += 1;
4176 QMetalTexture *texD = QRHI_RES(QMetalTexture, it->texture());
4177 QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, it->renderBuffer());
4178 Q_ASSERT(texD || rbD);
4179 id<MTLTexture> dst = nil;
4180 bool is3D = false;
4181 if (texD) {
4182 dst = texD->d->tex;
4183 if (attIndex == 0) {
4184 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4185 d->sampleCount = texD->samples;
4186 }
4187 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4188 } else if (rbD) {
4189 dst = rbD->d->tex;
4190 if (attIndex == 0) {
4191 d->pixelSize = rbD->pixelSize();
4192 d->sampleCount = rbD->samples;
4193 }
4194 }
4196 colorAtt.tex = dst;
4197 colorAtt.arrayLayer = is3D ? 0 : it->layer();
4198 colorAtt.slice = is3D ? it->layer() : 0;
4199 colorAtt.level = it->level();
4200 QMetalTexture *resTexD = QRHI_RES(QMetalTexture, it->resolveTexture());
4201 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4202 colorAtt.resolveLayer = it->resolveLayer();
4203 colorAtt.resolveLevel = it->resolveLevel();
4204 d->fb.colorAtt[attIndex] = colorAtt;
4205 }
4206 d->dpr = 1;
4207
4208 if (hasDepthStencil) {
4209 if (m_desc.depthTexture()) {
4211 d->fb.dsTex = depthTexD->d->tex;
4212 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4213 d->fb.depthNeedsStore = true;
4214 if (d->colorAttCount == 0) {
4215 d->pixelSize = depthTexD->pixelSize();
4216 d->sampleCount = depthTexD->samples;
4217 }
4218 } else {
4220 d->fb.dsTex = depthRbD->d->tex;
4221 d->fb.hasStencil = true;
4222 d->fb.depthNeedsStore = false;
4223 if (d->colorAttCount == 0) {
4224 d->pixelSize = depthRbD->pixelSize();
4225 d->sampleCount = depthRbD->samples;
4226 }
4227 }
4228 d->dsAttCount = 1;
4229 } else {
4230 d->dsAttCount = 0;
4231 }
4232
4233 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4234
4235 rhiD->registerResource(this, false);
4236 return true;
4237}
4238
4240{
4241 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4242 const_cast<QMetalTextureRenderTarget *>(this)->create();
4243
4244 return d->pixelSize;
4245}
4246
4248{
4249 return d->dpr;
4250}
4251
4253{
4254 return d->sampleCount;
4255}
4256
4259{
4260}
4261
4263{
4264 destroy();
4265}
4266
4268{
4270 maxBinding = -1;
4271
4273 if (rhiD)
4274 rhiD->unregisterResource(this);
4275}
4276
4278{
4279 if (!sortedBindings.isEmpty())
4280 destroy();
4281
4283 if (!rhiD->sanityCheckShaderResourceBindings(this))
4284 return false;
4285
4286 rhiD->updateLayoutDesc(this);
4287
4288 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4290 if (!sortedBindings.isEmpty())
4292 else
4293 maxBinding = -1;
4294
4296
4298 memset(&bd, 0, sizeof(BoundResourceData));
4299
4300 generation += 1;
4301 rhiD->registerResource(this, false);
4302 return true;
4303}
4304
4306{
4308 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4309 if (!flags.testFlag(BindingsAreSorted))
4311
4313 memset(&bd, 0, sizeof(BoundResourceData));
4314
4315 generation += 1;
4316}
4317
4319 : QRhiGraphicsPipeline(rhi),
4321{
4322 d->q = this;
4323 d->tess.q = d;
4324}
4325
4327{
4328 destroy();
4329 delete d;
4330}
4331
4333{
4334 d->vs.destroy();
4335 d->fs.destroy();
4336
4337 d->tess.compVs[0].destroy();
4338 d->tess.compVs[1].destroy();
4339 d->tess.compVs[2].destroy();
4340
4343
4348
4349 delete d->bufferSizeBuffer;
4350 d->bufferSizeBuffer = nullptr;
4351
4352 if (!d->ps && !d->ds
4355 {
4356 return;
4357 }
4358
4361 e.lastActiveFrameSlot = lastActiveFrameSlot;
4362 e.graphicsPipeline.pipelineState = d->ps;
4363 e.graphicsPipeline.depthStencilState = d->ds;
4364 e.graphicsPipeline.tessVertexComputeState = d->tess.vertexComputeState;
4365 e.graphicsPipeline.tessTessControlComputeState = d->tess.tessControlComputeState;
4366 d->ps = nil;
4367 d->ds = nil;
4370
4372 if (rhiD) {
4373 rhiD->d->releaseQueue.append(e);
4374 rhiD->unregisterResource(this);
4375 }
4376}
4377
4379{
4380 switch (format) {
4382 return MTLVertexFormatFloat4;
4384 return MTLVertexFormatFloat3;
4386 return MTLVertexFormatFloat2;
4388 return MTLVertexFormatFloat;
4390 return MTLVertexFormatUChar4Normalized;
4392 return MTLVertexFormatUChar2Normalized;
4394 return MTLVertexFormatUCharNormalized;
4396 return MTLVertexFormatUInt4;
4398 return MTLVertexFormatUInt3;
4400 return MTLVertexFormatUInt2;
4402 return MTLVertexFormatUInt;
4404 return MTLVertexFormatInt4;
4406 return MTLVertexFormatInt3;
4408 return MTLVertexFormatInt2;
4410 return MTLVertexFormatInt;
4412 return MTLVertexFormatHalf4;
4414 return MTLVertexFormatHalf3;
4416 return MTLVertexFormatHalf2;
4418 return MTLVertexFormatHalf;
4419 default:
4420 Q_UNREACHABLE();
4421 return MTLVertexFormatFloat4;
4422 }
4423}
4424
4426{
4427 switch (f) {
4429 return MTLBlendFactorZero;
4431 return MTLBlendFactorOne;
4433 return MTLBlendFactorSourceColor;
4435 return MTLBlendFactorOneMinusSourceColor;
4437 return MTLBlendFactorDestinationColor;
4439 return MTLBlendFactorOneMinusDestinationColor;
4441 return MTLBlendFactorSourceAlpha;
4443 return MTLBlendFactorOneMinusSourceAlpha;
4445 return MTLBlendFactorDestinationAlpha;
4447 return MTLBlendFactorOneMinusDestinationAlpha;
4449 return MTLBlendFactorBlendColor;
4451 return MTLBlendFactorBlendAlpha;
4453 return MTLBlendFactorOneMinusBlendColor;
4455 return MTLBlendFactorOneMinusBlendAlpha;
4457 return MTLBlendFactorSourceAlphaSaturated;
4459 return MTLBlendFactorSource1Color;
4461 return MTLBlendFactorOneMinusSource1Color;
4463 return MTLBlendFactorSource1Alpha;
4465 return MTLBlendFactorOneMinusSource1Alpha;
4466 default:
4467 Q_UNREACHABLE();
4468 return MTLBlendFactorZero;
4469 }
4470}
4471
4472static inline MTLBlendOperation toMetalBlendOp(QRhiGraphicsPipeline::BlendOp op)
4473{
4474 switch (op) {
4476 return MTLBlendOperationAdd;
4478 return MTLBlendOperationSubtract;
4480 return MTLBlendOperationReverseSubtract;
4482 return MTLBlendOperationMin;
4484 return MTLBlendOperationMax;
4485 default:
4486 Q_UNREACHABLE();
4487 return MTLBlendOperationAdd;
4488 }
4489}
4490
4491static inline uint toMetalColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
4492{
4493 uint f = 0;
4494 if (c.testFlag(QRhiGraphicsPipeline::R))
4495 f |= MTLColorWriteMaskRed;
4496 if (c.testFlag(QRhiGraphicsPipeline::G))
4497 f |= MTLColorWriteMaskGreen;
4498 if (c.testFlag(QRhiGraphicsPipeline::B))
4499 f |= MTLColorWriteMaskBlue;
4500 if (c.testFlag(QRhiGraphicsPipeline::A))
4501 f |= MTLColorWriteMaskAlpha;
4502 return f;
4503}
4504
4505static inline MTLCompareFunction toMetalCompareOp(QRhiGraphicsPipeline::CompareOp op)
4506{
4507 switch (op) {
4509 return MTLCompareFunctionNever;
4511 return MTLCompareFunctionLess;
4513 return MTLCompareFunctionEqual;
4515 return MTLCompareFunctionLessEqual;
4517 return MTLCompareFunctionGreater;
4519 return MTLCompareFunctionNotEqual;
4521 return MTLCompareFunctionGreaterEqual;
4523 return MTLCompareFunctionAlways;
4524 default:
4525 Q_UNREACHABLE();
4526 return MTLCompareFunctionAlways;
4527 }
4528}
4529
4530static inline MTLStencilOperation toMetalStencilOp(QRhiGraphicsPipeline::StencilOp op)
4531{
4532 switch (op) {
4534 return MTLStencilOperationZero;
4536 return MTLStencilOperationKeep;
4538 return MTLStencilOperationReplace;
4540 return MTLStencilOperationIncrementClamp;
4542 return MTLStencilOperationDecrementClamp;
4544 return MTLStencilOperationInvert;
4546 return MTLStencilOperationIncrementWrap;
4548 return MTLStencilOperationDecrementWrap;
4549 default:
4550 Q_UNREACHABLE();
4551 return MTLStencilOperationKeep;
4552 }
4553}
4554
4556{
4557 switch (t) {
4559 return MTLPrimitiveTypeTriangle;
4561 return MTLPrimitiveTypeTriangleStrip;
4563 return MTLPrimitiveTypeLine;
4565 return MTLPrimitiveTypeLineStrip;
4567 return MTLPrimitiveTypePoint;
4568 default:
4569 Q_UNREACHABLE();
4570 return MTLPrimitiveTypeTriangle;
4571 }
4572}
4573
4575{
4576 switch (t) {
4580 return MTLPrimitiveTopologyClassTriangle;
4583 return MTLPrimitiveTopologyClassLine;
4585 return MTLPrimitiveTopologyClassPoint;
4586 default:
4587 Q_UNREACHABLE();
4588 return MTLPrimitiveTopologyClassTriangle;
4589 }
4590}
4591
4593{
4594 switch (c) {
4596 return MTLCullModeNone;
4598 return MTLCullModeFront;
4600 return MTLCullModeBack;
4601 default:
4602 Q_UNREACHABLE();
4603 return MTLCullModeNone;
4604 }
4605}
4606
4608{
4609 switch (mode) {
4611 return MTLTriangleFillModeFill;
4613 return MTLTriangleFillModeLines;
4614 default:
4615 Q_UNREACHABLE();
4616 return MTLTriangleFillModeFill;
4617 }
4618}
4619
4621{
4622 switch (w) {
4624 return MTLWindingClockwise;
4626 return MTLWindingCounterClockwise;
4627 default:
4628 // this is reachable, consider a tess.eval. shader not declaring it, the value is then Unknown
4629 return MTLWindingCounterClockwise;
4630 }
4631}
4632
4634{
4635 switch (p) {
4637 return MTLTessellationPartitionModePow2;
4639 return MTLTessellationPartitionModeFractionalEven;
4641 return MTLTessellationPartitionModeFractionalOdd;
4642 default:
4643 // this is reachable, consider a tess.eval. shader not declaring it, the value is then Unknown
4644 return MTLTessellationPartitionModePow2;
4645 }
4646}
4647
4648static inline MTLLanguageVersion toMetalLanguageVersion(const QShaderVersion &version)
4649{
4650 int v = version.version();
4651 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
4652}
4653
4655 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
4656{
4657 QVarLengthArray<int, 8> versions;
4658 if (@available(macOS 13, iOS 16, *))
4659 versions << 30;
4660 if (@available(macOS 12, iOS 15, *))
4661 versions << 24;
4662 if (@available(macOS 11, iOS 14, *))
4663 versions << 23;
4664 if (@available(macOS 10.15, iOS 13, *))
4665 versions << 22;
4666 if (@available(macOS 10.14, iOS 12, *))
4667 versions << 21;
4668 versions << 20 << 12;
4669
4670 const QList<QShaderKey> shaders = shader.availableShaders();
4671
4673
4674 for (const int &version : versions) {
4675 key = { QShader::Source::MetalLibShader, version, shaderVariant };
4676 if (shaders.contains(key))
4677 break;
4678 }
4679
4680 QShaderCode mtllib = shader.shader(key);
4681 if (!mtllib.shader().isEmpty()) {
4682 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
4683 size_t(mtllib.shader().size()),
4684 dispatch_get_global_queue(0, 0),
4685 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
4686 NSError *err = nil;
4687 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
4688 dispatch_release(data);
4689 if (!err) {
4690 *entryPoint = mtllib.entryPoint();
4691 *activeKey = key;
4692 return lib;
4693 } else {
4694 const QString msg = QString::fromNSString(err.localizedDescription);
4695 qWarning("Failed to load metallib from baked shader: %s", qPrintable(msg));
4696 }
4697 }
4698
4699 for (const int &version : versions) {
4700 key = { QShader::Source::MslShader, version, shaderVariant };
4701 if (shaders.contains(key))
4702 break;
4703 }
4704
4705 QShaderCode mslSource = shader.shader(key);
4706 if (mslSource.shader().isEmpty()) {
4707 qWarning() << "No MSL 2.0 or 1.2 code found in baked shader" << shader;
4708 return nil;
4709 }
4710
4711 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
4712 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
4713 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
4714 NSError *err = nil;
4715 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
4716 [opts release];
4717 // src is autoreleased
4718
4719 // if lib is null and err is non-null, we had errors (fail)
4720 // if lib is non-null and err is non-null, we had warnings (success)
4721 // if lib is non-null and err is null, there were no errors or warnings (success)
4722 if (!lib) {
4723 const QString msg = QString::fromNSString(err.localizedDescription);
4724 *error = msg;
4725 return nil;
4726 }
4727
4728 *entryPoint = mslSource.entryPoint();
4729 *activeKey = key;
4730 return lib;
4731}
4732
4734{
4735 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
4736}
4737
4739{
4740 MTLRenderPipelineDescriptor *rpDesc = reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
4741
4742 if (rpD->colorAttachmentCount) {
4743 // defaults when no targetBlends are provided
4744 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD->colorFormat[0]);
4745 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
4746 rpDesc.colorAttachments[0].blendingEnabled = false;
4747
4749 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
4750
4751 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
4753 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD->colorFormat[i]);
4754 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
4755 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
4756 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
4757 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
4758 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
4759 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
4760 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
4761 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
4762 }
4763 }
4764
4765 if (rpD->hasDepthStencil) {
4766 // Must only be set when a depth-stencil buffer will actually be bound,
4767 // validation blows up otherwise.
4768 MTLPixelFormat fmt = MTLPixelFormat(rpD->dsFormat);
4769 rpDesc.depthAttachmentPixelFormat = fmt;
4770#if defined(Q_OS_MACOS)
4771 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
4772#else
4773 if (fmt != MTLPixelFormatDepth32Float)
4774#endif
4775 rpDesc.stencilAttachmentPixelFormat = fmt;
4776 }
4777
4779 rpDesc.sampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
4780}
4781
4783{
4784 MTLDepthStencilDescriptor *dsDesc = reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
4785
4786 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
4787 dsDesc.depthWriteEnabled = m_depthWrite;
4788 if (m_stencilTest) {
4789 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
4790 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
4791 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
4792 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
4793 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
4794 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
4795 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
4796
4797 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
4798 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
4799 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
4800 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
4801 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
4802 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
4803 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
4804 }
4805}
4806
4808{
4809 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
4812 d->depthBias = float(m_depthBias);
4814}
4815
4817{
4818 // same binding space for vertex and constant buffers - work it around
4819 // should be in native resource binding not SPIR-V, but this will work anyway
4820 const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, q->shaderResourceBindings())->maxBinding + 1;
4821
4822 QRhiVertexInputLayout vertexInputLayout = q->vertexInputLayout();
4823 for (auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
4824 it != itEnd; ++it)
4825 {
4826 const uint loc = uint(it->location());
4827 desc.attributes[loc].format = decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
4828 desc.attributes[loc].offset = NSUInteger(it->offset());
4829 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
4830 }
4831 int bindingIndex = 0;
4832 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
4833 for (auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
4834 it != itEnd; ++it, ++bindingIndex)
4835 {
4836 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
4837 desc.layouts[layoutIdx].stepFunction =
4838 it->classification() == QRhiVertexInputBinding::PerInstance
4839 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
4840 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
4841 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
4842 desc.layouts[layoutIdx].stepRate *= viewCount;
4843 desc.layouts[layoutIdx].stride = it->stride();
4844 }
4845}
4846
4848{
4849 // same binding space for vertex and constant buffers - work it around
4850 // should be in native resource binding not SPIR-V, but this will work anyway
4851 const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, q->shaderResourceBindings())->maxBinding + 1;
4852
4853 QRhiVertexInputLayout vertexInputLayout = q->vertexInputLayout();
4854 for (auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
4855 it != itEnd; ++it)
4856 {
4857 const uint loc = uint(it->location());
4858 desc.attributes[loc].format = decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
4859 desc.attributes[loc].offset = NSUInteger(it->offset());
4860 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
4861 }
4862 int bindingIndex = 0;
4863 for (auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
4864 it != itEnd; ++it, ++bindingIndex)
4865 {
4866 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
4867 if (desc.indexBufferIndex) {
4868 desc.layouts[layoutIdx].stepFunction =
4869 it->classification() == QRhiVertexInputBinding::PerInstance
4870 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
4871 } else {
4872 desc.layouts[layoutIdx].stepFunction =
4873 it->classification() == QRhiVertexInputBinding::PerInstance
4874 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
4875 }
4876 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
4877 desc.layouts[layoutIdx].stride = it->stride();
4878 }
4879}
4880
4881void QRhiMetalData::trySeedingRenderPipelineFromBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
4882{
4883 if (@available(macOS 11.0, iOS 14.0, *)) {
4884 if (binArch) {
4885 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
4886 rpDesc.binaryArchives = binArchArray;
4887 }
4888 }
4889}
4890
4892{
4893 if (@available(macOS 11.0, iOS 14.0, *)) {
4894 if (!d->binArch)
4895 return false;
4896
4897 // ### QTBUG-106703, QTBUG-108216, revisit after 13.0
4898 if (!d->binArchWasEmpty && d->q->osMajor >= 13) {
4899 static bool logPrinted = false;
4900 if (!logPrinted) {
4901 logPrinted = true;
4902 qCDebug(QRHI_LOG_INFO, "Skipping adding more pipelines to MTLBinaryArchive on this OS version (%d.%d) due to known issues.",
4903 d->q->osMajor, d->q->osMinor);
4904 }
4905 return false;
4906 }
4907
4908 return true;
4909 } else {
4910 return false;
4911 }
4912}
4913
4914void QRhiMetalData::addRenderPipelineToBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
4915{
4916 if (@available(macOS 11.0, iOS 14.0, *)) {
4917 if (canAddToBinaryArchive(this)) {
4918 NSError *err = nil;
4919 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
4920 const QString msg = QString::fromNSString(err.localizedDescription);
4921 qWarning("Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
4922 }
4923 }
4924 }
4925}
4926
4928{
4930
4931 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
4932 d->setupVertexInputDescriptor(vertexDesc);
4933
4934 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
4935 rpDesc.vertexDescriptor = vertexDesc;
4936
4937 // Mutability cannot be determined (slotted buffers could be set as
4938 // MTLMutabilityImmutable, but then we potentially need a different
4939 // descriptor for each buffer combination as this depends on the actual
4940 // buffers not just the resource binding layout), so leave
4941 // rpDesc.vertex/fragmentBuffers at the defaults.
4942
4943 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
4944 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
4945 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
4946 switch (shaderStage.type()) {
4948 d->vs = *cacheIt;
4949 [d->vs.lib retain];
4950 [d->vs.func retain];
4951 rpDesc.vertexFunction = d->vs.func;
4952 break;
4954 d->fs = *cacheIt;
4955 [d->fs.lib retain];
4956 [d->fs.func retain];
4957 rpDesc.fragmentFunction = d->fs.func;
4958 break;
4959 default:
4960 break;
4961 }
4962 } else {
4963 const QShader shader = shaderStage.shader();
4964 QString error;
4965 QByteArray entryPoint;
4966 QShaderKey activeKey;
4967 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
4968 &error, &entryPoint, &activeKey);
4969 if (!lib) {
4970 qWarning("MSL shader compilation failed: %s", qPrintable(error));
4971 return false;
4972 }
4973 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
4974 if (!func) {
4975 qWarning("MSL function for entry point %s not found", entryPoint.constData());
4976 [lib release];
4977 return false;
4978 }
4979 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
4980 // Use the simplest strategy: too many cached shaders -> drop them all.
4981 for (QMetalShader &s : rhiD->d->shaderCache)
4982 s.destroy();
4983 rhiD->d->shaderCache.clear();
4984 }
4985 switch (shaderStage.type()) {
4987 d->vs.lib = lib;
4988 d->vs.func = func;
4989 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
4990 d->vs.desc = shader.description();
4991 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
4992 rhiD->d->shaderCache.insert(shaderStage, d->vs);
4993 [d->vs.lib retain];
4994 [d->vs.func retain];
4995 rpDesc.vertexFunction = func;
4996 break;
4998 d->fs.lib = lib;
4999 d->fs.func = func;
5000 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5001 d->fs.desc = shader.description();
5002 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5003 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5004 [d->fs.lib retain];
5005 [d->fs.func retain];
5006 rpDesc.fragmentFunction = func;
5007 break;
5008 default:
5009 [func release];
5010 [lib release];
5011 break;
5012 }
5013 }
5014 }
5015
5018
5019 if (m_multiViewCount >= 2)
5020 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5021
5022 rhiD->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5023
5024 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5025 rhiD->d->addRenderPipelineToBinaryArchive(rpDesc);
5026
5027 NSError *err = nil;
5028 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5029 [rpDesc release];
5030 if (!d->ps) {
5031 const QString msg = QString::fromNSString(err.localizedDescription);
5032 qWarning("Failed to create render pipeline state: %s", qPrintable(msg));
5033 return false;
5034 }
5035
5036 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5038 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5039 [dsDesc release];
5040
5042 mapStates();
5043
5044 return true;
5045}
5046
5048{
5049 switch (vertexCompVariant) {
5051 return 0;
5053 return 1;
5055 return 2;
5056 default:
5057 break;
5058 }
5059 return -1;
5060}
5061
5063{
5064 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5065 if (varIndex >= 0 && vertexComputeState[varIndex])
5066 return vertexComputeState[varIndex];
5067
5068 id<MTLFunction> func = nil;
5069 if (varIndex >= 0)
5070 func = compVs[varIndex].func;
5071
5072 if (!func) {
5073 qWarning("No compute function found for vertex shader translated for tessellation, this should not happen");
5074 return nil;
5075 }
5076
5077 const QMap<int, int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5078 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5079
5080 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor new];
5081 cpDesc.computeFunction = func;
5082 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5083 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5084 if (indexBufferBinding >= 0) {
5085 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5086 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5087 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5088 } else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5089 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5090 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5091 }
5092 }
5093 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5094
5096
5097 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5098 rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
5099
5100 NSError *err = nil;
5101 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5102 options: MTLPipelineOptionNone
5103 reflection: nil
5104 error: &err];
5105 [cpDesc release];
5106 if (!ps) {
5107 const QString msg = QString::fromNSString(err.localizedDescription);
5108 qWarning("Failed to create compute pipeline state: %s", qPrintable(msg));
5109 } else {
5110 vertexComputeState[varIndex] = ps;
5111 }
5112 // not retained, the only owner is vertexComputeState and so the QRhiGraphicsPipeline
5113 return ps;
5114}
5115
5117{
5118 if (tessControlComputeState)
5119 return tessControlComputeState;
5120
5121 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor new];
5122 cpDesc.computeFunction = compTesc.func;
5123
5125
5126 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5127 rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
5128
5129 NSError *err = nil;
5130 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5131 options: MTLPipelineOptionNone
5132 reflection: nil
5133 error: &err];
5134 [cpDesc release];
5135 if (!ps) {
5136 const QString msg = QString::fromNSString(err.localizedDescription);
5137 qWarning("Failed to create compute pipeline state: %s", qPrintable(msg));
5138 } else {
5139 tessControlComputeState = ps;
5140 }
5141 // not retained, the only owner is tessControlComputeState and so the QRhiGraphicsPipeline
5142 return ps;
5143}
5144
5146{
5147 return (indices >> index) & 0x1;
5148}
5149
5151{
5152 indices |= 1 << index;
5153}
5154
5156{
5157 // Maximum number of vertex attributes per vertex descriptor. There does
5158 // not appear to be a way to query this from the implementation.
5159 // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf indicates
5160 // that all GPU families have a value of 31.
5161 static const int maxVertexAttributes = 31;
5162
5163 for (int index = 0; index < maxVertexAttributes; ++index) {
5164 if (!indexTaken(index, indices))
5165 return index;
5166 }
5167
5168 Q_UNREACHABLE_RETURN(-1);
5169}
5170
5172{
5173 return ((offset + alignment - 1) / alignment) * alignment;
5174}
5175
5176template<typename T>
5177static void addUnusedVertexAttribute(const T &variable, QRhiMetal *rhiD, quint32 &offset, quint32 &vertexAlignment)
5178{
5179
5180 int elements = 1;
5181 for (const int dim : variable.arrayDims)
5182 elements *= dim;
5183
5185 for (int element = 0; element < elements; ++element) {
5186 for (const auto &member : variable.structMembers) {
5187 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5188 }
5189 }
5190 } else {
5193
5194 // MSL specification 3.0 says alignment = size for non packed scalars and vectors
5195 const quint32 alignment = size;
5196 vertexAlignment = std::max(vertexAlignment, alignment);
5197
5198 for (int element = 0; element < elements; ++element) {
5199 // adjust alignment
5201 offset += size;
5202 }
5203 }
5204}
5205
5206template<typename T>
5207static void addVertexAttribute(const T &variable, int binding, QRhiMetal *rhiD, int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5208{
5209
5210 int elements = 1;
5211 for (const int dim : variable.arrayDims)
5212 elements *= dim;
5213
5215 for (int element = 0; element < elements; ++element) {
5216 for (const auto &member : variable.structMembers) {
5217 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5218 }
5219 }
5220 } else {
5223
5224 // MSL specification 3.0 says alignment = size for non packed scalars and vectors
5225 const quint32 alignment = size;
5226 vertexAlignment = std::max(vertexAlignment, alignment);
5227
5228 for (int element = 0; element < elements; ++element) {
5230
5231 // adjust alignment
5233
5234 attributes[index].bufferIndex = binding;
5235 attributes[index].format = toMetalAttributeFormat(format);
5236 attributes[index].offset = offset;
5237
5239 index++;
5240 if (indexTaken(index, indices))
5242
5243 offset += size;
5244 }
5245 }
5246}
5247
5249{
5250 if (a.size() == b.size()) {
5251 bool match = true;
5252 for (int i = 0; i < a.size() && match; ++i) {
5253 match &= a[i].type == b[i].type
5254 && a[i].arrayDims == b[i].arrayDims
5255 && matches(a[i].structMembers, b[i].structMembers);
5256 }
5257 return match;
5258 }
5259
5260 return false;
5261}
5262
5264{
5265 return a.location == b.location
5266 && a.type == b.type
5267 && a.perPatch == b.perPatch
5268 && matches(a.structMembers, b.structMembers);
5269}
5270
5271//
5272// Create the tessellation evaluation render pipeline state
5273//
5274// The tesc runs as a compute shader in a compute pipeline and writes per patch and per patch
5275// control point data into separate storage buffers. The tese runs as a vertex shader in a render
5276// pipeline. Our task is to generate a render pipeline descriptor for the tese that pulls vertices
5277// from these buffers.
5278//
5279// As the buffers we are pulling vertices from are written by a compute pipeline, they follow the
5280// MSL alignment conventions which we must take into account when generating our
5281// MTLVertexDescriptor. We must include the user defined tese input attributes, and any builtins
5282// that were used.
5283//
5284// SPIRV-Cross generates the MSL tese shader code with input attribute indices that reflect the
5285// specified GLSL locations. Interface blocks are flattened with each member having an incremented
5286// attribute index. SPIRV-Cross reports an error on compilation if there are clashes in the index
5287// address space.
5288//
5289// After the user specified attributes are processed, SPIRV-Cross places the in-use builtins at the
5290// next available (lowest value) attribute index. Tese builtins are processed in the following
5291// order:
5292//
5293// in gl_PerVertex
5294// {
5295// vec4 gl_Position;
5296// float gl_PointSize;
5297// float gl_ClipDistance[];
5298// };
5299//
5300// patch in float gl_TessLevelOuter[4];
5301// patch in float gl_TessLevelInner[2];
5302//
5303// Enumerations in QShaderDescription::BuiltinType are defined in this order.
5304//
5305// For quads, SPIRV-Cross places MTLQuadTessellationFactorsHalf per patch in the tessellation
5306// factor buffer. For triangles it uses MTLTriangleTessellationFactorsHalf.
5307//
5308// It should be noted that SPIRV-Cross handles the following builtin inputs internally, with no
5309// host side support required.
5310//
5311// in vec3 gl_TessCoord;
5312// in int gl_PatchVerticesIn;
5313// in int gl_PrimitiveID;
5314//
5316{
5317 if (pipeline->d->ps)
5318 return pipeline->d->ps;
5319
5320 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5321 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5322
5323 // tesc output buffers
5324 const QMap<int, int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5325 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5326 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5327 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5328 quint32 offsetInTescOutput = 0;
5329 quint32 offsetInTescPatchOutput = 0;
5330 quint32 offsetInTessFactorBuffer = 0;
5331 quint32 tescOutputAlignment = 0;
5332 quint32 tescPatchOutputAlignment = 0;
5333 quint32 tessFactorAlignment = 0;
5334 QSet<int> usedBuffers;
5335
5336 // tesc output variables in ascending location order
5338 for (const auto &tescOutVar : compTesc.desc.outputVariables())
5339 tescOutVars[tescOutVar.location] = tescOutVar;
5340
5341 // tese input variables in ascending location order
5343 for (const auto &teseInVar : vertTese.desc.inputVariables())
5344 teseInVars[teseInVar.location] = teseInVar;
5345
5346 // bit mask tracking usage of vertex attribute indices
5347 quint64 indices = 0;
5348
5349 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5350
5351 int index = tescOutVar.location;
5352 int binding = -1;
5353 quint32 *offset = nullptr;
5354 quint32 *alignment = nullptr;
5355
5356 if (tescOutVar.perPatch) {
5357 binding = tescPatchOutputBufferBinding;
5358 offset = &offsetInTescPatchOutput;
5359 alignment = &tescPatchOutputAlignment;
5360 } else {
5361 tescOutVar.arrayDims.removeLast();
5362 binding = tescOutputBufferBinding;
5363 offset = &offsetInTescOutput;
5364 alignment = &tescOutputAlignment;
5365 }
5366
5367 if (teseInVars.contains(index)) {
5368
5369 if (!matches(teseInVars[index], tescOutVar)) {
5370 qWarning() << "mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5371 qWarning() << " tesc out:" << tescOutVar;
5372 qWarning() << " tese in:" << teseInVars[index];
5373 }
5374
5375 if (binding != -1) {
5376 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5377 usedBuffers << binding;
5378 } else {
5379 qWarning() << "baked tessellation control shader missing output buffer binding information";
5380 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5381 }
5382
5383 } else {
5384 qWarning() << "missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5385 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5386 }
5387
5388 teseInVars.remove(tescOutVar.location);
5389 }
5390
5391 for (const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5392 qWarning() << "missing tessellation control output for tessellation evaluation input:" << teseInVar;
5393
5394 // tesc output builtins in ascending location order
5396 for (const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5397 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5398
5399 // tese input builtins in ascending location order
5401 for (const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5402 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5403
5404 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5405 bool tessLevelAdded = false;
5406
5407 for (const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5408
5410 int binding = -1;
5411 quint32 *offset = nullptr;
5412 quint32 *alignment = nullptr;
5413
5414 switch (builtin.type) {
5417 binding = tescOutputBufferBinding;
5418 offset = &offsetInTescOutput;
5419 alignment = &tescOutputAlignment;
5420 break;
5423 binding = tescOutputBufferBinding;
5424 offset = &offsetInTescOutput;
5425 alignment = &tescOutputAlignment;
5426 break;
5429 variable.arrayDims = builtin.arrayDims;
5430 binding = tescOutputBufferBinding;
5431 offset = &offsetInTescOutput;
5432 alignment = &tescOutputAlignment;
5433 break;
5436 binding = tessFactorBufferBinding;
5437 offset = &offsetInTessFactorBuffer;
5438 tessLevelAdded = trianglesMode;
5439 alignment = &tessFactorAlignment;
5440 break;
5442 if (trianglesMode) {
5443 if (!tessLevelAdded) {
5445 binding = tessFactorBufferBinding;
5446 offsetInTessFactorBuffer = 0;
5447 offset = &offsetInTessFactorBuffer;
5448 alignment = &tessFactorAlignment;
5449 tessLevelAdded = true;
5450 } else {
5451 teseInBuiltins.remove(builtin.type);
5452 continue;
5453 }
5454 } else {
5456 binding = tessFactorBufferBinding;
5457 offsetInTessFactorBuffer = 8;
5458 offset = &offsetInTessFactorBuffer;
5459 alignment = &tessFactorAlignment;
5460 }
5461 break;
5462 default:
5463 Q_UNREACHABLE();
5464 break;
5465 }
5466
5467 if (teseInBuiltins.contains(builtin.type)) {
5468 if (binding != -1) {
5470 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5471 usedBuffers << binding;
5472 } else {
5473 qWarning() << "baked tessellation control shader missing output buffer binding information";
5475 }
5476 } else {
5478 }
5479
5480 teseInBuiltins.remove(builtin.type);
5481 }
5482
5483 for (const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
5484 switch (builtin.type) {
5488 qWarning() << "missing tessellation control output for tessellation evaluation builtin input:" << builtin;
5489 break;
5490 default:
5491 break;
5492 }
5493 }
5494
5495 if (usedBuffers.contains(tescOutputBufferBinding)) {
5496 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
5497 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
5498 }
5499
5500 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
5501 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5502 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
5503 }
5504
5505 if (usedBuffers.contains(tessFactorBufferBinding)) {
5506 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5507 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ? sizeof(MTLTriangleTessellationFactorsHalf) : sizeof(MTLQuadTessellationFactorsHalf);
5508 }
5509
5510 rpDesc.vertexDescriptor = vertexDesc;
5511 rpDesc.vertexFunction = vertTese.func;
5512 rpDesc.fragmentFunction = pipeline->d->fs.func;
5513
5514 // The portable, cross-API approach is to use CCW, the results are then
5515 // identical (assuming the applied clipSpaceCorrMatrix) for all the 3D
5516 // APIs. The tess.eval. GLSL shader is thus expected to specify ccw. If it
5517 // doesn't, things may not work as expected.
5518 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
5519
5520 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
5521
5523 pipeline->setupAttachmentsInMetalRenderPassDescriptor(rpDesc, rpD);
5524
5526
5527 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5528 rhiD->d->addRenderPipelineToBinaryArchive(rpDesc);
5529
5530 NSError *err = nil;
5531 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5532 [rpDesc release];
5533 if (!ps) {
5534 const QString msg = QString::fromNSString(err.localizedDescription);
5535 qWarning("Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
5536 } else {
5537 // ps is stored in the QMetalGraphicsPipelineData so the end result in this
5538 // regard is no different from what createVertexFragmentPipeline does
5539 pipeline->d->ps = ps;
5540 }
5541 return ps;
5542}
5543
5545{
5546 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
5547
5548 // Check if something is reusable as-is.
5549 for (QMetalBuffer *workBuf : *workBuffers) {
5550 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
5551 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5552 return workBuf;
5553 }
5554 }
5555
5556 // Once the pool is above a certain threshold, see if there is something
5557 // unused (but too small) and recreate that our size.
5558 if (workBuffers->count() > QMTL_FRAMES_IN_FLIGHT * 8) {
5559 for (QMetalBuffer *workBuf : *workBuffers) {
5560 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
5561 workBuf->setSize(size);
5562 if (workBuf->create()) {
5563 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5564 return workBuf;
5565 }
5566 }
5567 }
5568 }
5569
5570 // Add a new buffer to the pool.
5572 if (type == WorkBufType::DeviceLocal) {
5573 // for GPU->GPU data (non-slotted, not necessarily host writable)
5574 buf = new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5575 } else {
5576 // for CPU->GPU (non-slotted, host writable/coherent)
5577 buf = new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5578 }
5579 if (buf->create()) {
5580 buf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5581 workBuffers->append(buf);
5582 return buf;
5583 }
5584
5585 qWarning("Failed to acquire work buffer of size %u", size);
5586 return nullptr;
5587}
5588
5589bool QMetalGraphicsPipeline::createTessellationPipelines(const QShader &tessVert, const QShader &tesc, const QShader &tese, const QShader &tessFrag)
5590{
5592 QString error;
5593 QByteArray entryPoint;
5594 QShaderKey activeKey;
5595
5596 const QShaderDescription tescDesc = tesc.description();
5597 const QShaderDescription teseDesc = tese.description();
5598 d->tess.inControlPointCount = uint(m_patchControlPointCount);
5599 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
5600 if (!d->tess.outControlPointCount)
5601 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
5602
5603 if (!d->tess.outControlPointCount) {
5604 qWarning("Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
5605 d->tess.enabled = false;
5606 d->tess.failed = true;
5607 return false;
5608 }
5609
5610 if (m_multiViewCount >= 2)
5611 qWarning("Multiview is not supported with tessellation");
5612
5613 // Now the vertex shader is a compute shader.
5614 // It should have three dedicated *VertexAsComputeShader variants.
5615 // What the requested variant was (Standard or Batchable) plays no role here.
5616 // (the Qt Quick scenegraph does not use tessellation with its materials)
5617 // Create all three versions.
5618
5619 bool variantsPresent[3] = {};
5620 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
5621 for (const QShaderKey &k : tessVertKeys) {
5622 switch (k.sourceVariant()) {
5624 variantsPresent[0] = true;
5625 break;
5627 variantsPresent[1] = true;
5628 break;
5630 variantsPresent[2] = true;
5631 break;
5632 default:
5633 break;
5634 }
5635 }
5636 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
5637 qWarning("Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
5638 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
5639 "Try passing --msltess to qsb.");
5640 d->tess.enabled = false;
5641 d->tess.failed = true;
5642 return false;
5643 }
5644
5645 int varIndex = 0; // Will map NonIndexed as 0, UInt32 as 1, UInt16 as 2. Do not change this ordering.
5646 for (QShader::Variant variant : {
5650 {
5651 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
5652 if (!lib) {
5653 qWarning("MSL shader compilation failed for vertex-as-compute shader %d: %s", int(variant), qPrintable(error));
5654 d->tess.enabled = false;
5655 d->tess.failed = true;
5656 return false;
5657 }
5658 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5659 if (!func) {
5660 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5661 [lib release];
5662 d->tess.enabled = false;
5663 d->tess.failed = true;
5664 return false;
5665 }
5666 QMetalShader &compVs(d->tess.compVs[varIndex]);
5667 compVs.lib = lib;
5668 compVs.func = func;
5669 compVs.desc = tessVert.description();
5670 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
5671 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
5672
5673 // pre-create all three MTLComputePipelineStates
5674 if (!d->tess.vsCompPipeline(rhiD, variant)) {
5675 qWarning("Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)", int(variant));
5676 d->tess.enabled = false;
5677 d->tess.failed = true;
5678 return false;
5679 }
5680
5681 ++varIndex;
5682 }
5683
5684 // Pipeline #2 is a compute that runs the tessellation control (compute) shader
5685 id<MTLLibrary> tessControlLib = rhiD->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
5686 if (!tessControlLib) {
5687 qWarning("MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
5688 d->tess.enabled = false;
5689 d->tess.failed = true;
5690 return false;
5691 }
5692 id<MTLFunction> tessControlFunc = rhiD->d->createMSLShaderFunction(tessControlLib, entryPoint);
5693 if (!tessControlFunc) {
5694 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5695 [tessControlLib release];
5696 d->tess.enabled = false;
5697 d->tess.failed = true;
5698 return false;
5699 }
5700 d->tess.compTesc.lib = tessControlLib;
5701 d->tess.compTesc.func = tessControlFunc;
5702 d->tess.compTesc.desc = tesc.description();
5703 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
5704 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
5705 if (!d->tess.tescCompPipeline(rhiD)) {
5706 qWarning("Failed to pre-generate compute pipeline for tessellation control shader");
5707 d->tess.enabled = false;
5708 d->tess.failed = true;
5709 return false;
5710 }
5711
5712 // Pipeline #3 is a render pipeline with the tessellation evaluation (vertex) + the fragment shader
5713 id<MTLLibrary> tessEvalLib = rhiD->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
5714 if (!tessEvalLib) {
5715 qWarning("MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
5716 d->tess.enabled = false;
5717 d->tess.failed = true;
5718 return false;
5719 }
5720 id<MTLFunction> tessEvalFunc = rhiD->d->createMSLShaderFunction(tessEvalLib, entryPoint);
5721 if (!tessEvalFunc) {
5722 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5723 [tessEvalLib release];
5724 d->tess.enabled = false;
5725 d->tess.failed = true;
5726 return false;
5727 }
5728 d->tess.vertTese.lib = tessEvalLib;
5729 d->tess.vertTese.func = tessEvalFunc;
5730 d->tess.vertTese.desc = tese.description();
5731 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
5732 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
5733
5734 id<MTLLibrary> fragLib = rhiD->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
5735 if (!fragLib) {
5736 qWarning("MSL shader compilation failed for fragment shader: %s", qPrintable(error));
5737 d->tess.enabled = false;
5738 d->tess.failed = true;
5739 return false;
5740 }
5741 id<MTLFunction> fragFunc = rhiD->d->createMSLShaderFunction(fragLib, entryPoint);
5742 if (!fragFunc) {
5743 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5744 [fragLib release];
5745 d->tess.enabled = false;
5746 d->tess.failed = true;
5747 return false;
5748 }
5749 d->fs.lib = fragLib;
5750 d->fs.func = fragFunc;
5751 d->fs.desc = tessFrag.description();
5752 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
5753 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
5754
5755 if (!d->tess.teseFragRenderPipeline(rhiD, this)) {
5756 qWarning("Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
5757 d->tess.enabled = false;
5758 d->tess.failed = true;
5759 return false;
5760 }
5761
5762 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5763 setupMetalDepthStencilDescriptor(dsDesc);
5764 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5765 [dsDesc release];
5766
5767 // no primitiveType
5768 mapStates();
5769
5770 return true;
5771}
5772
5774{
5775 destroy(); // no early test, always invoke and leave it to destroy to decide what to clean up
5776
5778 rhiD->pipelineCreationStart();
5779 if (!rhiD->sanityCheckGraphicsPipeline(this))
5780 return false;
5781
5782 // See if tessellation is involved. Things will be very different, if so.
5783 QShader tessVert;
5784 QShader tesc;
5785 QShader tese;
5786 QShader tessFrag;
5787 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5788 switch (shaderStage.type()) {
5790 tessVert = shaderStage.shader();
5791 break;
5793 tesc = shaderStage.shader();
5794 break;
5796 tese = shaderStage.shader();
5797 break;
5799 tessFrag = shaderStage.shader();
5800 break;
5801 default:
5802 break;
5803 }
5804 }
5805 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
5806 d->tess.failed = false;
5807
5808 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
5809 if (!ok)
5810 return false;
5811
5812 // SPIRV-Cross buffer size buffers
5813 int buffers = 0;
5815 if (d->tess.enabled) {
5816 shaders.append(&d->tess.compVs[0]);
5817 shaders.append(&d->tess.compVs[1]);
5818 shaders.append(&d->tess.compVs[2]);
5819 shaders.append(&d->tess.compTesc);
5820 shaders.append(&d->tess.vertTese);
5821 } else {
5822 shaders.append(&d->vs);
5823 }
5824 shaders.append(&d->fs);
5825
5826 for (QMetalShader *shader : shaders) {
5827 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
5828 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
5829 shader->nativeResourceBindingMap[binding] = qMakePair(binding, -1);
5830 int maxNativeBinding = 0;
5831 for (const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
5832 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
5833
5834 // we use one buffer to hold data for all graphics shader stages, each with a different offset.
5835 // buffer offsets must be 32byte aligned - adjust buffer count accordingly
5836 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
5837 }
5838 }
5839
5840 if (buffers) {
5841 if (!d->bufferSizeBuffer)
5842 d->bufferSizeBuffer = new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers * sizeof(int));
5843
5844 d->bufferSizeBuffer->setSize(buffers * sizeof(int));
5845 d->bufferSizeBuffer->create();
5846 }
5847
5848 rhiD->pipelineCreationEnd();
5849 lastActiveFrameSlot = -1;
5850 generation += 1;
5851 rhiD->registerResource(this);
5852 return true;
5853}
5854
5856 : QRhiComputePipeline(rhi),
5858{
5859}
5860
5862{
5863 destroy();
5864 delete d;
5865}
5866
5868{
5869 d->cs.destroy();
5870
5871 if (!d->ps)
5872 return;
5873
5874 delete d->bufferSizeBuffer;
5875 d->bufferSizeBuffer = nullptr;
5876
5879 e.lastActiveFrameSlot = lastActiveFrameSlot;
5880 e.computePipeline.pipelineState = d->ps;
5881 d->ps = nil;
5882
5884 if (rhiD) {
5885 rhiD->d->releaseQueue.append(e);
5886 rhiD->unregisterResource(this);
5887 }
5888}
5889
5890void QRhiMetalData::trySeedingComputePipelineFromBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
5891{
5892 if (@available(macOS 11.0, iOS 14.0, *)) {
5893 if (binArch) {
5894 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5895 cpDesc.binaryArchives = binArchArray;
5896 }
5897 }
5898}
5899
5900void QRhiMetalData::addComputePipelineToBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
5901{
5902 if (@available(macOS 11.0, iOS 14.0, *)) {
5903 if (canAddToBinaryArchive(this)) {
5904 NSError *err = nil;
5905 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
5906 const QString msg = QString::fromNSString(err.localizedDescription);
5907 qWarning("Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
5908 }
5909 }
5910 }
5911}
5912
5914{
5915 if (d->ps)
5916 destroy();
5917
5919 rhiD->pipelineCreationStart();
5920
5921 auto cacheIt = rhiD->d->shaderCache.constFind(m_shaderStage);
5922 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5923 d->cs = *cacheIt;
5924 } else {
5926 QString error;
5927 QByteArray entryPoint;
5928 QShaderKey activeKey;
5929 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
5930 &error, &entryPoint, &activeKey);
5931 if (!lib) {
5932 qWarning("MSL shader compilation failed: %s", qPrintable(error));
5933 return false;
5934 }
5935 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5936 if (!func) {
5937 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5938 [lib release];
5939 return false;
5940 }
5941 d->cs.lib = lib;
5942 d->cs.func = func;
5943 d->cs.localSize = shader.description().computeShaderLocalSize();
5944 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5945 d->cs.desc = shader.description();
5946 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5947
5948 // SPIRV-Cross buffer size buffers
5951 d->cs.nativeResourceBindingMap[binding] = qMakePair(binding, -1);
5952 }
5953
5954 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5955 for (QMetalShader &s : rhiD->d->shaderCache)
5956 s.destroy();
5957 rhiD->d->shaderCache.clear();
5958 }
5959 rhiD->d->shaderCache.insert(m_shaderStage, d->cs);
5960 }
5961
5962 [d->cs.lib retain];
5963 [d->cs.func retain];
5964
5965 d->localSize = MTLSizeMake(d->cs.localSize[0], d->cs.localSize[1], d->cs.localSize[2]);
5966
5967 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor new];
5968 cpDesc.computeFunction = d->cs.func;
5969
5970 rhiD->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5971
5972 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5973 rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
5974
5975 NSError *err = nil;
5976 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5977 options: MTLPipelineOptionNone
5978 reflection: nil
5979 error: &err];
5980 [cpDesc release];
5981 if (!d->ps) {
5982 const QString msg = QString::fromNSString(err.localizedDescription);
5983 qWarning("Failed to create compute pipeline state: %s", qPrintable(msg));
5984 return false;
5985 }
5986
5987 // SPIRV-Cross buffer size buffers
5989 int buffers = 0;
5992
5993 buffers += 1;
5994
5995 if (!d->bufferSizeBuffer)
5997
5998 d->bufferSizeBuffer->setSize(buffers * sizeof(int));
6000 }
6001
6002 rhiD->pipelineCreationEnd();
6004 generation += 1;
6005 rhiD->registerResource(this);
6006 return true;
6007}
6008
6010 : QRhiCommandBuffer(rhi),
6012{
6013 resetState();
6014}
6015
6017{
6018 destroy();
6019 delete d;
6020}
6021
6023{
6024 // nothing to do here, we do not own the MTL cb object
6025}
6026
6028{
6029 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6030 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6031 return &nativeHandlesStruct;
6032}
6033
6034void QMetalCommandBuffer::resetState(double lastGpuTime)
6035{
6036 d->lastGpuTime = lastGpuTime;
6040 d->currentPassRpDesc = nil;
6042}
6043
6045{
6047 currentTarget = nullptr;
6049}
6050
6052{
6053 currentGraphicsPipeline = nullptr;
6054 currentComputePipeline = nullptr;
6056 currentGraphicsSrb = nullptr;
6057 currentComputeSrb = nullptr;
6059 currentResSlot = -1;
6060 currentIndexBuffer = nullptr;
6063 currentCullMode = -1;
6066 currentDepthBiasValues = { 0.0f, 0.0f };
6067
6073}
6074
6076 : QRhiSwapChain(rhi),
6077 rtWrapper(rhi, this),
6078 cbWrapper(rhi),
6080{
6081 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6082 d->sem[i] = nullptr;
6083 d->msaaTex[i] = nil;
6084 }
6085}
6086
6088{
6089 destroy();
6090 delete d;
6091}
6092
6094{
6095 if (!d->layer)
6096 return;
6097
6098 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6099 if (d->sem[i]) {
6100 // the semaphores cannot be released if they do not have the initial value
6102
6103 dispatch_release(d->sem[i]);
6104 d->sem[i] = nullptr;
6105 }
6106 }
6107
6108 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6109 [d->msaaTex[i] release];
6110 d->msaaTex[i] = nil;
6111 }
6112
6113#ifdef Q_OS_MACOS
6114 d->liveResizeStartObserver.remove();
6115 d->liveResizeEndObserver.remove();
6116 d->liveResizeObserverSet = false;
6117#endif
6118
6119 d->layer = nullptr;
6120
6122 d->curDrawable = nil;
6123
6125 if (rhiD) {
6126 rhiD->swapchains.remove(this);
6127 rhiD->unregisterResource(this);
6128 }
6129}
6130
6132{
6133 return &cbWrapper;
6134}
6135
6137{
6138 return &rtWrapper;
6139}
6140
6141// view.layer should ideally be called on the main thread, otherwise the UI
6142// Thread Checker in Xcode drops a warning. Hence trying to proxy it through
6143// QRhiSwapChainProxyData instead of just calling this function directly.
6144static inline CAMetalLayer *layerForWindow(QWindow *window)
6145{
6147#ifdef Q_OS_MACOS
6148 NSView *view = reinterpret_cast<NSView *>(window->winId());
6149#else
6150 UIView *view = reinterpret_cast<UIView *>(window->winId());
6151#endif
6152 Q_ASSERT(view);
6153 return static_cast<CAMetalLayer *>(view.layer);
6154}
6155
6156// If someone calls this, it is hopefully from the main thread, and they will
6157// then set the returned data on the QRhiSwapChain, so it won't need to query
6158// the layer on its own later on.
6160{
6162 d.reserved[0] = layerForWindow(window);
6163 return d;
6164}
6165
6167{
6169 CAMetalLayer *layer = d->layer;
6170 if (!layer)
6171 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6172
6173 Q_ASSERT(layer);
6174 int height = (int)layer.bounds.size.height;
6175 int width = (int)layer.bounds.size.width;
6176 width *= layer.contentsScale;
6177 height *= layer.contentsScale;
6178 return QSize(width, height);
6179}
6180
6182{
6183 if (f == HDRExtendedSrgbLinear) {
6184 if (@available(macOS 10.11, iOS 16.0, *))
6186 else
6187 return false;
6188 }
6189 return f == SDR;
6190}
6191
6193{
6195
6196 chooseFormats(); // ensure colorFormat and similar are filled out
6197
6199 rpD->colorAttachmentCount = 1;
6200 rpD->hasDepthStencil = m_depthStencil != nullptr;
6201
6202 rpD->colorFormat[0] = int(d->colorFormat);
6203
6204#ifdef Q_OS_MACOS
6205 // m_depthStencil may not be built yet so cannot rely on computed fields in it
6206 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6207 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6208#else
6209 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6210#endif
6211
6213
6214 rhiD->registerResource(rpD, false);
6215 return rpD;
6216}
6217
6219{
6221 samples = rhiD->effectiveSampleCount(m_sampleCount);
6222 // pick a format that is allowed for CAMetalLayer.pixelFormat
6224 d->colorFormat = MTLPixelFormatRGBA16Float;
6226 return;
6227 }
6228 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6230}
6231
6233{
6234 // wait+signal is the general pattern to ensure the commands for a
6235 // given frame slot have completed (if sem is 1, we go 0 then 1; if
6236 // sem is 0 we go -1, block, completion increments to 0, then us to 1)
6237
6238 dispatch_semaphore_t sem = d->sem[slot];
6239 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6240 dispatch_semaphore_signal(sem);
6241}
6242
6244{
6246
6247 const bool needsRegistration = !window || window != m_window;
6248
6249 if (window && window != m_window)
6250 destroy();
6251 // else no destroy(), this is intentional
6252
6254 if (needsRegistration)
6255 rhiD->swapchains.insert(this);
6256
6257 window = m_window;
6258
6260 qWarning("QMetalSwapChain only supports MetalSurface windows");
6261 return false;
6262 }
6263
6264 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6265 Q_ASSERT(d->layer);
6266
6267 chooseFormats();
6268 if (d->colorFormat != d->layer.pixelFormat)
6269 d->layer.pixelFormat = d->colorFormat;
6270
6272 if (@available(macOS 10.11, iOS 16.0, *)) {
6273 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6274 d->layer.wantsExtendedDynamicRangeContent = YES;
6275 }
6276 }
6277
6278 if (m_flags.testFlag(UsedAsTransferSource))
6279 d->layer.framebufferOnly = NO;
6280
6281#ifdef Q_OS_MACOS
6282 if (m_flags.testFlag(NoVSync))
6283 d->layer.displaySyncEnabled = NO;
6284#endif
6285
6286 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6287 d->layer.opaque = NO;
6288 } else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6289 // The CoreAnimation compositor is said to expect premultiplied alpha,
6290 // so this is then wrong when it comes to the blending operations but
6291 // there's nothing we can do. Fortunately Qt Quick always outputs
6292 // premultiplied alpha so it is not a problem there.
6293 d->layer.opaque = NO;
6294 } else {
6295 d->layer.opaque = YES;
6296 }
6297
6298 // Now set the layer's drawableSize which will stay set to the same value
6299 // until the next createOrResize(), thus ensuring atomicity with regards to
6300 // the drawable size in frames.
6301 int width = (int)d->layer.bounds.size.width;
6302 int height = (int)d->layer.bounds.size.height;
6303 CGSize layerSize = CGSizeMake(width, height);
6304 const float scaleFactor = d->layer.contentsScale;
6305 layerSize.width *= scaleFactor;
6306 layerSize.height *= scaleFactor;
6307 d->layer.drawableSize = layerSize;
6308
6309 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6311
6312 [d->layer setDevice: rhiD->d->dev];
6313
6314#ifdef Q_OS_MACOS
6315 // Can only use presentsWithTransaction (to get smooth resizing) when
6316 // presenting from the main (gui) thread. We predict that based on the
6317 // thread this function is called on since if the QRhiSwapChain is
6318 // initialied on a given thread then that's almost certainly the thread on
6319 // which the QRhi renders and presents.
6320 const bool canUsePresentsWithTransaction = NSThread.isMainThread;
6321
6322 // Have an env.var. just in case it turns out presentsWithTransaction is
6323 // not desired in some specific case.
6324 static bool allowPresentsWithTransaction = !qEnvironmentVariableIntValue("QT_MTL_NO_TRANSACTION");
6325
6326 if (allowPresentsWithTransaction && canUsePresentsWithTransaction && !d->liveResizeObserverSet) {
6327 d->liveResizeObserverSet = true;
6328 NSView *view = reinterpret_cast<NSView *>(window->winId());
6329 NSWindow *window = view.window;
6330 if (window) {
6331 qCDebug(QRHI_LOG_INFO, "will set presentsWithTransaction during live resize");
6332 d->liveResizeStartObserver = QMacNotificationObserver(window, NSWindowWillStartLiveResizeNotification, [this] {
6333 d->layer.presentsWithTransaction = true;
6334 });
6335 d->liveResizeEndObserver = QMacNotificationObserver(window, NSWindowDidEndLiveResizeNotification, [this] {
6336 d->layer.presentsWithTransaction = false;
6337 });
6338 }
6339 }
6340#endif
6341
6343 d->curDrawable = nil;
6344
6345 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6346 d->lastGpuTime[i] = 0;
6347 if (!d->sem[i])
6348 d->sem[i] = dispatch_semaphore_create(QMTL_FRAMES_IN_FLIGHT - 1);
6349 }
6350
6351 currentFrameSlot = 0;
6352 frameCount = 0;
6353
6356 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6358 }
6362 if (!m_depthStencil->create())
6363 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6365 } else {
6366 qWarning("Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6369 }
6370 }
6371
6372 rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
6374 rtWrapper.d->dpr = scaleFactor;
6377 rtWrapper.d->dsAttCount = ds ? 1 : 0;
6378
6379 qCDebug(QRHI_LOG_INFO, "got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6380 pixelSize.width(), pixelSize.height(), scaleFactor);
6381
6382 if (samples > 1) {
6383 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6384 desc.textureType = MTLTextureType2DMultisample;
6385 desc.pixelFormat = d->colorFormat;
6386 desc.width = NSUInteger(pixelSize.width());
6387 desc.height = NSUInteger(pixelSize.height());
6388 desc.sampleCount = NSUInteger(samples);
6389 desc.resourceOptions = MTLResourceStorageModePrivate;
6390 desc.storageMode = MTLStorageModePrivate;
6391 desc.usage = MTLTextureUsageRenderTarget;
6392 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6393 [d->msaaTex[i] release];
6394 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6395 }
6396 [desc release];
6397 }
6398
6399 if (needsRegistration)
6400 rhiD->registerResource(this);
6401
6402 return true;
6403}
6404
6406{
6409 info.limits.colorComponentValue.maxColorComponentValue = 1;
6410 info.isHardCodedDefaults = true;
6411
6412 if (m_window) {
6413 // Must use m_window, not window, given this may be called before createOrResize().
6414#ifdef Q_OS_MACOS
6415 NSView *view = reinterpret_cast<NSView *>(m_window->winId());
6416 NSScreen *screen = view.window.screen;
6417 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6418 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6419 info.isHardCodedDefaults = false;
6420#else
6421 if (@available(iOS 16.0, *)) {
6422 UIView *view = reinterpret_cast<UIView *>(m_window->winId());
6423 UIScreen *screen = view.window.windowScene.screen;
6424 info.limits.colorComponentValue.maxColorComponentValue = view.window.windowScene.screen.currentEDRHeadroom;
6425 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.potentialEDRHeadroom;
6426 info.isHardCodedDefaults = false;
6427 }
6428#endif
6429 }
6430
6431 return info;
6432}
6433
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:479
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
float greenF() const noexcept
Returns the green color component of this color.
Definition qcolor.cpp:1643
float redF() const noexcept
Returns the red color component of this color.
Definition qcolor.cpp:1611
float alphaF() const noexcept
Returns the alpha color component of this color.
Definition qcolor.cpp:1497
float blueF() const noexcept
Returns the blue color component of this color.
Definition qcolor.cpp:1675
void close() override
Calls QFileDevice::flush() and closes the file.
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString absoluteFilePath() const
Returns an absolute path including the file name.
\inmodule QtCore
Definition qfile.h:93
\inmodule QtCore
Definition qhash.h:818
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:949
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
\inmodule QtGui
Definition qimage.h:37
qsizetype sizeInBytes() const
Definition qimage.cpp:1526
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition qimage.cpp:1197
Definition qlist.h:74
qsizetype count() const noexcept
Definition qlist.h:387
void append(parameter_type t)
Definition qlist.h:441
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:356
bool contains(const Key &key) const
Definition qmap.h:340
const_iterator cend() const
Definition qmap.h:604
size_type remove(const Key &key)
Definition qmap.h:299
const_iterator constFind(const Key &key) const
Definition qmap.h:654
void clear()
Definition qmap.h:288
bool isEmpty() const
Definition qmap.h:268
T & first()
Definition qmap.h:418
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
constexpr int majorVersion() const
Returns the major version number, that is, the first segment of the operating system's version number...
constexpr int minorVersion() const
Returns the minor version number, that is, the second segment of the operating system's version numbe...
static QOperatingSystemVersion current()
[0]
\inmodule QtCore\reentrant
Definition qpoint.h:23
constexpr bool isNull() const noexcept
Returns true if both the x and y coordinates are set to 0, otherwise returns false.
Definition qpoint.h:122
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:127
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:132
quint32 size() const
Definition qrhi_p.h:344
\inmodule QtGui
Definition qrhi.h:834
UsageFlags m_usage
Definition qrhi.h:876
Type m_type
Definition qrhi.h:875
quint32 size() const
Definition qrhi.h:863
Type
Specifies storage type of buffer resource.
Definition qrhi.h:836
@ Dynamic
Definition qrhi.h:839
@ Static
Definition qrhi.h:838
@ UniformBuffer
Definition qrhi.h:845
@ StorageBuffer
Definition qrhi.h:846
void setSize(quint32 sz)
Sets the size of the buffer in bytes.
Definition qrhi.h:864
quint32 m_size
Definition qrhi.h:877
\inmodule QtGui
Definition qrhi.h:568
QRhiRenderBuffer * renderBuffer() const
Definition qrhi.h:577
QRhiTexture * texture() const
Definition qrhi.h:574
\inmodule QtGui
Definition qrhi.h:1614
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1643
QPair< int, quint32 > DynamicOffset
Synonym for QPair<int, quint32>.
Definition qrhi.h:1639
IndexFormat
Specifies the index data type.
Definition qrhi.h:1616
\inmodule QtGui
Definition qrhi.h:1585
QRhiShaderStage m_shaderStage
Definition qrhi.h:1607
QRhiShaderResourceBindings * m_shaderResourceBindings
Definition qrhi.h:1608
\inmodule QtGui
Definition qrhi.h:44
float depthClearValue() const
Definition qrhi.h:49
quint32 stencilClearValue() const
Definition qrhi.h:52
\inmodule QtGui
Definition qrhi.h:1241
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1474
quint32 m_stencilReadMask
Definition qrhi.h:1462
BlendOp
Specifies the blend operation.
Definition qrhi.h:1302
void setCullMode(CullMode mode)
Sets the specified face culling mode.
Definition qrhi.h:1364
PolygonMode
Specifies the polygon rasterization mode.
Definition qrhi.h:1350
BlendFactor
Specifies the blend factor.
Definition qrhi.h:1280
StencilOpState m_stencilFront
Definition qrhi.h:1460
quint32 m_stencilWriteMask
Definition qrhi.h:1463
QRhiShaderResourceBindings * shaderResourceBindings() const
Definition qrhi.h:1432
CompareOp
Specifies the depth or stencil comparison function.
Definition qrhi.h:1321
Topology m_topology
Definition qrhi.h:1452
CullMode
Specifies the culling mode.
Definition qrhi.h:1261
QVarLengthArray< QRhiShaderStage, 4 > m_shaderStages
Definition qrhi.h:1471
QRhiRenderPassDescriptor * renderPassDescriptor() const
Definition qrhi.h:1435
QVarLengthArray< TargetBlend, 8 > m_targetBlends
Definition qrhi.h:1455
QRhiShaderResourceBindings * m_shaderResourceBindings
Definition qrhi.h:1473
PolygonMode m_polygonMode
Definition qrhi.h:1469
float m_slopeScaledDepthBias
Definition qrhi.h:1467
Topology
Specifies the primitive topology.
Definition qrhi.h:1251
StencilOpState m_stencilBack
Definition qrhi.h:1461
FrontFace m_frontFace
Definition qrhi.h:1454
void setDepthBias(int bias)
Sets the depth bias.
Definition qrhi.h:1412
StencilOp
Specifies the stencil operation.
Definition qrhi.h:1332
CullMode m_cullMode
Definition qrhi.h:1453
CompareOp m_depthOp
Definition qrhi.h:1458
bool isCompressedFormat(QRhiTexture::Format format) const
Definition qrhi.cpp:7731
static const QRhiShaderResourceBinding::Data * shaderResourceBindingData(const QRhiShaderResourceBinding &binding)
Definition qrhi_p.h:210
quint32 pipelineCacheRhiId() const
Definition qrhi_p.h:186
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, QSize *blockDim) const
Definition qrhi.cpp:7738
static const int MAX_SHADER_CACHE_ENTRIES
Definition qrhi_p.h:227
static bool sortedBindingLessThan(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
Definition qrhi_p.h:220
quint32 byteSizePerVertexForVertexInputFormat(QRhiVertexInputAttribute::Format format) const
Definition qrhi.cpp:1831
QRhiVertexInputAttribute::Format shaderDescVariableFormatToVertexInputFormat(QShaderDescription::VariableType type) const
Definition qrhi.cpp:1787
qint64 totalPipelineCreationTime() const
Definition qrhi_p.h:202
void textureFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const
Definition qrhi.cpp:7858
\inmodule QtRhi
\inmodule QtRhi
static QRhiSwapChainProxyData updateSwapChainProxyData(QWindow *window)
QMetalSwapChain * currentSwapChain
bool isDeviceLost() const override
Definition qrhimetal.mm:917
QRhiMetalNativeHandles nativeHandlesStruct
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override
QRhiStats statistics() override
Definition qrhimetal.mm:896
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) override
void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD)
int ubufAlignment() const override
Definition qrhimetal.mm:686
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override
Definition qrhimetal.mm:720
void endExternal(QRhiCommandBuffer *cb) override
QRhiMetalData * d
QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice=nullptr)
Definition qrhimetal.mm:457
void beginPass(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QColor &colorClearValue, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
qsizetype subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
void beginExternal(QRhiCommandBuffer *cb) override
quint32 osMajor
void adjustForMultiViewDraw(quint32 *instanceCount, QRhiCommandBuffer *cb)
QRhiSwapChain * createSwapChain() override
Definition qrhimetal.mm:676
QRhiGraphicsPipeline * createGraphicsPipeline() override
bool create(QRhi::Flags flags) override
Definition qrhimetal.mm:519
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override
QRhi::Flags rhiFlags
void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QRhiSampler * createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w) override
static const int SUPPORTED_STAGES
void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets, bool offsetOnlyChange, const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
QRhiRenderBuffer * createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags, QRhiTexture::Format backingFormatHint) override
void setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) override
bool isYUpInNDC() const override
Definition qrhimetal.mm:696
int resourceLimit(QRhi::ResourceLimit limit) const override
Definition qrhimetal.mm:849
QRhiShaderResourceBindings * createShaderResourceBindings() override
void executeBufferHostWritesForSlot(QMetalBuffer *bufD, int slot)
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override
QMatrix4x4 clipSpaceCorrMatrix() const override
Definition qrhimetal.mm:706
const QRhiNativeHandles * nativeHandles() override
Definition qrhimetal.mm:886
void executeDeferredReleases(bool forced=false)
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QRhiComputePipeline * createComputePipeline() override
bool isClipDepthZeroToOne() const override
Definition qrhimetal.mm:701
QVector< int > supportedSampleCounts
QRhiTextureRenderTarget * createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags) override
bool isYUpInFramebuffer() const override
Definition qrhimetal.mm:691
QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override
quint32 osMinor
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
void setPipelineCacheData(const QByteArray &data) override
Definition qrhimetal.mm:987
QByteArray pipelineCacheData() override
Definition qrhimetal.mm:932
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override
void setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) override
void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override
bool importedDevice
void tessellatedDraw(const TessDrawArgs &args)
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override
void debugMarkEnd(QRhiCommandBuffer *cb) override
QRhi::FrameOpResult finish() override
QSet< QMetalSwapChain * > swapchains
bool importedCmdQueue
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override
bool makeThreadLocalNativeContextCurrent() override
Definition qrhimetal.mm:903
QRhiTexture * createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) override
QRhiBuffer * createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size) override
Definition qrhimetal.mm:681
void setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override
void releaseCachedResources() override
Definition qrhimetal.mm:909
QRhiDriverInfo driverInfoStruct
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override
struct QRhiMetal::@254 caps
void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QRhiDriverInfo driverInfo() const override
Definition qrhimetal.mm:891
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override
void draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override
void finishActiveReadbacks(bool forced=false)
static bool probe(QRhiMetalInitParams *params)
Definition qrhimetal.mm:488
int effectiveSampleCount(int sampleCount) const
Definition qrhimetal.mm:665
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override
bool isFeatureSupported(QRhi::Feature feature) const override
Definition qrhimetal.mm:753
void enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr, int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc, qsizetype *curOfs)
void destroy() override
Definition qrhimetal.mm:634
void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
\inmodule QtGui
Definition qrhi.h:765
\inmodule QtGui
Definition qrhi.h:1071
Flags flags() const
Definition qrhi.h:1098
void setPixelSize(const QSize &sz)
Sets the size (in pixels) to sz.
Definition qrhi.h:1093
QSize pixelSize() const
Definition qrhi.h:1092
int sampleCount() const
Definition qrhi.h:1095
int m_sampleCount
Definition qrhi.h:1111
QRhiTexture::Format m_backingFormatHint
Definition qrhi.h:1113
QSize m_pixelSize
Definition qrhi.h:1110
Type
Specifies the type of the renderbuffer.
Definition qrhi.h:1073
virtual bool create()=0
Creates the corresponding native graphics resources.
@ UsedWithSwapChainOnly
Definition qrhi.h:1079
\inmodule QtGui
Definition qrhi.h:1119
\inmodule QtGui
Definition qrhi.h:1135
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Sets the QRhiRenderPassDescriptor desc for use with this render target.
Definition qrhi.h:1142
virtual QSize pixelSize() const =0
QVarLengthArray< BufferOp, BUFFER_OPS_STATIC_ALLOC > bufferOps
Definition qrhi_p.h:508
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:523
\inmodule QtGui
Definition qrhi.h:1694
QByteArray m_objectName
Definition qrhi.h:830
@ SwapChainRenderTarget
Definition qrhi.h:800
@ TextureRenderTarget
Definition qrhi.h:801
quint64 m_id
Definition qrhi.h:829
virtual Type resourceType() const =0
QRhiImplementation * m_rhi
Definition qrhi.h:828
\inmodule QtGui
Definition qrhi.h:1007
Filter m_minFilter
Definition qrhi.h:1062
Filter
Specifies the minification, magnification, or mipmap filtering.
Definition qrhi.h:1009
AddressMode m_addressV
Definition qrhi.h:1065
Filter m_mipmapMode
Definition qrhi.h:1063
AddressMode m_addressU
Definition qrhi.h:1064
AddressMode
Specifies the addressing mode.
Definition qrhi.h:1015
@ ClampToEdge
Definition qrhi.h:1017
CompareOp
Specifies the texture comparison function.
Definition qrhi.h:1021
@ LessOrEqual
Definition qrhi.h:1025
@ GreaterOrEqual
Definition qrhi.h:1028
CompareOp m_compareOp
Definition qrhi.h:1067
AddressMode m_addressW
Definition qrhi.h:1066
Filter m_magFilter
Definition qrhi.h:1061
\inmodule QtGui
Definition qrhi.h:138
std::array< int, 4 > scissor() const
Definition qrhi.h:143
\inmodule QtGui
Definition qrhi.h:431
static QRhiShaderResourceBinding bufferLoad(int binding, StageFlags stage, QRhiBuffer *buf)
Definition qrhi.cpp:5712
StageFlag
Flag values to indicate which stages the shader resource is visible in.
Definition qrhi.h:446
\inmodule QtGui
Definition qrhi.h:1190
QVarLengthArray< QRhiShaderResourceBinding, BINDING_PREALLOC > m_bindings
Definition qrhi.h:1222
\inmodule QtGui
Definition qrhi.h:371
QShader::Variant shaderVariant() const
Definition qrhi.h:392
QShader shader() const
Definition qrhi.h:389
@ TessellationControl
Definition qrhi.h:375
@ TessellationEvaluation
Definition qrhi.h:376
\inmodule QtGui
Definition qrhi.h:1150
\inmodule QtGui
Definition qrhi.h:1513
QWindow * m_window
Definition qrhi.h:1572
QRhiSwapChainProxyData m_proxyData
Definition qrhi.h:1579
int m_sampleCount
Definition qrhi.h:1576
@ SurfaceHasNonPreMulAlpha
Definition qrhi.h:1517
@ UsedAsTransferSource
Definition qrhi.h:1519
@ SurfaceHasPreMulAlpha
Definition qrhi.h:1516
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1577
QSize m_currentPixelSize
Definition qrhi.h:1578
Flags m_flags
Definition qrhi.h:1573
Format
Describes the swapchain format.
Definition qrhi.h:1525
@ HDRExtendedSrgbLinear
Definition qrhi.h:1527
Format m_format
Definition qrhi.h:1574
QRhiRenderBuffer * m_depthStencil
Definition qrhi.h:1575
const QRhiColorAttachment * cbeginColorAttachments() const
Definition qrhi.h:626
QRhiTexture * depthTexture() const
Definition qrhi.h:634
const QRhiColorAttachment * cendColorAttachments() const
Definition qrhi.h:627
QRhiRenderBuffer * depthStencilBuffer() const
Definition qrhi.h:631
const QRhiColorAttachment * colorAttachmentAt(qsizetype index) const
Definition qrhi.h:628
qsizetype colorAttachmentCount() const
Definition qrhi.h:629
\inmodule QtGui
Definition qrhi.h:1161
QRhiTextureRenderTargetDescription m_desc
Definition qrhi.h:1183
QRhiTextureRenderTargetDescription description() const
Definition qrhi.h:1171
\inmodule QtGui
Definition qrhi.h:883
QSize m_pixelSize
Definition qrhi.h:995
int m_arraySize
Definition qrhi.h:997
int m_depth
Definition qrhi.h:996
Format format() const
Definition qrhi.h:960
@ ThreeDimensional
Definition qrhi.h:895
@ UsedWithLoadStore
Definition qrhi.h:892
@ MipMapped
Definition qrhi.h:888
@ RenderTarget
Definition qrhi.h:886
@ OneDimensional
Definition qrhi.h:898
@ TextureArray
Definition qrhi.h:897
@ CubeMap
Definition qrhi.h:887
Format
Specifies the texture format.
Definition qrhi.h:902
@ ASTC_10x8
Definition qrhi.h:947
@ ASTC_12x12
Definition qrhi.h:950
@ ASTC_8x5
Definition qrhi.h:942
@ ASTC_10x5
Definition qrhi.h:945
@ RGBA32F
Definition qrhi.h:914
@ ETC2_RGBA8
Definition qrhi.h:935
@ ASTC_5x5
Definition qrhi.h:939
@ ASTC_4x4
Definition qrhi.h:937
@ ASTC_6x6
Definition qrhi.h:941
@ ASTC_12x10
Definition qrhi.h:949
@ ETC2_RGB8
Definition qrhi.h:933
@ ASTC_5x4
Definition qrhi.h:938
@ RED_OR_ALPHA8
Definition qrhi.h:911
@ ASTC_6x5
Definition qrhi.h:940
@ ASTC_8x8
Definition qrhi.h:944
@ RGBA16F
Definition qrhi.h:913
@ RGB10A2
Definition qrhi.h:918
@ ASTC_10x6
Definition qrhi.h:946
@ ASTC_10x10
Definition qrhi.h:948
@ UnknownFormat
Definition qrhi.h:903
@ ETC2_RGB8A1
Definition qrhi.h:934
@ ASTC_8x6
Definition qrhi.h:943
Flags flags() const
Definition qrhi.h:980
QSize pixelSize() const
Definition qrhi.h:963
Format m_format
Definition qrhi.h:994
Flags m_flags
Definition qrhi.h:999
int m_sampleCount
Definition qrhi.h:998
Format
Specifies the type of the element data.
Definition qrhi.h:234
\inmodule QtGui
Definition qrhi.h:313
const QRhiVertexInputAttribute * cendAttributes() const
Definition qrhi.h:337
const QRhiVertexInputBinding * cendBindings() const
Definition qrhi.h:325
const QRhiVertexInputAttribute * cbeginAttributes() const
Definition qrhi.h:336
const QRhiVertexInputBinding * cbeginBindings() const
Definition qrhi.h:324
\inmodule QtGui
Definition qrhi.h:85
@ Metal
Definition qrhi.h:1774
ResourceLimit
Describes the resource limit to query.
Definition qrhi.h:1846
@ MaxThreadsPerThreadGroup
Definition qrhi.h:1853
@ MaxThreadGroupZ
Definition qrhi.h:1856
@ FramesInFlight
Definition qrhi.h:1850
@ TextureSizeMin
Definition qrhi.h:1847
@ MaxThreadGroupsPerDimension
Definition qrhi.h:1852
@ MaxAsyncReadbackFrames
Definition qrhi.h:1851
@ TextureArraySizeMax
Definition qrhi.h:1857
@ MaxColorAttachments
Definition qrhi.h:1849
@ MaxThreadGroupY
Definition qrhi.h:1855
@ MaxVertexInputs
Definition qrhi.h:1859
@ MaxThreadGroupX
Definition qrhi.h:1854
@ TextureSizeMax
Definition qrhi.h:1848
@ MaxVertexOutputs
Definition qrhi.h:1860
@ MaxUniformBufferRange
Definition qrhi.h:1858
@ SkipPresent
Definition qrhi.h:1842
Feature
Flag values to indicate what features are supported by the backend currently in use.
Definition qrhi.h:1793
@ HalfAttributes
Definition qrhi.h:1831
@ CustomInstanceStepRate
Definition qrhi.h:1799
@ NonDynamicUniformBuffers
Definition qrhi.h:1801
@ ElementIndexUint
Definition qrhi.h:1805
@ RenderToNonBaseMipLevel
Definition qrhi.h:1815
@ MultisampleRenderBuffer
Definition qrhi.h:1795
@ RenderTo3DTextureSlice
Definition qrhi.h:1823
@ Tessellation
Definition qrhi.h:1825
@ IntAttributes
Definition qrhi.h:1816
@ TextureArrays
Definition qrhi.h:1824
@ PipelineCacheDataLoadSave
Definition qrhi.h:1819
@ ReadBackNonUniformBuffer
Definition qrhi.h:1812
@ MultiView
Definition qrhi.h:1834
@ TexelFetch
Definition qrhi.h:1814
@ TextureArrayRange
Definition qrhi.h:1827
@ RenderToOneDimensionalTexture
Definition qrhi.h:1832
@ BaseVertex
Definition qrhi.h:1809
@ GeometryShader
Definition qrhi.h:1826
@ Compute
Definition qrhi.h:1806
@ OneDimensionalTextureMipmaps
Definition qrhi.h:1830
@ WideLines
Definition qrhi.h:1807
@ TriangleFanTopology
Definition qrhi.h:1811
@ OneDimensionalTextures
Definition qrhi.h:1829
@ ImageDataStride
Definition qrhi.h:1820
@ BaseInstance
Definition qrhi.h:1810
@ DebugMarkers
Definition qrhi.h:1796
@ ReadBackNonBaseMipLevel
Definition qrhi.h:1813
@ MultisampleTexture
Definition qrhi.h:1794
@ ThreeDimensionalTextureMipmaps
Definition qrhi.h:1833
@ NonFourAlignedEffectiveIndexBufferOffset
Definition qrhi.h:1802
@ RedOrAlpha8IsRed
Definition qrhi.h:1804
@ NonFillPolygonMode
Definition qrhi.h:1828
@ Timestamps
Definition qrhi.h:1797
@ ThreeDimensionalTextures
Definition qrhi.h:1822
@ PrimitiveRestart
Definition qrhi.h:1800
@ ReadBackAnyTextureFormat
Definition qrhi.h:1818
@ RenderBufferImport
Definition qrhi.h:1821
@ ScreenSpaceDerivatives
Definition qrhi.h:1817
@ VertexShaderPointSize
Definition qrhi.h:1808
@ NPOTTextureRepeat
Definition qrhi.h:1803
@ Instancing
Definition qrhi.h:1798
static const int MAX_MIP_LEVELS
Definition qrhi.h:1955
FrameOpResult
Describes the result of operations that can have a soft failure.
Definition qrhi.h:1786
@ FrameOpSuccess
Definition qrhi.h:1787
@ EnablePipelineCacheDataSave
Definition qrhi.h:1781
Definition qset.h:18
bool contains(const T &value) const
Definition qset.h:71
\inmodule QtGui
Definition qshader.h:60
QByteArray shader() const
Definition qshader.h:65
QByteArray entryPoint() const
Definition qshader.h:68
TessellationWindingOrder
\value UnknownTessellationWindingOrder \value CwTessellationWindingOrder \value CcwTessellationWindin...
QList< StorageBlock > storageBlocks() const
TessellationPartitioning
\value UnknownTessellationPartitioning \value EqualTessellationPartitioning \value FractionalEvenTess...
uint tessellationOutputVertexCount() const
\inmodule QtGui
Definition qshader.h:174
\inmodule QtGui
Definition qshader.h:32
int version() const
Definition qshader.h:42
\inmodule QtGui
Definition qshader.h:81
QShaderCode shader(const QShaderKey &key) const
Definition qshader.cpp:365
QList< QShaderKey > availableShaders() const
Definition qshader.cpp:357
NativeResourceBindingMap nativeResourceBindingMap(const QShaderKey &key) const
Definition qshader.cpp:994
NativeShaderInfo nativeShaderInfo(const QShaderKey &key) const
\variable QShader::NativeShaderInfo::flags
Definition qshader.cpp:1152
Variant
Describes what kind of shader code an entry contains.
Definition qshader.h:103
@ UInt32IndexedVertexAsComputeShader
Definition qshader.h:107
@ NonIndexedVertexAsComputeShader
Definition qshader.h:108
@ UInt16IndexedVertexAsComputeShader
Definition qshader.h:106
@ StandardShader
Definition qshader.h:104
@ MetalLibShader
Definition qshader.h:99
@ MslShader
Definition qshader.h:97
QShaderDescription description() const
Definition qshader.cpp:340
bool isValid() const
Definition qshader.cpp:313
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:123
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
QByteArray toUtf8() const &
Definition qstring.h:563
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7005
@ MetalSurface
Definition qsurface.h:36
\inmodule QtCore \reentrant
QString fileName() const override
Returns the complete unique filename backing the QTemporaryFile object.
bool open()
A QTemporaryFile will always be opened in QIODevice::ReadWrite mode, this allows easy access to the d...
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3354
constexpr size_type size() const noexcept
bool isEmpty() const
const T & at(qsizetype idx) const
void resize(qsizetype sz)
const_iterator cbegin() const noexcept
qsizetype count() const
const_iterator cend() const noexcept
iterator end() noexcept
void append(const T &t)
const T * constData() const
iterator begin() noexcept
\inmodule QtGui
Definition qwindow.h:63
SurfaceType surfaceType() const override
Returns the surface type of the window.
Definition qwindow.cpp:628
#define this
Definition dialogs.cpp:9
QString str
[2]
QMap< QString, QString > map
[6]
qDeleteAll(list.begin(), list.end())
double e
QSet< QString >::iterator it
uint alignment
Combined button and popup list for selecting options.
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:105
unsigned long NSUInteger
#define Q_FALLTHROUGH()
std::pair< T1, T2 > QPair
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError * error
static int instanceCount
static QString header(const QString &name)
static const qint64 headerSize
EGLOutputLayerEXT layer
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
Flags
#define qWarning
Definition qlogging.h:162
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLint location
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
GLuint const GLuint * buffers
GLint GLenum GLsizei GLsizei GLsizei depth
GLsizei samples
GLenum mode
const GLfloat * m
GLenum GLuint GLint level
GLuint64 key
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLsizei dataSize
GLuint sampler
GLenum GLuint id
[7]
GLint GLint GLint GLint GLsizei GLsizei GLsizei GLboolean commit
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLfloat GLfloat f
GLsizei levels
GLenum src
GLenum GLuint buffer
GLint GLsizei width
GLenum type
GLenum GLenum dst
GLuint GLsizei const GLchar * label
[43]
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLenum GLuint texture
GLfloat GLfloat clamp
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLuint name
GLint first
GLint GLsizei GLsizei GLenum format
GLsizei GLenum const void * indices
GLint y
GLfloat GLfloat GLfloat GLfloat h
void ** params
GLuint bindingIndex
GLbyte GLbyte blue
Definition qopenglext.h:385
GLenum func
Definition qopenglext.h:663
GLuint res
const GLubyte * c
GLuint renderbuffer
GLint void * img
Definition qopenglext.h:233
GLuint GLsizei const GLuint const GLintptr * offsets
GLuint GLsizei const GLuint const GLintptr const GLsizeiptr * sizes
GLuint shader
Definition qopenglext.h:665
GLint limit
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei GLsizei GLuint * shaders
Definition qopenglext.h:677
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
GLenum GLenum variable
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
GLbyte green
Definition qopenglext.h:385
GLenum GLenum colorFormat
GLsizeiptr const void GLenum usage
Definition qopenglext.h:543
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QRHI_RES_RHI(t)
Definition qrhi_p.h:29
#define QRHI_RES(t, x)
Definition qrhi_p.h:28
static QPair< int, int > mapBinding(int binding, int stageIndex, const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
Int aligned(Int v, Int byteAlign)
static id< MTLComputeCommandEncoder > tessellationComputeEncoder(QMetalCommandBuffer *cbD)
static MTLStencilOperation toMetalStencilOp(QRhiGraphicsPipeline::StencilOp op)
static MTLLanguageVersion toMetalLanguageVersion(const QShaderVersion &version)
static MTLPrimitiveTopologyClass toMetalPrimitiveTopologyClass(QRhiGraphicsPipeline::Topology t)
static CAMetalLayer * layerForWindow(QWindow *window)
static void addVertexAttribute(const T &variable, int binding, QRhiMetal *rhiD, int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
static void qrhimtl_releaseRenderBuffer(const QRhiMetalData::DeferredReleaseEntry &e)
static bool matches(const QList< QShaderDescription::BlockVariable > &a, const QList< QShaderDescription::BlockVariable > &b)
static MTLBlendOperation toMetalBlendOp(QRhiGraphicsPipeline::BlendOp op)
static MTLBlendFactor toMetalBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
static MTLWinding toMetalTessellationWindingOrder(QShaderDescription::TessellationWindingOrder w)
static MTLPrimitiveType toMetalPrimitiveType(QRhiGraphicsPipeline::Topology t)
static MTLCompareFunction toMetalCompareOp(QRhiGraphicsPipeline::CompareOp op)
static MTLVertexFormat toMetalAttributeFormat(QRhiVertexInputAttribute::Format format)
static void endTessellationComputeEncoding(QMetalCommandBuffer *cbD)
BindingType
static MTLTriangleFillMode toMetalTriangleFillMode(QRhiGraphicsPipeline::PolygonMode mode)
static MTLSamplerMinMagFilter toMetalFilter(QRhiSampler::Filter f)
static bool canAddToBinaryArchive(QRhiMetalData *d)
Int aligned(Int v, Int byteAlign)
Definition qrhimetal.mm:483
static MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
static void qrhimtl_releaseBuffer(const QRhiMetalData::DeferredReleaseEntry &e)
static void takeIndex(quint32 index, quint64 &indices)
static int mapBinding(int binding, int stageIndex, const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[], BindingType type)
static void rebindShaderResources(QMetalCommandBuffer *cbD, int resourceStage, int encoderStage, const QMetalShaderResourceBindingsData *customBindingState=nullptr)
static void qrhimtl_releaseSampler(const QRhiMetalData::DeferredReleaseEntry &e)
static MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags, const QRhiMetal *d)
static QRhiShaderResourceBinding::StageFlag toRhiSrbStage(int stage)
static void addUnusedVertexAttribute(const T &variable, QRhiMetal *rhiD, quint32 &offset, quint32 &vertexAlignment)
static uint toMetalColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
static MTLSamplerMipFilter toMetalMipmapMode(QRhiSampler::Filter f)
static MTLTessellationPartitionMode toMetalTessellationPartitionMode(QShaderDescription::TessellationPartitioning p)
static MTLCompareFunction toMetalTextureCompareFunction(QRhiSampler::CompareOp op)
static MTLSamplerAddressMode toMetalAddressMode(QRhiSampler::AddressMode m)
static void bindStageBuffers(QMetalCommandBuffer *cbD, int stage, const QRhiBatchedBindings< id< MTLBuffer > >::Batch &bufferBatch, const QRhiBatchedBindings< NSUInteger >::Batch &offsetBatch)
static void qrhimtl_releaseTexture(const QRhiMetalData::DeferredReleaseEntry &e)
static bool indexTaken(quint32 index, quint64 indices)
static void bindStageTextures(QMetalCommandBuffer *cbD, int stage, const QRhiBatchedBindings< id< MTLTexture > >::Batch &textureBatch)
static void bindStageSamplers(QMetalCommandBuffer *cbD, int encoderStage, const QRhiBatchedBindings< id< MTLSamplerState > >::Batch &samplerBatch)
static int nextAttributeIndex(quint64 indices)
static QT_BEGIN_NAMESPACE const int QMTL_FRAMES_IN_FLIGHT
Definition qrhimetal_p.h:23
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define qPrintable(string)
Definition qstring.h:1391
#define sp
QScreen * screen
[1]
Definition main.cpp:29
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
static const QTextHtmlElement elements[Html_NumElements]
#define Q_UNUSED(x)
@ desc
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
@ Q_RELOCATABLE_TYPE
Definition qtypeinfo.h:145
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:163
unsigned int quint32
Definition qtypes.h:45
int qint32
Definition qtypes.h:44
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
QVideoFrameFormat::PixelFormat fmt
QFileInfo info(fileName)
[8]
QUrl url("example.com")
[constructor-url-reference]
sem release()
QVariant variant
[1]
QSemaphore sem(5)
[0]
QSharedPointer< T > other(t)
[5]
view viewport() -> scroll(dx, dy, deviceRect)
aWidget window() -> setWindowTitle("New Window Title")
[2]
QQuickView * view
[0]
QJSValueList args
QVarLengthArray< BufferUpdate, 16 > pendingUpdates[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:268
id< MTLBuffer > buf[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:263
char * beginFullDynamicBufferUpdateForCurrentFrame() override
QMetalBufferData * d
Definition qrhimetal_p.h:38
QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
static constexpr int WorkBufPoolUsage
Definition qrhimetal_p.h:44
int lastActiveFrameSlot
Definition qrhimetal_p.h:40
QRhiBuffer::NativeBuffer nativeBuffer() override
void endFullDynamicBufferUpdateForCurrentFrame() override
To be called when the entire contents of the buffer data has been updated in the memory block returne...
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool create() override
Creates the corresponding native graphics resources.
id< MTLCommandBuffer > cb
Definition qrhimetal.mm:324
QRhiBatchedBindings< NSUInteger > currentVertexInputOffsets
Definition qrhimetal.mm:332
QMetalShaderResourceBindingsData currentShaderResourceBindingState
Definition qrhimetal.mm:334
id< MTLComputeCommandEncoder > tessellationComputeEncoder
Definition qrhimetal.mm:328
id< MTLRenderCommandEncoder > currentRenderPassEncoder
Definition qrhimetal.mm:326
QRhiBatchedBindings< id< MTLBuffer > > currentVertexInputsBuffers
Definition qrhimetal.mm:331
MTLRenderPassDescriptor * currentPassRpDesc
Definition qrhimetal.mm:329
id< MTLDepthStencilState > currentDepthStencilState
Definition qrhimetal.mm:333
id< MTLComputeCommandEncoder > currentComputePassEncoder
Definition qrhimetal.mm:327
QMetalBuffer * currentIndexBuffer
QRhiCommandBuffer::IndexFormat currentIndexFormat
QRhiMetalCommandBufferNativeHandles nativeHandlesStruct
QPair< float, float > currentDepthBiasValues
const QRhiNativeHandles * nativeHandles()
QMetalShaderResourceBindings * currentComputeSrb
QMetalComputePipeline * currentComputePipeline
QMetalShaderResourceBindings * currentGraphicsSrb
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QMetalCommandBuffer(QRhiImplementation *rhi)
void resetPerPassCachedState()
QMetalCommandBufferData * d
QRhiRenderTarget * currentTarget
QMetalGraphicsPipeline * currentGraphicsPipeline
void resetState(double lastGpuTime=0)
QMetalBuffer * bufferSizeBuffer
Definition qrhimetal.mm:437
id< MTLComputePipelineState > ps
Definition qrhimetal.mm:432
QMetalComputePipeline(QRhiImplementation *rhi)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QMetalComputePipelineData * d
bool create() override
QVector< QMetalBuffer * > hostVisibleWorkBuffers
Definition qrhimetal.mm:387
QMetalBuffer * acquireWorkBuffer(QRhiMetal *rhiD, quint32 size, WorkBufType type=WorkBufType::DeviceLocal)
QVector< QMetalBuffer * > deviceLocalWorkBuffers
Definition qrhimetal.mm:386
quint32 tescCompOutputBufferSize(quint32 patchCount) const
Definition qrhimetal.mm:405
quint32 tescCompPatchOutputBufferSize(quint32 patchCount) const
Definition qrhimetal.mm:409
QMetalGraphicsPipelineData * q
Definition qrhimetal.mm:390
static int vsCompVariantToIndex(QShader::Variant vertexCompVariant)
id< MTLComputePipelineState > tessControlComputeState
Definition qrhimetal.mm:397
id< MTLComputePipelineState > tescCompPipeline(QRhiMetal *rhiD)
id< MTLRenderPipelineState > teseFragRenderPipeline(QRhiMetal *rhiD, QMetalGraphicsPipeline *pipeline)
id< MTLComputePipelineState > vsCompPipeline(QRhiMetal *rhiD, QShader::Variant vertexCompVariant)
quint32 patchCountForDrawCall(quint32 vertexOrIndexCount, quint32 instanceCount) const
Definition qrhimetal.mm:414
quint32 vsCompOutputBufferSize(quint32 vertexOrIndexCount, quint32 instanceCount) const
Definition qrhimetal.mm:400
std::array< id< MTLComputePipelineState >, 3 > vertexComputeState
Definition qrhimetal.mm:396
QMetalBuffer * bufferSizeBuffer
Definition qrhimetal.mm:427
MTLPrimitiveType primitiveType
Definition qrhimetal.mm:372
id< MTLDepthStencilState > ds
Definition qrhimetal.mm:371
id< MTLRenderPipelineState > ps
Definition qrhimetal.mm:370
void setupVertexInputDescriptor(MTLVertexDescriptor *desc)
void setupStageInputDescriptor(MTLStageInputOutputDescriptor *desc)
struct QMetalGraphicsPipelineData::ExtraBufferManager extraBufMgr
MTLTriangleFillMode triangleFillMode
Definition qrhimetal.mm:375
QMetalGraphicsPipeline * q
Definition qrhimetal.mm:369
struct QMetalGraphicsPipelineData::Tessellation tess
QMetalGraphicsPipelineData * d
bool createVertexFragmentPipeline()
QMetalGraphicsPipeline(QRhiImplementation *rhi)
void setupAttachmentsInMetalRenderPassDescriptor(void *metalRpDesc, QMetalRenderPassDescriptor *rpD)
void makeActiveForCurrentRenderPassEncoder(QMetalCommandBuffer *cbD)
bool create() override
Creates the corresponding native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
void setupMetalDepthStencilDescriptor(void *metalDsDesc)
bool createTessellationPipelines(const QShader &tessVert, const QShader &tesc, const QShader &tese, const QShader &tessFrag)
MTLPixelFormat format
Definition qrhimetal.mm:273
id< MTLTexture > tex
Definition qrhimetal.mm:274
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QMetalRenderBufferData * d
Definition qrhimetal_p.h:60
QRhiTexture::Format backingFormat() const override
bool create() override
Creates the corresponding native graphics resources.
QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags, QRhiTexture::Format backingFormatHint)
QMetalRenderPassDescriptor(QRhiImplementation *rhi)
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QVector< quint32 > serializedFormatData
QVector< quint32 > serializedFormat() const override
bool isCompatible(const QRhiRenderPassDescriptor *other) const override
int colorFormat[MAX_COLOR_ATTACHMENTS]
static const int MAX_COLOR_ATTACHMENTS
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() const override
ColorAtt colorAtt[QMetalRenderPassDescriptor::MAX_COLOR_ATTACHMENTS]
Definition qrhimetal.mm:358
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList
Definition qrhimetal.mm:364
id< MTLTexture > dsTex
Definition qrhimetal.mm:359
struct QMetalRenderTargetData::@358 fb
id< MTLSamplerState > samplerState
Definition qrhimetal.mm:293
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QMetalSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, AddressMode u, AddressMode v, AddressMode w)
QMetalSamplerData * d
int lastActiveFrameSlot
bool create() override
QRhiBatchedBindings< id< MTLTexture > > textureBatches
Definition qrhimetal.mm:316
QRhiBatchedBindings< id< MTLSamplerState > > samplerBatches
Definition qrhimetal.mm:317
QVarLengthArray< Buffer, 8 > buffers
Definition qrhimetal.mm:311
QRhiBatchedBindings< NSUInteger > bufferOffsetBatches
Definition qrhimetal.mm:315
QVarLengthArray< Texture, 8 > textures
Definition qrhimetal.mm:312
QRhiBatchedBindings< id< MTLBuffer > > bufferBatches
Definition qrhimetal.mm:314
QVarLengthArray< Sampler, 8 > samplers
Definition qrhimetal.mm:313
struct QMetalShaderResourceBindingsData::Stage res[QRhiMetal::SUPPORTED_STAGES]
struct QMetalShaderResourceBindings::BoundSampledTextureData::@235 d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE]
QMetalShaderResourceBindings(QRhiImplementation *rhi)
QVarLengthArray< QRhiShaderResourceBinding, 8 > sortedBindings
QVarLengthArray< BoundResourceData, 8 > boundResourceData
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
void updateResources(UpdateFlags flags) override
\variable QRhiMetalCommandBufferNativeHandles::commandBuffer
Definition qrhimetal.mm:135
void destroy()
Definition qrhimetal.mm:144
uint outputVertexCount
Definition qrhimetal.mm:139
QShader::NativeShaderInfo nativeShaderInfo
Definition qrhimetal.mm:142
std::array< uint, 3 > localSize
Definition qrhimetal.mm:138
id< MTLFunction > func
Definition qrhimetal.mm:137
QShader::NativeResourceBindingMap nativeResourceBindingMap
Definition qrhimetal.mm:141
id< MTLLibrary > lib
Definition qrhimetal.mm:136
QShaderDescription desc
Definition qrhimetal.mm:140
double lastGpuTime[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:445
dispatch_semaphore_t sem[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:444
id< CAMetalDrawable > curDrawable
Definition qrhimetal.mm:443
MTLPixelFormat colorFormat
Definition qrhimetal.mm:449
CAMetalLayer * layer
Definition qrhimetal.mm:442
MTLRenderPassDescriptor * rp
Definition qrhimetal.mm:446
id< MTLTexture > msaaTex[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:447
QRhiTexture::Format rhiColorFormat
Definition qrhimetal.mm:448
QMetalRenderTargetData * d
QMetalSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
QSize pixelSize() const override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
float devicePixelRatio() const override
int sampleCount() const override
QWindow * window
void waitUntilCompleted(int slot)
bool createOrResize() override
Creates the swapchain if not already done and resizes the swapchain buffers to match the current size...
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QRhiCommandBuffer * currentFrameCommandBuffer() override
QMetalSwapChain(QRhiImplementation *rhi)
QMetalCommandBuffer cbWrapper
virtual QRhiSwapChainHdrInfo hdrInfo() override
\variable QRhiSwapChainHdrInfo::isHardCodedDefaults
QMetalRenderBuffer * ds
QMetalSwapChainRenderTarget rtWrapper
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
QMetalSwapChainData * d
bool isFormatSupported(Format f) override
QSize surfacePixelSize() override
QRhiRenderTarget * currentFrameRenderTarget() override
QMetalTexture * q
Definition qrhimetal.mm:281
id< MTLTexture > viewForLevel(int level)
id< MTLTexture > tex
Definition qrhimetal.mm:283
QMetalTextureData(QMetalTexture *t)
Definition qrhimetal.mm:279
id< MTLBuffer > stagingBuf[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:284
MTLPixelFormat format
Definition qrhimetal.mm:282
id< MTLTexture > perLevelViews[QRhi::MAX_MIP_LEVELS]
Definition qrhimetal.mm:286
float devicePixelRatio() const override
QMetalRenderTargetData * d
QMetalTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags)
bool create() override
Creates the corresponding native graphics resources.
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
int sampleCount() const override
QSize pixelSize() const override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, Flags flags)
bool prepareCreate(QSize *adjustedSize=nullptr)
NativeTexture nativeTexture() override
QMetalTextureData * d
Definition qrhimetal_p.h:81
bool create() override
Creates the corresponding native graphics resources.
int lastActiveFrameSlot
Definition qrhimetal_p.h:85
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool createFrom(NativeTexture src) override
Similar to create(), except that no new native textures are created.
QVarLengthArray< T, 4 > resources
Definition qrhi_p.h:556
void feed(int binding, T resource)
Definition qrhi_p.h:529
QVarLengthArray< Batch, 4 > batches
Definition qrhi_p.h:569
\inmodule QtGui
Definition qrhi.h:850
\inmodule QtGui
Definition qrhi.h:1722
QByteArray deviceName
Definition qrhi.h:1732
@ IntegratedDevice
Definition qrhi.h:1725
DeviceType deviceType
Definition qrhi.h:1735
QRhiReadbackResult * result
Definition qrhimetal.mm:240
id< MTLComputePipelineState > pipelineState
Definition qrhimetal.mm:213
id< MTLDepthStencilState > depthStencilState
Definition qrhimetal.mm:208
std::array< id< MTLComputePipelineState >, 3 > tessVertexComputeState
Definition qrhimetal.mm:209
id< MTLSamplerState > samplerState
Definition qrhimetal.mm:201
id< MTLComputePipelineState > tessTessControlComputeState
Definition qrhimetal.mm:210
id< MTLRenderPipelineState > pipelineState
Definition qrhimetal.mm:207
QMetalCommandBuffer cbWrapper
Definition qrhimetal.mm:223
OffscreenFrame(QRhiImplementation *rhi)
Definition qrhimetal.mm:220
QRhiReadbackResult * result
Definition qrhimetal.mm:229
QRhiReadbackDescription desc
Definition qrhimetal.mm:228
QRhiTexture::Format format
Definition qrhimetal.mm:233
void trySeedingRenderPipelineFromBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
QRhiMetalData(QRhiMetal *rhi)
Definition qrhimetal.mm:155
QVarLengthArray< BufferReadback, 2 > activeBufferReadbacks
Definition qrhimetal.mm:246
bool setupBinaryArchive(NSURL *sourceFileUrl=nil)
Definition qrhimetal.mm:499
id< MTLCommandQueue > cmdQueue
Definition qrhimetal.mm:159
void addRenderPipelineToBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
static const int TEXBUF_ALIGN
Definition qrhimetal.mm:251
id< MTLDevice > dev
Definition qrhimetal.mm:158
void trySeedingComputePipelineFromBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
QVarLengthArray< TextureReadback, 2 > activeTextureReadbacks
Definition qrhimetal.mm:235
id< MTLLibrary > createMetalLib(const QShader &shader, QShader::Variant shaderVariant, QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
MTLRenderPassDescriptor * createDefaultRenderPass(bool hasDepthStencil, const QColor &colorClearValue, const QRhiDepthStencilClearValue &depthStencilClearValue, int colorAttCount)
id< MTLFunction > createMSLShaderFunction(id< MTLLibrary > lib, const QByteArray &entryPoint)
id< MTLCaptureScope > captureScope
Definition qrhimetal.mm:249
struct QRhiMetalData::OffscreenFrame ofr
QRhiMetal * q
Definition qrhimetal.mm:157
QHash< QRhiShaderStage, QMetalShader > shaderCache
Definition qrhimetal.mm:253
API_AVAILABLE(macosx(11.0), ios(14.0)) id< MTLBinaryArchive > binArch
MTLCaptureManager * captureMgr
Definition qrhimetal.mm:248
bool binArchWasEmpty
Definition qrhimetal.mm:161
void addComputePipelineToBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
QVector< DeferredReleaseEntry > releaseQueue
Definition qrhimetal.mm:217
\variable QRhiReadbackResult::completed
Definition qrhi.h:788
\inmodule QtGui
Definition qrhi.h:1686
QByteArray data
Definition qrhi.h:1690
QRhiTexture::Format format
Definition qrhi.h:1688
std::function< void()> completed
Definition qrhi.h:1687
\inmodule QtGui
Definition qrhi.h:1745
\inmodule QtGui
Definition qrhi.h:1482
float maxPotentialColorComponentValue
Definition qrhi.h:1496
union QRhiSwapChainHdrInfo::@398 limits
struct QRhiSwapChainHdrInfo::@398::@400 colorComponentValue
\inmodule QtGui
Definition qrhi.h:1508
\inmodule QtGui
Definition qrhi.h:953
\variable QShaderDescription::StorageBlock::blockName
\variable QShaderDescription::PushConstantBlock::name
@ MslTessTescTessLevelBufferBinding
Definition qshader_p.h:40
@ MslMultiViewMaskBufferBinding
Definition qshader_p.h:45
@ MslTessTescInputBufferBinding
Definition qshader_p.h:43
@ MslTessTescPatchOutputBufferBinding
Definition qshader_p.h:41
@ MslBufferSizeBufferBinding
Definition qshader_p.h:44
@ MslTessVertTescOutputBufferBinding
Definition qshader_p.h:39
@ MslTessTescParamsBufferBinding
Definition qshader_p.h:42
@ MslTessVertIndicesBufferBinding
Definition qshader_p.h:38
\inmodule QtGui
Definition qshader.h:155
QMap< int, int > extraBufferBindings
Definition qshader.h:157
Definition moc.h:24