Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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// Even though the macOS 13 MTLBinaryArchive problem (QTBUG-106703) seems
44// to be solved in later 13.x releases, we have reports from old Intel hardware
45// and older macOS versions where this causes problems (QTBUG-114338).
46// Thus we no longer do OS version based differentiation, but rather have a
47// single toggle that is currently on, and so QRhi::(set)pipelineCache()
48// does nothing with Metal.
49#define QRHI_METAL_DISABLE_BINARY_ARCHIVE
50
51// We should be able to operate with command buffers that do not automatically
52// retain/release the resources used by them. (since we have logic that mirrors
53// other backends such as the Vulkan one anyway)
54#define QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
55
146{
147 id<MTLLibrary> lib = nil;
148 id<MTLFunction> func = nil;
149 std::array<uint, 3> localSize = {};
154
155 void destroy() {
157 [lib release];
158 lib = nil;
159 [func release];
160 func = nil;
161 }
162};
163
165{
166 QRhiMetalData(QRhiMetal *rhi) : q(rhi), ofr(rhi) { }
167
169 id<MTLDevice> dev = nil;
170 id<MTLCommandQueue> cmdQueue = nil;
171 API_AVAILABLE(macosx(11.0), ios(14.0)) id<MTLBinaryArchive> binArch = nil;
172
173 id<MTLCommandBuffer> newCommandBuffer();
174 MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil,
175 const QColor &colorClearValue,
176 const QRhiDepthStencilClearValue &depthStencilClearValue,
177 int colorAttCount);
178 id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
179 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
180 id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
181 bool setupBinaryArchive(NSURL *sourceFileUrl = nil);
182 void addRenderPipelineToBinaryArchive(MTLRenderPipelineDescriptor *rpDesc);
183 void trySeedingRenderPipelineFromBinaryArchive(MTLRenderPipelineDescriptor *rpDesc);
184 void addComputePipelineToBinaryArchive(MTLComputePipelineDescriptor *cpDesc);
185 void trySeedingComputePipelineFromBinaryArchive(MTLComputePipelineDescriptor *cpDesc);
186
198 int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
199 union {
200 struct {
203 struct {
204 id<MTLTexture> texture;
206 struct {
207 id<MTLTexture> texture;
208 id<MTLBuffer> stagingBuffers[QMTL_FRAMES_IN_FLIGHT];
209 id<MTLTexture> views[QRhi::MAX_MIP_LEVELS];
211 struct {
212 id<MTLSamplerState> samplerState;
214 struct {
215 id<MTLBuffer> buffer;
216 } stagingBuffer;
217 struct {
218 id<MTLRenderPipelineState> pipelineState;
219 id<MTLDepthStencilState> depthStencilState;
220 std::array<id<MTLComputePipelineState>, 3> tessVertexComputeState;
221 id<MTLComputePipelineState> tessTessControlComputeState;
222 } graphicsPipeline;
223 struct {
224 id<MTLComputePipelineState> pipelineState;
225 } computePipeline;
226 };
227 };
228 QVector<DeferredReleaseEntry> releaseQueue;
229
236
246 QVarLengthArray<TextureReadback, 2> activeTextureReadbacks;
247
256
257 QVarLengthArray<BufferReadback, 2> activeBufferReadbacks;
258
259 MTLCaptureManager *captureMgr;
260 id<MTLCaptureScope> captureScope = nil;
261
262 static const int TEXBUF_ALIGN = 256; // probably not accurate
263
264 QHash<QRhiShaderStage, QMetalShader> shaderCache;
265};
266
269
271{
279 QVarLengthArray<BufferUpdate, 16> pendingUpdates[QMTL_FRAMES_IN_FLIGHT];
280};
281
283{
284 MTLPixelFormat format;
285 id<MTLTexture> tex = nil;
286};
287
289{
291
293 MTLPixelFormat format;
294 id<MTLTexture> tex = nil;
296 bool owns = true;
298
299 id<MTLTexture> viewForLevel(int level);
300};
301
303{
304 id<MTLSamplerState> samplerState = nil;
305};
306
308 struct Stage {
309 struct Buffer {
311 id<MTLBuffer> mtlbuf;
313 };
314 struct Texture {
316 id<MTLTexture> mtltex;
317 };
318 struct Sampler {
320 id<MTLSamplerState> mtlsampler;
321 };
322 QVarLengthArray<Buffer, 8> buffers;
323 QVarLengthArray<Texture, 8> textures;
324 QVarLengthArray<Sampler, 8> samplers;
325 QRhiBatchedBindings<id<MTLBuffer> > bufferBatches;
326 QRhiBatchedBindings<NSUInteger> bufferOffsetBatches;
327 QRhiBatchedBindings<id<MTLTexture> > textureBatches;
328 QRhiBatchedBindings<id<MTLSamplerState> > samplerBatches;
330 enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2, TESSCTRL = 3, TESSEVAL = 4 };
331};
332
334{
335 id<MTLCommandBuffer> cb;
336 double lastGpuTime = 0;
337 id<MTLRenderCommandEncoder> currentRenderPassEncoder;
338 id<MTLComputeCommandEncoder> currentComputePassEncoder;
339 id<MTLComputeCommandEncoder> tessellationComputeEncoder;
340 MTLRenderPassDescriptor *currentPassRpDesc;
342 QRhiBatchedBindings<id<MTLBuffer> > currentVertexInputsBuffers;
343 QRhiBatchedBindings<NSUInteger> currentVertexInputOffsets;
344 id<MTLDepthStencilState> currentDepthStencilState;
346};
347
349{
351 float dpr = 1;
352 int sampleCount = 1;
354 int dsAttCount = 0;
355
356 struct ColorAtt {
358 id<MTLTexture> tex = nil;
359 int arrayLayer = 0;
360 int slice = 0;
361 int level = 0;
363 id<MTLTexture> resolveTex = nil;
366 };
367
368 struct {
370 id<MTLTexture> dsTex = nil;
371 id<MTLTexture> dsResolveTex = nil;
372 bool hasStencil = false;
373 bool depthNeedsStore = false;
374 bool preserveColor = false;
375 bool preserveDs = false;
376 } fb;
377
379};
380
382{
384 id<MTLRenderPipelineState> ps = nil;
385 id<MTLDepthStencilState> ds = nil;
386 MTLPrimitiveType primitiveType;
387 MTLWinding winding;
388 MTLCullMode cullMode;
389 MTLTriangleFillMode triangleFillMode;
405 bool enabled = false;
406 bool failed = false;
410 std::array<id<MTLComputePipelineState>, 3> vertexComputeState = {};
411 id<MTLComputePipelineState> tessControlComputeState = nil;
415 {
416 // max vertex output components = resourceLimit(MaxVertexOutputs) * 4 = 60
417 return vertexOrIndexCount * instanceCount * sizeof(float) * 60;
418 }
420 {
421 return outControlPointCount * patchCount * sizeof(float) * 60;
422 }
424 {
425 // assume maxTessellationControlPerPatchOutputComponents is 128
426 return patchCount * sizeof(float) * 128;
427 }
429 {
430 return ((vertexOrIndexCount + inControlPointCount - 1) / inControlPointCount) * instanceCount;
431 }
432 static int vsCompVariantToIndex(QShader::Variant vertexCompVariant);
433 id<MTLComputePipelineState> vsCompPipeline(QRhiMetal *rhiD, QShader::Variant vertexCompVariant);
434 id<MTLComputePipelineState> tescCompPipeline(QRhiMetal *rhiD);
435 id<MTLRenderPipelineState> teseFragRenderPipeline(QRhiMetal *rhiD, QMetalGraphicsPipeline *pipeline);
437 void setupVertexInputDescriptor(MTLVertexDescriptor *desc);
438 void setupStageInputDescriptor(MTLStageInputOutputDescriptor *desc);
439
440 // SPIRV-Cross buffer size buffers
442};
443
445{
446 id<MTLComputePipelineState> ps = nil;
448 MTLSize localSize;
449
450 // SPIRV-Cross buffer size buffers
452};
453
455{
456 CAMetalLayer *layer = nullptr;
457 id<CAMetalDrawable> curDrawable = nil;
458 dispatch_semaphore_t sem[QMTL_FRAMES_IN_FLIGHT];
460 MTLRenderPassDescriptor *rp = nullptr;
463 MTLPixelFormat colorFormat;
464#ifdef Q_OS_MACOS
465 bool liveResizeObserverSet = false;
466 QMacNotificationObserver liveResizeStartObserver;
467 QMacNotificationObserver liveResizeEndObserver;
468#endif
469};
470
472{
474
475 d = new QRhiMetalData(this);
476
477 importedDevice = importDevice != nullptr;
478 if (importedDevice) {
479 if (importDevice->dev) {
480 d->dev = (id<MTLDevice>) importDevice->dev;
481 importedCmdQueue = importDevice->cmdQueue != nullptr;
483 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
484 } else {
485 qWarning("No MTLDevice given, cannot import");
486 importedDevice = false;
487 }
488 }
489}
490
492{
493 delete d;
494}
495
496template <class Int>
497inline Int aligned(Int v, Int byteAlign)
498{
499 return (v + byteAlign - 1) & ~(byteAlign - 1);
500}
501
503{
505 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
506 if (dev) {
507 [dev release];
508 return true;
509 }
510 return false;
511}
512
514{
515#ifdef QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
516 // Do not let the command buffer mess with the refcount of objects. We do
517 // have a proper render loop and will manage lifetimes similarly to other
518 // backends (Vulkan).
519 return [cmdQueue commandBufferWithUnretainedReferences];
520#else
521 return [cmdQueue commandBuffer];
522#endif
523}
524
525bool QRhiMetalData::setupBinaryArchive(NSURL *sourceFileUrl)
526{
527#ifdef QRHI_METAL_DISABLE_BINARY_ARCHIVE
528 return false;
529#endif
530
531 if (@available(macOS 11.0, iOS 14.0, *)) {
532 [binArch release];
533 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor new];
534 binArchDesc.url = sourceFileUrl;
535 NSError *err = nil;
536 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
537 [binArchDesc release];
538 if (!binArch) {
539 const QString msg = QString::fromNSString(err.localizedDescription);
540 qWarning("newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
541 return false;
542 }
543 return true;
544 }
545 return false;
546}
547
548bool QRhiMetal::create(QRhi::Flags flags)
549{
550 rhiFlags = flags;
551
552 if (importedDevice)
553 [d->dev retain];
554 else
555 d->dev = MTLCreateSystemDefaultDevice();
556
557 if (!d->dev) {
558 qWarning("No MTLDevice");
559 return false;
560 }
561
562 const QString deviceName = QString::fromNSString([d->dev name]);
563 qCDebug(QRHI_LOG_INFO, "Metal device: %s", qPrintable(deviceName));
564 driverInfoStruct.deviceName = deviceName.toUtf8();
565
566 // deviceId and vendorId stay unset for now. Note that registryID is not
567 // suitable as deviceId because it does not seem stable on macOS and can
568 // apparently change when the system is rebooted.
569
570#ifdef Q_OS_MACOS
571 if (@available(macOS 10.15, *)) {
572 const MTLDeviceLocation deviceLocation = [d->dev location];
573 switch (deviceLocation) {
574 case MTLDeviceLocationBuiltIn:
576 break;
577 case MTLDeviceLocationSlot:
579 break;
580 case MTLDeviceLocationExternal:
582 break;
583 default:
584 break;
585 }
586 }
587#else
589#endif
590
592 osMajor = ver.majorVersion();
593 osMinor = ver.minorVersion();
594
596 [d->cmdQueue retain];
597 else
598 d->cmdQueue = [d->dev newCommandQueue];
599
600 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
601 // Have a custom capture scope as well which then shows up in XCode as
602 // an option when capturing, and becomes especially useful when having
603 // multiple windows with multiple QRhis.
604 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
605 const QString label = QString::asprintf("Qt capture scope for QRhi %p", this);
606 d->captureScope.label = label.toNSString();
607
608#if defined(Q_OS_MACOS)
609 caps.maxTextureSize = 16384;
610 caps.baseVertexAndInstance = true;
611 if (@available(macOS 10.15, *))
612 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
613 caps.maxThreadGroupSize = 1024;
614 caps.multiView = true;
615#elif defined(Q_OS_TVOS)
616 if ([d->dev supportsFeatureSet: MTLFeatureSet(30003)]) // MTLFeatureSet_tvOS_GPUFamily2_v1
617 caps.maxTextureSize = 16384;
618 else
619 caps.maxTextureSize = 8192;
620 caps.baseVertexAndInstance = false;
621 caps.isAppleGPU = true;
622#elif defined(Q_OS_IOS)
623 // welcome to feature set hell
624 if ([d->dev supportsFeatureSet: MTLFeatureSet(16)] // MTLFeatureSet_iOS_GPUFamily5_v1
625 || [d->dev supportsFeatureSet: MTLFeatureSet(11)] // MTLFeatureSet_iOS_GPUFamily4_v1
626 || [d->dev supportsFeatureSet: MTLFeatureSet(4)]) // MTLFeatureSet_iOS_GPUFamily3_v1
627 {
628 caps.maxTextureSize = 16384;
629 caps.baseVertexAndInstance = true;
630 } else if ([d->dev supportsFeatureSet: MTLFeatureSet(3)] // MTLFeatureSet_iOS_GPUFamily2_v2
631 || [d->dev supportsFeatureSet: MTLFeatureSet(2)]) // MTLFeatureSet_iOS_GPUFamily1_v2
632 {
633 caps.maxTextureSize = 8192;
634 caps.baseVertexAndInstance = false;
635 } else {
636 caps.maxTextureSize = 4096;
637 caps.baseVertexAndInstance = false;
638 }
639 caps.isAppleGPU = true;
640 if (@available(iOS 13, *)) {
641 if ([d->dev supportsFamily: MTLGPUFamilyApple4])
642 caps.maxThreadGroupSize = 1024;
643 if ([d->dev supportsFamily: MTLGPUFamilyApple5])
644 caps.multiView = true;
645 }
646#endif
647
648 caps.supportedSampleCounts = { 1 };
649 for (int sampleCount : { 2, 4, 8 }) {
650 if ([d->dev supportsTextureSampleCount: sampleCount])
651 caps.supportedSampleCounts.append(sampleCount);
652 }
653
656
657 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
658 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
659
660 return true;
661}
662
664{
667
668 for (QMetalShader &s : d->shaderCache)
669 s.destroy();
671
673 d->captureScope = nil;
674
675 if (@available(macOS 11.0, iOS 14.0, *)) {
676 [d->binArch release];
677 d->binArch = nil;
678 }
679
680 [d->cmdQueue release];
681 if (!importedCmdQueue)
682 d->cmdQueue = nil;
683
684 [d->dev release];
685 if (!importedDevice)
686 d->dev = nil;
687}
688
689QVector<int> QRhiMetal::supportedSampleCounts() const
690{
691 return caps.supportedSampleCounts;
692}
693
698
700{
701 return new QMetalBuffer(this, type, usage, size);
702}
703
705{
706 return 256;
707}
708
710{
711 return false;
712}
713
715{
716 return true;
717}
718
720{
721 return true;
722}
723
725{
726 // depth range 0..1
727 static QMatrix4x4 m;
728 if (m.isIdentity()) {
729 // NB the ctor takes row-major
730 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
731 0.0f, 1.0f, 0.0f, 0.0f,
732 0.0f, 0.0f, 0.5f, 0.5f,
733 0.0f, 0.0f, 0.0f, 1.0f);
734 }
735 return m;
736}
737
739{
741
742 bool supportsFamilyMac2 = false; // needed for BC* formats
743 bool supportsFamilyApple3 = false;
744
745#ifdef Q_OS_MACOS
746 supportsFamilyMac2 = true;
747 if (caps.isAppleGPU)
748 supportsFamilyApple3 = true;
749#else
750 supportsFamilyApple3 = true;
751#endif
752
753 // BC5 is not available for any Apple hardare
755 return false;
756
757 if (!supportsFamilyApple3) {
759 return false;
761 return false;
762 }
763
764 if (!supportsFamilyMac2)
766 return false;
767
768 return true;
769}
770
772{
773 switch (feature) {
775 return true;
777 return true;
779 return true;
780 case QRhi::Timestamps:
781 return true;
782 case QRhi::Instancing:
783 return true;
785 return true;
787 return true;
789 return true;
791 return false;
793 return true;
795 return true;
797 return true;
798 case QRhi::Compute:
799 return true;
800 case QRhi::WideLines:
801 return false;
803 return true;
804 case QRhi::BaseVertex:
805 return caps.baseVertexAndInstance;
807 return caps.baseVertexAndInstance;
809 return false;
811 return true;
813 return true;
814 case QRhi::TexelFetch:
815 return true;
817 return true;
819 return true;
821 return true;
823 return true;
825 {
826 if (@available(macOS 11.0, iOS 14.0, *))
827 return true;
828 else
829 return false;
830 }
832 return true;
834 return false;
836 return true;
838 return true;
840 return true;
842 return true;
844 return false;
846 return false;
848 return true;
850 return true;
852 return false;
854 return true;
856 return false;
858 return true;
859 case QRhi::MultiView:
860 return caps.multiView;
862 return false;
864 return true;
865 default:
866 Q_UNREACHABLE();
867 return false;
868 }
869}
870
872{
873 switch (limit) {
875 return 1;
877 return caps.maxTextureSize;
879 return 8;
885 return 65535;
893 return caps.maxThreadGroupSize;
895 return 2048;
897 return 65536;
899 return 31;
901 return 15; // use the minimum from MTLGPUFamily1/2/3
902 default:
903 Q_UNREACHABLE();
904 return 0;
905 }
906}
907
912
917
924
926{
927 // not applicable
928 return false;
929}
930
932{
933 for (QMetalShader &s : d->shaderCache)
934 s.destroy();
935
937}
938
940{
941 return false;
942}
943
953
955{
958 if (@available(macOS 11.0, iOS 14.0, *)) {
959 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
960 return data;
961
962 QTemporaryFile tmp;
963 if (!tmp.open()) {
964 qCDebug(QRHI_LOG_INFO, "pipelineCacheData: Failed to create temporary file for Metal");
965 return data;
966 }
967 tmp.close(); // the file exists until the tmp dtor runs
968
969 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
970 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
971 NSError *err = nil;
972 if (![d->binArch serializeToURL: url error: &err]) {
973 const QString msg = QString::fromNSString(err.localizedDescription);
974 // Some of these "errors" are not actual errors. (think of "Nothing to serialize")
975 qCDebug(QRHI_LOG_INFO, "Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
976 return data;
977 }
978
979 QFile f(fn);
980 if (!f.open(QIODevice::ReadOnly)) {
981 qCDebug(QRHI_LOG_INFO, "pipelineCacheData: Failed to reopen temporary file");
982 return data;
983 }
984 const QByteArray blob = f.readAll();
985 f.close();
986
987 const size_t headerSize = sizeof(QMetalPipelineCacheDataHeader);
988 const quint32 dataSize = quint32(blob.size());
989
990 data.resize(headerSize + dataSize);
991
993 header.rhiId = pipelineCacheRhiId();
994 header.arch = quint32(sizeof(void*));
995 header.dataSize = quint32(dataSize);
996 header.osMajor = osMajor;
997 header.osMinor = osMinor;
998 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
999 if (driverStrLen)
1000 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1001 header.driver[driverStrLen] = '\0';
1002
1003 memcpy(data.data(), &header, headerSize);
1004 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1005 }
1006 return data;
1007}
1008
1010{
1011 if (data.isEmpty())
1012 return;
1013
1014 const size_t headerSize = sizeof(QMetalPipelineCacheDataHeader);
1015 if (data.size() < qsizetype(headerSize)) {
1016 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (header incomplete)");
1017 return;
1018 }
1019
1020 const size_t dataOffset = headerSize;
1022 memcpy(&header, data.constData(), headerSize);
1023
1024 const quint32 rhiId = pipelineCacheRhiId();
1025 if (header.rhiId != rhiId) {
1026 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1027 rhiId, header.rhiId);
1028 return;
1029 }
1030
1031 const quint32 arch = quint32(sizeof(void*));
1032 if (header.arch != arch) {
1033 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Architecture does not match (%u, %u)",
1034 arch, header.arch);
1035 return;
1036 }
1037
1038 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1039 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1040 osMajor, osMinor, header.osMajor, header.osMinor);
1041 return;
1042 }
1043
1044 const size_t driverStrLen = qMin(sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1045 if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1046 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Metal device name does not match");
1047 return;
1048 }
1049
1050 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1051 qCDebug(QRHI_LOG_INFO, "setPipelineCacheData: Invalid blob size (data incomplete)");
1052 return;
1053 }
1054
1055 if (@available(macOS 11.0, iOS 14.0, *)) {
1056 const char *p = data.constData() + dataOffset;
1057
1058 QTemporaryFile tmp;
1059 if (!tmp.open()) {
1060 qCDebug(QRHI_LOG_INFO, "pipelineCacheData: Failed to create temporary file for Metal");
1061 return;
1062 }
1063 tmp.write(p, header.dataSize);
1064 tmp.close(); // the file exists until the tmp dtor runs
1065
1066 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1067 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1068 if (d->setupBinaryArchive(url))
1069 qCDebug(QRHI_LOG_INFO, "Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1070 }
1071}
1072
1074 int sampleCount, QRhiRenderBuffer::Flags flags,
1075 QRhiTexture::Format backingFormatHint)
1076{
1077 return new QMetalRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
1078}
1079
1081 const QSize &pixelSize, int depth, int arraySize,
1082 int sampleCount, QRhiTexture::Flags flags)
1083{
1084 return new QMetalTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
1085}
1086
1088 QRhiSampler::Filter mipmapMode,
1090{
1091 return new QMetalSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
1092}
1093
1095 QRhiTextureRenderTarget::Flags flags)
1096{
1097 return new QMetalTextureRenderTarget(this, desc, flags);
1098}
1099
1104
1109
1114
1115enum class BindingType {
1116 Buffer,
1117 Texture,
1118 Sampler
1119};
1120
1121static inline int mapBinding(int binding,
1122 int stageIndex,
1123 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1125{
1126 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1127 if (!map || map->isEmpty())
1128 return binding; // old QShader versions do not have this map, assume 1:1 mapping then
1129
1130 auto it = map->constFind(binding);
1131 if (it != map->cend())
1132 return type == BindingType::Sampler ? it->second : it->first; // may be -1, if the resource is inactive
1133
1134 // Hitting this path is normal too. It is not given that the resource (for
1135 // example, a uniform block) is present in the shaders for all the stages
1136 // specified by the visibility mask in the QRhiShaderResourceBinding.
1137 return -1;
1138}
1139
1141 int stage,
1142 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1143 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1144{
1145 switch (stage) {
1147 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1148 offsets: offsetBatch.resources.constData()
1149 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1150 break;
1152 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1153 offsets: offsetBatch.resources.constData()
1154 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1155 break;
1157 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1158 offsets: offsetBatch.resources.constData()
1159 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1160 break;
1163 // do nothing. These are used later for tessellation
1164 break;
1165 default:
1166 Q_UNREACHABLE();
1167 break;
1168 }
1169}
1170
1172 int stage,
1173 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1174{
1175 switch (stage) {
1177 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1178 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1179 break;
1181 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1182 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1183 break;
1185 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1186 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1187 break;
1190 // do nothing. These are used later for tessellation
1191 break;
1192 default:
1193 Q_UNREACHABLE();
1194 break;
1195 }
1196}
1197
1199 int encoderStage,
1200 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1201{
1202 switch (encoderStage) {
1204 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1205 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1206 break;
1208 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1209 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1210 break;
1212 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1213 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1214 break;
1217 // do nothing. These are used later for tessellation
1218 break;
1219 default:
1220 Q_UNREACHABLE();
1221 break;
1222 }
1223}
1224
1225// Helper that is not used during the common vertex+fragment and compute
1226// pipelines, but is necessary when tessellation is involved and so the
1227// graphics pipeline is under the hood a combination of multiple compute and
1228// render pipelines. We need to be able to set the buffers, textures, samplers
1229// when a switching between render and compute encoders.
1230static inline void rebindShaderResources(QMetalCommandBuffer *cbD, int resourceStage, int encoderStage,
1231 const QMetalShaderResourceBindingsData *customBindingState = nullptr)
1232{
1233 const QMetalShaderResourceBindingsData *bindingData = customBindingState ? customBindingState : &cbD->d->currentShaderResourceBindingState;
1234
1235 for (int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1236 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1237 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1238 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1239 }
1240
1241 for (int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1242 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1243 bindStageTextures(cbD, encoderStage, batch);
1244 }
1245
1246 for (int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1247 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1248 bindStageSamplers(cbD, encoderStage, batch);
1249 }
1250}
1251
1269
1272 int dynamicOffsetCount,
1273 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1274 bool offsetOnlyChange,
1275 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
1276{
1278
1279 for (const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1281 switch (b->type) {
1283 {
1284 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1285 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1286 quint32 offset = b->u.ubuf.offset;
1287 for (int i = 0; i < dynamicOffsetCount; ++i) {
1288 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1289 if (dynOfs.first == b->binding) {
1290 offset = dynOfs.second;
1291 break;
1292 }
1293 }
1294
1295 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1296 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1297 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1298 if (nativeBinding >= 0)
1299 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1300 }
1301 }
1302 }
1303 break;
1307 {
1309 for (int elem = 0; elem < data->count; ++elem) {
1310 QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1311 QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1312
1313 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1314 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1315 // Must handle all three cases (combined, separate, separate):
1316 // first = texture binding, second = sampler binding
1317 // first = texture binding
1318 // first = sampler binding (i.e. BindingType::Texture...)
1319 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1320 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1321 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1322 if (textureBinding >= 0 && texD)
1323 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1324 if (samplerBinding >= 0)
1325 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1326 }
1327 }
1328 }
1329 }
1330 break;
1334 {
1335 QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
1336 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1337
1338 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1339 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1340 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1341 if (nativeBinding >= 0)
1342 bindingData.res[stage].textures.append({ nativeBinding, t });
1343 }
1344 }
1345 }
1346 break;
1350 {
1351 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1352 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1353 quint32 offset = b->u.sbuf.offset;
1354 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1355 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1356 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1357 if (nativeBinding >= 0)
1358 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1359 }
1360 }
1361 }
1362 break;
1363 default:
1364 Q_UNREACHABLE();
1365 break;
1366 }
1367 }
1368
1369 for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1372 continue;
1374 continue;
1375
1376 // QRhiBatchedBindings works with the native bindings and expects
1377 // sorted input. The pre-sorted QRhiShaderResourceBinding list (based
1378 // on the QRhi (SPIR-V) binding) is not helpful in this regard, so we
1379 // have to sort here every time.
1380
1381 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](const QMetalShaderResourceBindingsData::Stage::Buffer &a, const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1382 return a.nativeBinding < b.nativeBinding;
1383 });
1384
1385 for (const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1386 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1387 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1388 }
1389
1390 bindingData.res[stage].bufferBatches.finish();
1391 bindingData.res[stage].bufferOffsetBatches.finish();
1392
1393 for (int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1394 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1395 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1396 // skip setting Buffer binding if the current state is already correct
1397 if (cbD->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1398 && cbD->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1399 && bufferBatch == cbD->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1400 && offsetBatch == cbD->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1401 {
1402 continue;
1403 }
1404 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1405 }
1406
1407 if (offsetOnlyChange)
1408 continue;
1409
1410 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](const QMetalShaderResourceBindingsData::Stage::Texture &a, const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1411 return a.nativeBinding < b.nativeBinding;
1412 });
1413
1414 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](const QMetalShaderResourceBindingsData::Stage::Sampler &a, const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1415 return a.nativeBinding < b.nativeBinding;
1416 });
1417
1418 for (const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1419 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1420
1421 for (const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1422 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1423
1424 bindingData.res[stage].textureBatches.finish();
1425 bindingData.res[stage].samplerBatches.finish();
1426
1427 for (int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1428 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1429 // skip setting Texture binding if the current state is already correct
1430 if (cbD->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1431 && batch == cbD->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1432 {
1433 continue;
1434 }
1435 bindStageTextures(cbD, stage, batch);
1436 }
1437
1438 for (int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1439 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1440 // skip setting Sampler State if the current state is already correct
1441 if (cbD->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1442 && batch == cbD->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1443 {
1444 continue;
1445 }
1446 bindStageSamplers(cbD, stage, batch);
1447 }
1448 }
1449
1450 cbD->d->currentShaderResourceBindingState = bindingData;
1451}
1452
1454{
1455 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1456
1457 if (cbD->d->currentDepthStencilState != d->ds) {
1458 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1459 cbD->d->currentDepthStencilState = d->ds;
1460 }
1461
1462 if (cbD->currentCullMode == -1 || d->cullMode != uint(cbD->currentCullMode)) {
1464 cbD->currentCullMode = int(d->cullMode);
1465 }
1467 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1469 }
1470 if (cbD->currentFrontFaceWinding == -1 || d->winding != uint(cbD->currentFrontFaceWinding)) {
1471 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1472 cbD->currentFrontFaceWinding = int(d->winding);
1473 }
1476 {
1478 slopeScale: d->slopeScaledDepthBias
1479 clamp: 0.0f];
1481 }
1482}
1483
1485{
1489
1490 if (cbD->currentGraphicsPipeline == psD && cbD->currentPipelineGeneration == psD->generation)
1491 return;
1492
1493 cbD->currentGraphicsPipeline = psD;
1494 cbD->currentComputePipeline = nullptr;
1495 cbD->currentPipelineGeneration = psD->generation;
1496
1497 if (!psD->d->tess.enabled && !psD->d->tess.failed) {
1498 psD->makeActiveForCurrentRenderPassEncoder(cbD);
1499 } else {
1500 // mark work buffers that can now be safely reused as reusable
1501 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1502 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1503 workBuf->lastActiveFrameSlot = -1;
1504 }
1505 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1506 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1507 workBuf->lastActiveFrameSlot = -1;
1508 }
1509 }
1510
1511 psD->lastActiveFrameSlot = currentFrameSlot;
1512}
1513
1515 int dynamicOffsetCount,
1516 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1517{
1522
1523 if (!srb) {
1524 if (gfxPsD)
1525 srb = gfxPsD->m_shaderResourceBindings;
1526 else
1527 srb = compPsD->m_shaderResourceBindings;
1528 }
1529
1531 bool hasSlottedResourceInSrb = false;
1532 bool hasDynamicOffsetInSrb = false;
1533 bool resNeedsRebind = false;
1534
1535 // SPIRV-Cross buffer size buffers
1536 // Need to determine storage buffer sizes here as this is the last opportunity for storage
1537 // buffer bindings (offset, size) to be specified before draw / dispatch call
1538 const bool needsBufferSizeBuffer = (compPsD && compPsD->d->bufferSizeBuffer) || (gfxPsD && gfxPsD->d->bufferSizeBuffer);
1539 QMap<QRhiShaderResourceBinding::StageFlag, QMap<int, quint32>> storageBufferSizes;
1540
1541 // do buffer writes, figure out if we need to rebind, and mark as in-use
1542 for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1543 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1544 QMetalShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
1545 switch (b->type) {
1547 {
1548 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1549 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1551 if (bufD->d->slotted)
1552 hasSlottedResourceInSrb = true;
1553 if (b->u.ubuf.hasDynamicOffset)
1554 hasDynamicOffsetInSrb = true;
1555 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1556 resNeedsRebind = true;
1557 bd.ubuf.id = bufD->m_id;
1558 bd.ubuf.generation = bufD->generation;
1559 }
1560 bufD->lastActiveFrameSlot = currentFrameSlot;
1561 }
1562 break;
1566 {
1568 if (bd.stex.count != data->count) {
1569 bd.stex.count = data->count;
1570 resNeedsRebind = true;
1571 }
1572 for (int elem = 0; elem < data->count; ++elem) {
1573 QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex);
1574 QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler);
1575 Q_ASSERT(texD || samplerD);
1576 const quint64 texId = texD ? texD->m_id : 0;
1577 const uint texGen = texD ? texD->generation : 0;
1578 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1579 const uint samplerGen = samplerD ? samplerD->generation : 0;
1580 if (texGen != bd.stex.d[elem].texGeneration
1581 || texId != bd.stex.d[elem].texId
1582 || samplerGen != bd.stex.d[elem].samplerGeneration
1583 || samplerId != bd.stex.d[elem].samplerId)
1584 {
1585 resNeedsRebind = true;
1586 bd.stex.d[elem].texId = texId;
1587 bd.stex.d[elem].texGeneration = texGen;
1588 bd.stex.d[elem].samplerId = samplerId;
1589 bd.stex.d[elem].samplerGeneration = samplerGen;
1590 }
1591 if (texD)
1592 texD->lastActiveFrameSlot = currentFrameSlot;
1593 if (samplerD)
1594 samplerD->lastActiveFrameSlot = currentFrameSlot;
1595 }
1596 }
1597 break;
1601 {
1602 QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
1603 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1604 resNeedsRebind = true;
1605 bd.simage.id = texD->m_id;
1606 bd.simage.generation = texD->generation;
1607 }
1608 texD->lastActiveFrameSlot = currentFrameSlot;
1609 }
1610 break;
1614 {
1615 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1616 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1617
1618 if (needsBufferSizeBuffer) {
1619 for (int i = 0; i < 6; ++i) {
1622 if (b->stage.testFlag(stage)) {
1623 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1624 }
1625 }
1626 }
1627
1629 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1630 resNeedsRebind = true;
1631 bd.sbuf.id = bufD->m_id;
1632 bd.sbuf.generation = bufD->generation;
1633 }
1634 bufD->lastActiveFrameSlot = currentFrameSlot;
1635 }
1636 break;
1637 default:
1638 Q_UNREACHABLE();
1639 break;
1640 }
1641 }
1642
1643 if (needsBufferSizeBuffer) {
1644 QMetalBuffer *bufD = nullptr;
1645 QVarLengthArray<QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1646
1647 if (compPsD) {
1648 bufD = compPsD->d->bufferSizeBuffer;
1649 Q_ASSERT(compPsD->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1651 } else {
1652 bufD = gfxPsD->d->bufferSizeBuffer;
1653 if (gfxPsD->d->tess.enabled) {
1654
1655 // Assumptions
1656 // * We only use one of the compute vertex shader variants in a pipeline at any one time
1657 // * The vertex shader variants all have the same storage block bindings
1658 // * The vertex shader variants all have the same native resource binding map
1659 // * The vertex shader variants all have the same MslBufferSizeBufferBinding requirement
1660 // * The vertex shader variants all have the same MslBufferSizeBufferBinding binding
1661 // => We only need to use one vertex shader variant to generate the identical shader
1662 // resource bindings
1663 Q_ASSERT(gfxPsD->d->tess.compVs[0].desc.storageBlocks() == gfxPsD->d->tess.compVs[1].desc.storageBlocks());
1664 Q_ASSERT(gfxPsD->d->tess.compVs[0].desc.storageBlocks() == gfxPsD->d->tess.compVs[2].desc.storageBlocks());
1665 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[1].nativeResourceBindingMap);
1666 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[2].nativeResourceBindingMap);
1667 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1668 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1669 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1670 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1671 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1672 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1673 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1674 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1675
1676 if (gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1677 shaders.append(qMakePair(&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage));
1678
1679 if (gfxPsD->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1681
1682 if (gfxPsD->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1684
1685 } else {
1686 if (gfxPsD->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1688 }
1689 if (gfxPsD->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1691 }
1692
1693 quint32 offset = 0;
1694 for (const QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag> &shader : shaders) {
1695
1696 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1697
1698 // if we don't have a srb entry for the buffer size buffer
1699 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1700
1701 int maxNativeBinding = 0;
1702 for (const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1703 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1704
1705 const int size = (maxNativeBinding + 1) * sizeof(int);
1706
1707 Q_ASSERT(offset + size <= bufD->size());
1708 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1709
1711 bd.sbuf.id = bufD->m_id;
1712 bd.sbuf.generation = bufD->generation;
1713 srbD->boundResourceData.append(bd);
1714 }
1715
1716 // create the buffer size buffer data
1717 QVarLengthArray<int, 8> bufferSizeBufferData;
1718 Q_ASSERT(storageBufferSizes.contains(shader.second));
1719 const QMap<int, quint32> &sizes(storageBufferSizes[shader.second]);
1720 for (const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1721 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1722
1723 // if the native binding is -1, the buffer is present but not accessed in the shader
1724 if (index < 0)
1725 continue;
1726
1727 if (bufferSizeBufferData.size() <= index)
1728 bufferSizeBufferData.resize(index + 1);
1729
1730 Q_ASSERT(sizes.contains(block.binding));
1731 bufferSizeBufferData[index] = sizes[block.binding];
1732 }
1733
1735 const quint32 size = bufferSizeBufferData.size() * sizeof(int);
1736 data.assign(reinterpret_cast<const char *>(bufferSizeBufferData.constData()), size);
1737 Q_ASSERT(offset + size <= bufD->size());
1738 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1739
1740 // buffer offsets must be 32byte aligned
1741 offset += ((size + 31) / 32) * 32;
1742 }
1743
1745 bufD->lastActiveFrameSlot = currentFrameSlot;
1746 }
1747
1748 // make sure the resources for the correct slot get bound
1749 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1750 if (hasSlottedResourceInSrb && cbD->currentResSlot != resSlot)
1751 resNeedsRebind = true;
1752
1753 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srbD) : (cbD->currentComputeSrb != srbD);
1754 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1755
1756 // dynamic uniform buffer offsets always trigger a rebind
1757 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) {
1758 const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = { nullptr, nullptr, nullptr, nullptr, nullptr };
1759 if (gfxPsD) {
1760 cbD->currentGraphicsSrb = srbD;
1761 cbD->currentComputeSrb = nullptr;
1762 if (gfxPsD->d->tess.enabled) {
1763 // If tessellating, we don't know which compVs shader to use until the draw call is
1764 // made. They should all have the same native resource binding map, so pick one.
1765 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[1].nativeResourceBindingMap);
1766 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[2].nativeResourceBindingMap);
1767 resBindMaps[QMetalShaderResourceBindingsData::VERTEX] = &gfxPsD->d->tess.compVs[0].nativeResourceBindingMap;
1768 resBindMaps[QMetalShaderResourceBindingsData::TESSCTRL] = &gfxPsD->d->tess.compTesc.nativeResourceBindingMap;
1769 resBindMaps[QMetalShaderResourceBindingsData::TESSEVAL] = &gfxPsD->d->tess.vertTese.nativeResourceBindingMap;
1770 } else {
1771 resBindMaps[QMetalShaderResourceBindingsData::VERTEX] = &gfxPsD->d->vs.nativeResourceBindingMap;
1772 }
1773 resBindMaps[QMetalShaderResourceBindingsData::FRAGMENT] = &gfxPsD->d->fs.nativeResourceBindingMap;
1774 } else {
1775 cbD->currentGraphicsSrb = nullptr;
1776 cbD->currentComputeSrb = srbD;
1777 resBindMaps[QMetalShaderResourceBindingsData::COMPUTE] = &compPsD->d->cs.nativeResourceBindingMap;
1778 }
1779 cbD->currentSrbGeneration = srbD->generation;
1780 cbD->currentResSlot = resSlot;
1781
1782 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
1783 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1784 }
1785}
1786
1788 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
1789 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1790{
1793
1794 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1795 QRhiBatchedBindings<NSUInteger> offsets;
1796 for (int i = 0; i < bindingCount; ++i) {
1797 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, bindings[i].first);
1799 bufD->lastActiveFrameSlot = currentFrameSlot;
1800 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1801 buffers.feed(startBinding + i, mtlbuf);
1802 offsets.feed(startBinding + i, bindings[i].second);
1803 }
1804 buffers.finish();
1805 offsets.finish();
1806
1807 // same binding space for vertex and constant buffers - work it around
1809 // There's nothing guaranteeing setShaderResources() was called before
1810 // setVertexInput()... but whatever srb will get bound will have to be
1811 // layout-compatible anyways so maxBinding is the same.
1812 if (!srbD)
1814 const int firstVertexBinding = srbD->maxBinding + 1;
1815
1816 if (firstVertexBinding != cbD->d->currentFirstVertexBinding
1819 {
1820 cbD->d->currentFirstVertexBinding = firstVertexBinding;
1823
1824 for (int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1825 const auto &bufferBatch(buffers.batches[i]);
1826 const auto &offsetBatch(offsets.batches[i]);
1827 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1828 bufferBatch.resources.constData()
1829 offsets: offsetBatch.resources.constData()
1830 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1831 }
1832 }
1833
1834 if (indexBuf) {
1835 QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, indexBuf);
1837 ibufD->lastActiveFrameSlot = currentFrameSlot;
1838 cbD->currentIndexBuffer = ibufD;
1839 cbD->currentIndexOffset = indexOffset;
1840 cbD->currentIndexFormat = indexFormat;
1841 } else {
1842 cbD->currentIndexBuffer = nullptr;
1843 }
1844}
1845
1847{
1850 const QSize outputSize = cbD->currentTarget->pixelSize();
1851
1852 // x,y is top-left in MTLViewportRect but bottom-left in QRhiViewport
1853 float x, y, w, h;
1854 if (!qrhi_toTopLeftRenderTargetRect<UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1855 return;
1856
1857 MTLViewport vp;
1858 vp.originX = double(x);
1859 vp.originY = double(y);
1860 vp.width = double(w);
1861 vp.height = double(h);
1862 vp.znear = double(viewport.minDepth());
1863 vp.zfar = double(viewport.maxDepth());
1864
1866
1869 MTLScissorRect s;
1870 qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
1871 s.x = NSUInteger(x);
1872 s.y = NSUInteger(y);
1873 s.width = NSUInteger(w);
1874 s.height = NSUInteger(h);
1875 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1876 }
1877}
1878
1880{
1884 const QSize outputSize = cbD->currentTarget->pixelSize();
1885
1886 // x,y is top-left in MTLScissorRect but bottom-left in QRhiScissor
1887 int x, y, w, h;
1888 if (!qrhi_toTopLeftRenderTargetRect<Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
1889 return;
1890
1891 MTLScissorRect s;
1892 s.x = NSUInteger(x);
1893 s.y = NSUInteger(y);
1894 s.width = NSUInteger(w);
1895 s.height = NSUInteger(h);
1896
1897 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1898}
1899
1901{
1904
1905 [cbD->d->currentRenderPassEncoder setBlendColorRed: float(c.redF())
1906 green: float(c.greenF()) blue: float(c.blueF()) alpha: float(c.alphaF())];
1907}
1908
1910{
1913
1914 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
1915}
1916
1917static id<MTLComputeCommandEncoder> tessellationComputeEncoder(QMetalCommandBuffer *cbD)
1918{
1919 if (cbD->d->currentRenderPassEncoder) {
1920 [cbD->d->currentRenderPassEncoder endEncoding];
1921 cbD->d->currentRenderPassEncoder = nil;
1922 }
1923
1924 if (!cbD->d->tessellationComputeEncoder)
1925 cbD->d->tessellationComputeEncoder = [cbD->d->cb computeCommandEncoder];
1926
1927 return cbD->d->tessellationComputeEncoder;
1928}
1929
1931{
1932 if (cbD->d->tessellationComputeEncoder) {
1933 [cbD->d->tessellationComputeEncoder endEncoding];
1934 cbD->d->tessellationComputeEncoder = nil;
1935 }
1936
1937 QMetalRenderTargetData * rtD = nullptr;
1938
1939 switch (cbD->currentTarget->resourceType()) {
1942 break;
1945 break;
1946 default:
1947 break;
1948 }
1949
1950 Q_ASSERT(rtD);
1951
1952 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
1953 for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
1954 oldColorLoad.append(cbD->d->currentPassRpDesc.colorAttachments[i].loadAction);
1955 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
1956 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
1957 }
1958
1959 MTLLoadAction oldDepthLoad;
1960 MTLLoadAction oldStencilLoad;
1961 if (rtD->dsAttCount) {
1962 oldDepthLoad = cbD->d->currentPassRpDesc.depthAttachment.loadAction;
1963 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
1964 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
1965
1966 oldStencilLoad = cbD->d->currentPassRpDesc.stencilAttachment.loadAction;
1967 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
1968 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
1969 }
1970
1971 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
1973
1974 for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
1975 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
1976 }
1977
1978 if (rtD->dsAttCount) {
1979 cbD->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
1980 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
1981 }
1982
1983}
1984
1986{
1987 QMetalCommandBuffer *cbD = args.cbD;
1988 QMetalGraphicsPipeline *graphicsPipeline = cbD->currentGraphicsPipeline;
1989 if (graphicsPipeline->d->tess.failed)
1990 return;
1991
1992 const bool indexed = args.type != TessDrawArgs::NonIndexed;
1993 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
1994 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
1995
1996 QMetalGraphicsPipelineData::Tessellation &tess(graphicsPipeline->d->tess);
1997 QMetalGraphicsPipelineData::ExtraBufferManager &extraBufMgr(graphicsPipeline->d->extraBufMgr);
1998 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
1999 QMetalBuffer *vertOutBuf = nullptr;
2000 QMetalBuffer *tescOutBuf = nullptr;
2001 QMetalBuffer *tescPatchOutBuf = nullptr;
2002 QMetalBuffer *tescFactorBuf = nullptr;
2003 QMetalBuffer *tescParamsBuf = nullptr;
2004 id<MTLComputeCommandEncoder> vertTescComputeEncoder = tessellationComputeEncoder(cbD);
2005
2006 // Step 1: vertex shader (as compute)
2007 {
2008 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2010 if (args.type == TessDrawArgs::U16Indexed)
2012 else if (args.type == TessDrawArgs::U32Indexed)
2014 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2015 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(this, shaderVariant);
2016 [computeEncoder setComputePipelineState: computePipelineState];
2017
2018 // Make uniform buffers, textures, and samplers (meant for the
2019 // vertex stage from the client's point of view) visible in the
2020 // "vertex as compute" shader
2021 cbD->d->currentComputePassEncoder = computeEncoder;
2023 cbD->d->currentComputePassEncoder = nil;
2024
2025 const QMap<int, int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2026 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2027 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2028
2029 if (outputBufferBinding >= 0) {
2030 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2031 vertOutBuf = extraBufMgr.acquireWorkBuffer(this, workBufSize);
2032 if (!vertOutBuf)
2033 return;
2034 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2035 }
2036
2037 if (indexBufferBinding >= 0)
2038 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2039
2040 for (int i = 0, ie = cbD->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2041 const auto &bufferBatch(cbD->d->currentVertexInputsBuffers.batches[i]);
2042 const auto &offsetBatch(cbD->d->currentVertexInputOffsets.batches[i]);
2043 [computeEncoder setBuffers: bufferBatch.resources.constData()
2044 offsets: offsetBatch.resources.constData()
2045 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2046 }
2047
2048 if (indexed) {
2049 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2050 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2051 } else {
2052 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2053 args.draw.vertexCount, args.draw.instanceCount)];
2054 }
2055
2056 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2057 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2058 }
2059
2060 // Step 2: tessellation control shader (as compute)
2061 {
2062 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2063 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(this);
2064 [computeEncoder setComputePipelineState: computePipelineState];
2065
2066 cbD->d->currentComputePassEncoder = computeEncoder;
2068 cbD->d->currentComputePassEncoder = nil;
2069
2070 const QMap<int, int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2071 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2072 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2073 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2074 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2075 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2076
2077 if (outputBufferBinding >= 0) {
2078 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2079 tescOutBuf = extraBufMgr.acquireWorkBuffer(this, workBufSize);
2080 if (!tescOutBuf)
2081 return;
2082 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2083 }
2084
2085 if (patchOutputBufferBinding >= 0) {
2086 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2087 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(this, workBufSize);
2088 if (!tescPatchOutBuf)
2089 return;
2090 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2091 }
2092
2093 if (tessFactorBufferBinding >= 0) {
2094 tescFactorBuf = extraBufMgr.acquireWorkBuffer(this, patchCount * sizeof(MTLQuadTessellationFactorsHalf));
2095 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2096 }
2097
2098 if (paramsBufferBinding >= 0) {
2099 struct {
2100 quint32 inControlPointCount;
2101 quint32 patchCount;
2102 } params;
2104 if (!tescParamsBuf)
2105 return;
2106 params.inControlPointCount = tess.inControlPointCount;
2107 params.patchCount = patchCount;
2108 id<MTLBuffer> paramsBuf = tescParamsBuf->d->buf[0];
2109 char *p = reinterpret_cast<char *>([paramsBuf contents]);
2110 memcpy(p, &params, sizeof(params));
2111 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2112 }
2113
2114 if (vertOutBuf && inputBufferBinding >= 0)
2115 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2116
2117 int sgSize = int(computePipelineState.threadExecutionWidth);
2118 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2119 while (wgSize > caps.maxThreadGroupSize) {
2120 sgSize /= 2;
2121 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2122 }
2123 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2124 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2125 }
2126
2127 // Much of the state in the QMetalCommandBuffer is going to be reset
2128 // when we get a new render encoder. Save what we need. (cheaper than
2129 // starting to walk over the srb again)
2131
2133
2134 // Step 3: tessellation evaluation (as vertex) + fragment shader
2135 {
2136 // No need to call tess.teseFragRenderPipeline because it was done
2137 // once and we know the result is stored in the standard place
2138 // (graphicsPipeline->d->ps).
2139
2140 graphicsPipeline->makeActiveForCurrentRenderPassEncoder(cbD);
2141 id<MTLRenderCommandEncoder> renderEncoder = cbD->d->currentRenderPassEncoder;
2142
2145
2146 const QMap<int, int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2147 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2148 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2149 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2150
2151 if (outputBufferBinding >= 0 && tescOutBuf)
2152 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2153
2154 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2155 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2156
2157 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2158 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2159 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2160 }
2161
2162 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2163 patchStart: 0
2164 patchCount: patchCount
2165 patchIndexBuffer: nil
2166 patchIndexBufferOffset: 0
2167 instanceCount: 1
2168 baseInstance: 0];
2169 }
2170}
2171
2173{
2175 const int multiViewCount = cbD->currentGraphicsPipeline->m_multiViewCount;
2176 if (multiViewCount <= 1)
2177 return;
2178
2179 const QMap<int, int> &ebb(cbD->currentGraphicsPipeline->d->vs.nativeShaderInfo.extraBufferBindings);
2180 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2181 if (viewMaskBufBinding == -1) {
2182 qWarning("No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2183 return;
2184 }
2185 struct {
2186 quint32 viewOffset;
2187 quint32 viewCount;
2188 } multiViewInfo;
2189 multiViewInfo.viewOffset = 0;
2190 multiViewInfo.viewCount = quint32(multiViewCount);
2191 QMetalBuffer *buf = cbD->currentGraphicsPipeline->d->extraBufMgr.acquireWorkBuffer(this, sizeof(multiViewInfo),
2193 if (buf) {
2194 id<MTLBuffer> mtlbuf = buf->d->buf[0];
2195 char *p = reinterpret_cast<char *>([mtlbuf contents]);
2196 memcpy(p, &multiViewInfo, sizeof(multiViewInfo));
2197 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2198 // The instance count is adjusted for layered rendering. The vertex shader is expected to contain something like:
2199 // uint gl_ViewIndex = spvViewMask[0] + (gl_InstanceIndex - gl_BaseInstance) % spvViewMask[1];
2200 // where spvViewMask is the buffer with multiViewInfo passed in above.
2201 *instanceCount *= multiViewCount;
2202 }
2203}
2204
2206 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2207{
2210
2211 if (cbD->currentGraphicsPipeline->d->tess.enabled) {
2213 a.cbD = cbD;
2215 a.draw.vertexCount = vertexCount;
2216 a.draw.instanceCount = instanceCount;
2217 a.draw.firstVertex = firstVertex;
2218 a.draw.firstInstance = firstInstance;
2220 return;
2221 }
2222
2224
2225 if (caps.baseVertexAndInstance) {
2227 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2228 } else {
2230 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2231 }
2232}
2233
2235 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2236{
2239
2240 if (!cbD->currentIndexBuffer)
2241 return;
2242
2243 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2244 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2245
2246 QMetalBuffer *ibufD = cbD->currentIndexBuffer;
2247 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2248
2249 if (cbD->currentGraphicsPipeline->d->tess.enabled) {
2251 a.cbD = cbD;
2253 a.drawIndexed.indexCount = indexCount;
2254 a.drawIndexed.instanceCount = instanceCount;
2255 a.drawIndexed.firstIndex = firstIndex;
2256 a.drawIndexed.vertexOffset = vertexOffset;
2257 a.drawIndexed.firstInstance = firstInstance;
2258 a.drawIndexed.indexBuffer = mtlibuf;
2260 return;
2261 }
2262
2264
2265 if (caps.baseVertexAndInstance) {
2266 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2267 indexCount: indexCount
2268 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2269 indexBuffer: mtlibuf
2270 indexBufferOffset: indexOffset
2272 baseVertex: vertexOffset
2273 baseInstance: firstInstance];
2274 } else {
2275 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2276 indexCount: indexCount
2277 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2278 indexBuffer: mtlibuf
2279 indexBufferOffset: indexOffset
2281 }
2282}
2283
2285{
2286 if (!debugMarkers)
2287 return;
2288
2289 NSString *str = [NSString stringWithUTF8String: name.constData()];
2292 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2293 else
2294 [cbD->d->cb pushDebugGroup: str];
2295}
2296
2298{
2299 if (!debugMarkers)
2300 return;
2301
2304 [cbD->d->currentRenderPassEncoder popDebugGroup];
2305 else
2306 [cbD->d->cb popDebugGroup];
2307}
2308
2310{
2311 if (!debugMarkers)
2312 return;
2313
2316 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2317}
2318
2323
2328
2334
2340
2342{
2343 Q_UNUSED(flags);
2344
2345 QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
2346 currentSwapChain = swapChainD;
2347 currentFrameSlot = swapChainD->currentFrameSlot;
2348
2349 // If we are too far ahead, block. This is also what ensures that any
2350 // resource used in the previous frame for this slot is now not in use
2351 // anymore by the GPU.
2352 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2353
2354 // Do this also for any other swapchain's commands with the same frame slot
2355 // While this reduces concurrency, it keeps resource usage safe: swapchain
2356 // A starting its frame 0, followed by swapchain B starting its own frame 0
2357 // will make B wait for A's frame 0 commands, so if a resource is written
2358 // in B's frame or when B checks for pending resource releases, that won't
2359 // mess up A's in-flight commands (as they are not in flight anymore).
2360 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2361 if (sc != swapChainD)
2362 sc->waitUntilCompleted(currentFrameSlot); // wait+signal
2363 }
2364
2365 [d->captureScope beginScope];
2366
2367 swapChainD->cbWrapper.d->cb = d->newCommandBuffer();
2368
2370 if (swapChainD->samples > 1) {
2371 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2372 colorAtt.needsDrawableForResolveTex = true;
2373 } else {
2374 colorAtt.needsDrawableForTex = true;
2375 }
2376
2377 swapChainD->rtWrapper.d->fb.colorAtt[0] = colorAtt;
2378 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2379 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
2380 swapChainD->rtWrapper.d->fb.hasStencil = swapChainD->ds ? true : false;
2381 swapChainD->rtWrapper.d->fb.depthNeedsStore = false;
2382
2383 if (swapChainD->ds)
2384 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2385
2387 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2388 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2390
2391 return QRhi::FrameOpSuccess;
2392}
2393
2395{
2396 QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
2397 Q_ASSERT(currentSwapChain == swapChainD);
2398
2399 __block int thisFrameSlot = currentFrameSlot;
2400 [swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2401 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2402 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2403 }];
2404
2405#ifdef QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
2406 // When Metal API validation diagnostics is enabled in Xcode the texture is
2407 // released before the command buffer is done with it. Manually keep it alive
2408 // to work around this.
2409 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2410 [swapChainD->cbWrapper.d->cb addCompletedHandler:^(id<MTLCommandBuffer>) {
2411 [drawableTexture release];
2412 }];
2413#endif
2414
2415 const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
2416 const bool presentsWithTransaction = swapChainD->d->layer.presentsWithTransaction;
2417 if (!presentsWithTransaction && needsPresent) {
2418 // beginFrame-endFrame without a render pass inbetween means there is no drawable.
2419 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable)
2420 [swapChainD->cbWrapper.d->cb presentDrawable: drawable];
2421 }
2422
2423 [swapChainD->cbWrapper.d->cb commit];
2424
2425 if (presentsWithTransaction && needsPresent) {
2426 // beginFrame-endFrame without a render pass inbetween means there is no drawable.
2427 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2428 // The layer has presentsWithTransaction set to true to avoid flicker on resizing,
2429 // so here it is important to follow what the Metal docs say when it comes to the
2430 // issuing the present.
2431 [swapChainD->cbWrapper.d->cb waitUntilScheduled];
2432 [drawable present];
2433 }
2434 }
2435
2436 // Must not hold on to the drawable, regardless of needsPresent
2437 [swapChainD->d->curDrawable release];
2438 swapChainD->d->curDrawable = nil;
2439
2440 [d->captureScope endScope];
2441
2442 if (needsPresent)
2443 swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2444
2445 swapChainD->frameCount += 1;
2446 currentSwapChain = nullptr;
2447 return QRhi::FrameOpSuccess;
2448}
2449
2451{
2452 Q_UNUSED(flags);
2453
2455
2456 for (QMetalSwapChain *sc : std::as_const(swapchains))
2457 sc->waitUntilCompleted(currentFrameSlot);
2458
2459 d->ofr.active = true;
2460 *cb = &d->ofr.cbWrapper;
2461 d->ofr.cbWrapper.d->cb = d->newCommandBuffer();
2462
2464 d->ofr.cbWrapper.resetState(d->ofr.lastGpuTime);
2465 d->ofr.lastGpuTime = 0;
2467
2468 return QRhi::FrameOpSuccess;
2469}
2470
2472{
2473 Q_UNUSED(flags);
2474 Q_ASSERT(d->ofr.active);
2475 d->ofr.active = false;
2476
2477 id<MTLCommandBuffer> cb = d->ofr.cbWrapper.d->cb;
2478 [cb commit];
2479
2480 // offscreen frames wait for completion, unlike swapchain ones
2481 [cb waitUntilCompleted];
2482
2483 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2484
2486
2487 return QRhi::FrameOpSuccess;
2488}
2489
2491{
2492 id<MTLCommandBuffer> cb = nil;
2493 QMetalSwapChain *swapChainD = nullptr;
2494 if (inFrame) {
2495 if (d->ofr.active) {
2497 Q_ASSERT(d->ofr.cbWrapper.recordingPass == QMetalCommandBuffer::NoPass);
2498 cb = d->ofr.cbWrapper.d->cb;
2499 } else {
2501 swapChainD = currentSwapChain;
2502 Q_ASSERT(swapChainD->cbWrapper.recordingPass == QMetalCommandBuffer::NoPass);
2503 cb = swapChainD->cbWrapper.d->cb;
2504 }
2505 }
2506
2507 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2508 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2510 // no wait as this is the thing we're going to be commit below and
2511 // beginFrame decremented sem already and going to be signaled by endFrame
2512 continue;
2513 }
2514 sc->waitUntilCompleted(i);
2515 }
2516 }
2517
2518 if (cb) {
2519 [cb commit];
2520 [cb waitUntilCompleted];
2521 }
2522
2523 if (inFrame) {
2524 if (d->ofr.active) {
2525 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2526 d->ofr.cbWrapper.d->cb = d->newCommandBuffer();
2527 } else {
2528 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2529 swapChainD->cbWrapper.d->cb = d->newCommandBuffer();
2530 }
2531 }
2532
2534
2536
2537 return QRhi::FrameOpSuccess;
2538}
2539
2540MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthStencil,
2541 const QColor &colorClearValue,
2542 const QRhiDepthStencilClearValue &depthStencilClearValue,
2543 int colorAttCount)
2544{
2545 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2546 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2547 colorClearValue.alphaF());
2548
2549 for (uint i = 0; i < uint(colorAttCount); ++i) {
2550 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2551 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2552 rp.colorAttachments[i].clearColor = c;
2553 }
2554
2555 if (hasDepthStencil) {
2556 rp.depthAttachment.loadAction = MTLLoadActionClear;
2557 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2558 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2559 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2560 rp.depthAttachment.clearDepth = double(depthStencilClearValue.depthClearValue());
2561 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2562 }
2563
2564 return rp;
2565}
2566
2568{
2569 qsizetype size = 0;
2570 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
2571 subresDesc.data().size() : subresDesc.image().sizeInBytes();
2572 if (imageSizeBytes > 0)
2573 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2574 return size;
2575}
2576
2577void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr,
2578 int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc,
2579 qsizetype *curOfs)
2580{
2581 const QPoint dp = subresDesc.destinationTopLeft();
2582 const QByteArray rawData = subresDesc.data();
2583 QImage img = subresDesc.image();
2584 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2585 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
2586
2587 if (!img.isNull()) {
2588 const qsizetype fullImageSizeBytes = img.sizeInBytes();
2589 int w = img.width();
2590 int h = img.height();
2591 int bpl = img.bytesPerLine();
2592
2593 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2594 const int sx = subresDesc.sourceTopLeft().x();
2595 const int sy = subresDesc.sourceTopLeft().y();
2596 if (!subresDesc.sourceSize().isEmpty()) {
2597 w = subresDesc.sourceSize().width();
2598 h = subresDesc.sourceSize().height();
2599 }
2600 if (w == img.width()) {
2601 const int bpc = qMax(1, img.depth() / 8);
2602 Q_ASSERT(h * img.bytesPerLine() <= fullImageSizeBytes);
2603 memcpy(reinterpret_cast<char *>(mp) + *curOfs,
2604 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
2605 h * img.bytesPerLine());
2606 } else {
2607 img = img.copy(sx, sy, w, h);
2608 bpl = img.bytesPerLine();
2609 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
2610 memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
2611 }
2612 } else {
2613 memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
2614 }
2615
2616 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2617 sourceOffset: NSUInteger(*curOfs)
2618 sourceBytesPerRow: NSUInteger(bpl)
2619 sourceBytesPerImage: 0
2620 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2621 toTexture: texD->d->tex
2622 destinationSlice: NSUInteger(is3D ? 0 : layer)
2623 destinationLevel: NSUInteger(level)
2624 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2625 options: MTLBlitOptionNone];
2626
2627 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2628 } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
2629 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2630 const int subresw = subresSize.width();
2631 const int subresh = subresSize.height();
2632 int w, h;
2633 if (subresDesc.sourceSize().isEmpty()) {
2634 w = subresw;
2635 h = subresh;
2636 } else {
2637 w = subresDesc.sourceSize().width();
2638 h = subresDesc.sourceSize().height();
2639 }
2640
2641 quint32 bpl = 0;
2642 QSize blockDim;
2643 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr, &blockDim);
2644
2645 const int dx = aligned(dp.x(), blockDim.width());
2646 const int dy = aligned(dp.y(), blockDim.height());
2647 if (dx + w != subresw)
2648 w = aligned(w, blockDim.width());
2649 if (dy + h != subresh)
2650 h = aligned(h, blockDim.height());
2651
2652 memcpy(reinterpret_cast<char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2653
2654 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2655 sourceOffset: NSUInteger(*curOfs)
2656 sourceBytesPerRow: bpl
2657 sourceBytesPerImage: 0
2658 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2659 toTexture: texD->d->tex
2660 destinationSlice: NSUInteger(is3D ? 0 : layer)
2661 destinationLevel: NSUInteger(level)
2662 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
2663 options: MTLBlitOptionNone];
2664
2665 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2666 } else if (!rawData.isEmpty()) {
2667 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2668 const int subresw = subresSize.width();
2669 const int subresh = subresSize.height();
2670 int w, h;
2671 if (subresDesc.sourceSize().isEmpty()) {
2672 w = subresw;
2673 h = subresh;
2674 } else {
2675 w = subresDesc.sourceSize().width();
2676 h = subresDesc.sourceSize().height();
2677 }
2678
2679 quint32 bpl = 0;
2680 if (subresDesc.dataStride())
2681 bpl = subresDesc.dataStride();
2682 else
2683 textureFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr, nullptr);
2684
2685 memcpy(reinterpret_cast<char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2686
2687 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2688 sourceOffset: NSUInteger(*curOfs)
2689 sourceBytesPerRow: bpl
2690 sourceBytesPerImage: 0
2691 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2692 toTexture: texD->d->tex
2693 destinationSlice: NSUInteger(is3D ? 0 : layer)
2694 destinationLevel: NSUInteger(level)
2695 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2696 options: MTLBlitOptionNone];
2697
2698 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2699 } else {
2700 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2701 }
2702}
2703
2705{
2708
2709 id<MTLBlitCommandEncoder> blitEnc = nil;
2710 auto ensureBlit = [&blitEnc, cbD, this]() {
2711 if (!blitEnc) {
2712 blitEnc = [cbD->d->cb blitCommandEncoder];
2713 if (debugMarkers)
2714 [blitEnc pushDebugGroup: @"Texture upload/copy"];
2715 }
2716 };
2717
2718 for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
2719 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
2722 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2723 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2724 if (u.offset == 0 && u.data.size() == bufD->m_size)
2725 bufD->d->pendingUpdates[i].clear();
2726 bufD->d->pendingUpdates[i].append({ u.offset, u.data });
2727 }
2729 // Due to the Metal API the handling of static and dynamic buffers is
2730 // basically the same. So go through the same pendingUpdates machinery.
2732 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
2733 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
2734 for (int i = 0, ie = bufD->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i)
2735 bufD->d->pendingUpdates[i].append({ u.offset, u.data });
2737 QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
2739 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
2740 if (bufD->m_type == QRhiBuffer::Dynamic) {
2741 char *p = reinterpret_cast<char *>([bufD->d->buf[idx] contents]);
2742 if (p) {
2743 u.result->data.resize(u.readSize);
2744 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
2745 }
2746 if (u.result->completed)
2747 u.result->completed();
2748 } else {
2750 readback.activeFrameSlot = idx;
2751 readback.buf = bufD->d->buf[idx];
2752 readback.offset = u.offset;
2753 readback.readSize = u.readSize;
2754 readback.result = u.result;
2755 d->activeBufferReadbacks.append(readback);
2756#ifdef Q_OS_MACOS
2757 if (bufD->d->managed) {
2758 // On non-Apple Silicon, manually synchronize memory from GPU to CPU
2759 ensureBlit();
2760 [blitEnc synchronizeResource:readback.buf];
2761 }
2762#endif
2763 }
2764 }
2765 }
2766
2767 for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
2768 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
2770 QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst);
2771 qsizetype stagingSize = 0;
2772 for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2773 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2774 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2775 stagingSize += subresUploadByteSize(subresDesc);
2776 }
2777 }
2778
2779 ensureBlit();
2780 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
2781 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
2782 options: MTLResourceStorageModeShared];
2783
2784 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
2785 qsizetype curOfs = 0;
2786 for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2787 for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2788 for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2789 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
2790 }
2791 }
2792
2793 utexD->lastActiveFrameSlot = currentFrameSlot;
2794
2797 e.lastActiveFrameSlot = currentFrameSlot;
2798 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
2799 utexD->d->stagingBuf[currentFrameSlot] = nil;
2800 d->releaseQueue.append(e);
2802 Q_ASSERT(u.src && u.dst);
2803 QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.src);
2804 QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.dst);
2805 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2806 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2807 const QPoint dp = u.desc.destinationTopLeft();
2808 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2809 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2810 const QPoint sp = u.desc.sourceTopLeft();
2811
2812 ensureBlit();
2813 [blitEnc copyFromTexture: srcD->d->tex
2814 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
2815 sourceLevel: NSUInteger(u.desc.sourceLevel())
2816 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
2817 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
2818 toTexture: dstD->d->tex
2819 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
2820 destinationLevel: NSUInteger(u.desc.destinationLevel())
2821 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
2822
2823 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
2826 readback.activeFrameSlot = currentFrameSlot;
2827 readback.desc = u.rb;
2828 readback.result = u.result;
2829
2830 QMetalTexture *texD = QRHI_RES(QMetalTexture, u.rb.texture());
2831 QMetalSwapChain *swapChainD = nullptr;
2832 id<MTLTexture> src;
2833 QSize srcSize;
2834 bool is3D = false;
2835 if (texD) {
2836 if (texD->samples > 1) {
2837 qWarning("Multisample texture cannot be read back");
2838 continue;
2839 }
2840 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2841 readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
2842 readback.format = texD->m_format;
2843 src = texD->d->tex;
2844 srcSize = readback.pixelSize;
2845 texD->lastActiveFrameSlot = currentFrameSlot;
2846 } else {
2847 Q_ASSERT(currentSwapChain);
2848 swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain);
2849 readback.pixelSize = swapChainD->pixelSize;
2850 readback.format = swapChainD->d->rhiColorFormat;
2851 // Multisample swapchains need nothing special since resolving
2852 // happens when ending a renderpass.
2853 const QMetalRenderTargetData::ColorAtt &colorAtt(swapChainD->rtWrapper.d->fb.colorAtt[0]);
2854 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
2855 srcSize = swapChainD->rtWrapper.d->pixelSize;
2856 }
2857
2858 quint32 bpl = 0;
2859 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize, nullptr);
2860 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
2861
2862 ensureBlit();
2863 [blitEnc copyFromTexture: src
2864 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
2865 sourceLevel: NSUInteger(u.rb.level())
2866 sourceOrigin: MTLOriginMake(0, 0, is3D ? u.rb.layer() : 0)
2867 sourceSize: MTLSizeMake(NSUInteger(srcSize.width()), NSUInteger(srcSize.height()), 1)
2868 toBuffer: readback.buf
2869 destinationOffset: 0
2870 destinationBytesPerRow: bpl
2871 destinationBytesPerImage: 0
2872 options: MTLBlitOptionNone];
2873
2874 d->activeTextureReadbacks.append(readback);
2876 QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst);
2877 ensureBlit();
2878 [blitEnc generateMipmapsForTexture: utexD->d->tex];
2879 utexD->lastActiveFrameSlot = currentFrameSlot;
2880 }
2881 }
2882
2883 if (blitEnc) {
2884 if (debugMarkers)
2885 [blitEnc popDebugGroup];
2886 [blitEnc endEncoding];
2887 }
2888
2889 ud->free();
2890}
2891
2892// this handles all types of buffers, not just Dynamic
2894{
2895 if (bufD->d->pendingUpdates[slot].isEmpty())
2896 return;
2897
2898 void *p = [bufD->d->buf[slot] contents];
2899 quint32 changeBegin = UINT32_MAX;
2900 quint32 changeEnd = 0;
2901 for (const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
2902 memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
2903 if (u.offset < changeBegin)
2904 changeBegin = u.offset;
2905 if (u.offset + u.data.size() > changeEnd)
2906 changeEnd = u.offset + u.data.size();
2907 }
2908#ifdef Q_OS_MACOS
2909 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
2910 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
2911#endif
2912
2913 bufD->d->pendingUpdates[slot].clear();
2914}
2915
2920
2927
2929 QRhiRenderTarget *rt,
2930 const QColor &colorClearValue,
2931 const QRhiDepthStencilClearValue &depthStencilClearValue,
2932 QRhiResourceUpdateBatch *resourceUpdates,
2933 QRhiCommandBuffer::BeginPassFlags)
2934{
2937
2938 if (resourceUpdates)
2939 enqueueResourceUpdates(cb, resourceUpdates);
2940
2941 QMetalRenderTargetData *rtD = nullptr;
2942 switch (rt->resourceType()) {
2945 cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
2946 if (rtD->colorAttCount) {
2947 QMetalRenderTargetData::ColorAtt &color0(rtD->fb.colorAtt[0]);
2948 if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
2951 if (!swapChainD->d->curDrawable) {
2953 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
2954 }
2955 if (!swapChainD->d->curDrawable) {
2956 qWarning("No drawable");
2957 return;
2958 }
2959 id<MTLTexture> scTex = swapChainD->d->curDrawable.texture;
2960 if (color0.needsDrawableForTex) {
2961 color0.tex = scTex;
2962 color0.needsDrawableForTex = false;
2963 } else {
2964 color0.resolveTex = scTex;
2965 color0.needsDrawableForResolveTex = false;
2966 }
2967 }
2968 }
2969 break;
2971 {
2973 rtD = rtTex->d;
2974 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
2975 rtTex->create();
2976 cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
2977 if (rtD->fb.preserveColor) {
2978 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
2979 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2980 }
2981 if (rtD->dsAttCount && rtD->fb.preserveDs) {
2982 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2983 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2984 }
2985 int colorAttCount = 0;
2986 for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
2987 it != itEnd; ++it)
2988 {
2989 colorAttCount += 1;
2990 if (it->texture()) {
2991 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
2992 if (it->multiViewCount() >= 2)
2993 cbD->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
2994 } else if (it->renderBuffer()) {
2995 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
2996 }
2997 if (it->resolveTexture())
2998 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
2999 }
3000 if (rtTex->m_desc.depthStencilBuffer())
3001 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3002 if (rtTex->m_desc.depthTexture()) {
3003 QMetalTexture *depthTexture = QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture());
3004 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3005 if (colorAttCount == 0 && depthTexture->arraySize() >= 2)
3006 cbD->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3007 }
3008 if (rtTex->m_desc.depthResolveTexture())
3009 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3010 }
3011 break;
3012 default:
3013 Q_UNREACHABLE();
3014 break;
3015 }
3016
3017 for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
3018 cbD->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3019 cbD->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3020 cbD->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3021 cbD->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3022 if (rtD->fb.colorAtt[i].resolveTex) {
3023 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3024 : MTLStoreActionMultisampleResolve;
3025 cbD->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3026 cbD->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3027 cbD->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3028 }
3029 }
3030
3031 if (rtD->dsAttCount) {
3032 Q_ASSERT(rtD->fb.dsTex);
3033 cbD->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3034 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3035 if (rtD->fb.depthNeedsStore) // Depth/Stencil is set to DontCare by default, override if needed
3036 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3037 if (rtD->fb.dsResolveTex) {
3038 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3039 : MTLStoreActionMultisampleResolve;
3040 cbD->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3041 if (rtD->fb.hasStencil) {
3042 cbD->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3043 cbD->d->currentPassRpDesc.stencilAttachment.storeAction = cbD->d->currentPassRpDesc.depthAttachment.storeAction;
3044 }
3045 }
3046 }
3047
3048 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3049
3050 cbD->resetPerPassState();
3051
3053 cbD->currentTarget = rt;
3054}
3055
3057{
3060
3061 [cbD->d->currentRenderPassEncoder endEncoding];
3062
3064 cbD->currentTarget = nullptr;
3065
3066 if (resourceUpdates)
3067 enqueueResourceUpdates(cb, resourceUpdates);
3068}
3069
3071 QRhiResourceUpdateBatch *resourceUpdates,
3072 QRhiCommandBuffer::BeginPassFlags)
3073{
3076
3077 if (resourceUpdates)
3078 enqueueResourceUpdates(cb, resourceUpdates);
3079
3080 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3081 cbD->resetPerPassState();
3083}
3084
3086{
3089
3090 [cbD->d->currentComputePassEncoder endEncoding];
3092
3093 if (resourceUpdates)
3094 enqueueResourceUpdates(cb, resourceUpdates);
3095}
3096
3098{
3102
3103 if (cbD->currentComputePipeline != psD || cbD->currentPipelineGeneration != psD->generation) {
3104 cbD->currentGraphicsPipeline = nullptr;
3105 cbD->currentComputePipeline = psD;
3106 cbD->currentPipelineGeneration = psD->generation;
3107
3108 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3109 }
3110
3111 psD->lastActiveFrameSlot = currentFrameSlot;
3112}
3113
3115{
3119
3120 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3121 threadsPerThreadgroup: psD->d->localSize];
3122}
3123
3125{
3126 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3127 [e.buffer.buffers[i] release];
3128}
3129
3134
3136{
3137 [e.texture.texture release];
3138 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3139 [e.texture.stagingBuffers[i] release];
3140 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3141 [e.texture.views[i] release];
3142}
3143
3145{
3146 [e.sampler.samplerState release];
3147}
3148
3150{
3151 for (int i = d->releaseQueue.count() - 1; i >= 0; --i) {
3153 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3154 switch (e.type) {
3157 break;
3160 break;
3163 break;
3166 break;
3168 [e.stagingBuffer.buffer release];
3169 break;
3171 [e.graphicsPipeline.pipelineState release];
3172 [e.graphicsPipeline.depthStencilState release];
3173 [e.graphicsPipeline.tessVertexComputeState[0] release];
3174 [e.graphicsPipeline.tessVertexComputeState[1] release];
3175 [e.graphicsPipeline.tessVertexComputeState[2] release];
3176 [e.graphicsPipeline.tessTessControlComputeState release];
3177 break;
3179 [e.computePipeline.pipelineState release];
3180 break;
3181 default:
3182 break;
3183 }
3184 d->releaseQueue.removeAt(i);
3185 }
3186 }
3187}
3188
3190{
3191 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
3192
3193 for (int i = d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3195 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3196 readback.result->format = readback.format;
3197 readback.result->pixelSize = readback.pixelSize;
3198 readback.result->data.resize(int(readback.bufSize));
3199 void *p = [readback.buf contents];
3200 memcpy(readback.result->data.data(), p, readback.bufSize);
3201 [readback.buf release];
3202
3203 if (readback.result->completed)
3204 completedCallbacks.append(readback.result->completed);
3205
3206 d->activeTextureReadbacks.remove(i);
3207 }
3208 }
3209
3210 for (int i = d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3212 if (forced || currentFrameSlot == readback.activeFrameSlot
3213 || readback.activeFrameSlot < 0) {
3214 readback.result->data.resize(readback.readSize);
3215 char *p = reinterpret_cast<char *>([readback.buf contents]);
3216 Q_ASSERT(p);
3217 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3218
3219 if (readback.result->completed)
3220 completedCallbacks.append(readback.result->completed);
3221
3222 d->activeBufferReadbacks.remove(i);
3223 }
3224 }
3225
3226 for (auto f : completedCallbacks)
3227 f();
3228}
3229
3231 : QRhiBuffer(rhi, type, usage, size),
3232 d(new QMetalBufferData)
3233{
3234 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3235 d->buf[i] = nil;
3236}
3237
3239{
3240 destroy();
3241 delete d;
3242}
3243
3245{
3246 if (!d->buf[0])
3247 return;
3248
3252
3253 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3254 e.buffer.buffers[i] = d->buf[i];
3255 d->buf[i] = nil;
3256 d->pendingUpdates[i].clear();
3257 }
3258
3260 if (rhiD) {
3261 rhiD->d->releaseQueue.append(e);
3262 rhiD->unregisterResource(this);
3263 }
3264}
3265
3267{
3268 if (d->buf[0])
3269 destroy();
3270
3271 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3272 qWarning("StorageBuffer cannot be combined with Dynamic");
3273 return false;
3274 }
3275
3276 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3277 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3278
3279 d->managed = false;
3280 MTLResourceOptions opts = MTLResourceStorageModeShared;
3281
3283#ifdef Q_OS_MACOS
3284 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3285 opts = MTLResourceStorageModeManaged;
3286 d->managed = true;
3287 }
3288#endif
3289
3290 // Have QMTL_FRAMES_IN_FLIGHT versions regardless of the type, for now.
3291 // This is because writing to a Managed buffer (which is what Immutable and
3292 // Static maps to on macOS) is not safe when another frame reading from the
3293 // same buffer is still in flight.
3294 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer); // except for SSBOs written in the shader
3295 // and a special case for internal work buffers
3296 if (int(m_usage) == WorkBufPoolUsage)
3297 d->slotted = false;
3298
3299 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3300 if (i == 0 || d->slotted) {
3301 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3302 if (!m_objectName.isEmpty()) {
3303 if (!d->slotted) {
3304 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3305 } else {
3307 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3308 }
3309 }
3310 }
3311 }
3312
3314 generation += 1;
3315 rhiD->registerResource(this);
3316 return true;
3317}
3318
3320{
3321 if (d->slotted) {
3323 Q_ASSERT(sizeof(b.objects) / sizeof(b.objects[0]) >= size_t(QMTL_FRAMES_IN_FLIGHT));
3324 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3326 rhiD->executeBufferHostWritesForSlot(this, i);
3327 b.objects[i] = &d->buf[i];
3328 }
3329 b.slotCount = QMTL_FRAMES_IN_FLIGHT;
3330 return b;
3331 }
3332 return { { &d->buf[0] }, 1 };
3333}
3334
3336{
3337 // Shortcut the entire buffer update mechanism and allow the client to do
3338 // the host writes directly to the buffer. This will lead to unexpected
3339 // results when combined with QRhiResourceUpdateBatch-based updates for the
3340 // buffer, but provides a fast path for dynamic buffers that have all their
3341 // content changed in every frame.
3344 Q_ASSERT(rhiD->inFrame);
3345 const int slot = rhiD->currentFrameSlot;
3346 void *p = [d->buf[slot] contents];
3347 return static_cast<char *>(p);
3348}
3349
3351{
3352#ifdef Q_OS_MACOS
3353 if (d->managed) {
3355 const int slot = rhiD->currentFrameSlot;
3356 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3357 }
3358#endif
3359}
3360
3361static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags, const QRhiMetal *d)
3362{
3363#ifndef Q_OS_MACOS
3364 Q_UNUSED(d);
3365#endif
3366
3367 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3368 switch (format) {
3369 case QRhiTexture::RGBA8:
3370 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3371 case QRhiTexture::BGRA8:
3372 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3373 case QRhiTexture::R8:
3374#ifdef Q_OS_MACOS
3375 return MTLPixelFormatR8Unorm;
3376#else
3377 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3378#endif
3379 case QRhiTexture::RG8:
3380#ifdef Q_OS_MACOS
3381 return MTLPixelFormatRG8Unorm;
3382#else
3383 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3384#endif
3385 case QRhiTexture::R16:
3386 return MTLPixelFormatR16Unorm;
3387 case QRhiTexture::RG16:
3388 return MTLPixelFormatRG16Unorm;
3390 return MTLPixelFormatR8Unorm;
3391
3393 return MTLPixelFormatRGBA16Float;
3395 return MTLPixelFormatRGBA32Float;
3396 case QRhiTexture::R16F:
3397 return MTLPixelFormatR16Float;
3398 case QRhiTexture::R32F:
3399 return MTLPixelFormatR32Float;
3400
3402 return MTLPixelFormatRGB10A2Unorm;
3403
3404#ifdef Q_OS_MACOS
3405 case QRhiTexture::D16:
3406 return MTLPixelFormatDepth16Unorm;
3407 case QRhiTexture::D24:
3408 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3409 case QRhiTexture::D24S8:
3410 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3411#else
3412 case QRhiTexture::D16:
3413 return MTLPixelFormatDepth32Float;
3414 case QRhiTexture::D24:
3415 return MTLPixelFormatDepth32Float;
3416 case QRhiTexture::D24S8:
3417 return MTLPixelFormatDepth32Float_Stencil8;
3418#endif
3419 case QRhiTexture::D32F:
3420 return MTLPixelFormatDepth32Float;
3421
3422#ifdef Q_OS_MACOS
3423 case QRhiTexture::BC1:
3424 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3425 case QRhiTexture::BC2:
3426 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3427 case QRhiTexture::BC3:
3428 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3429 case QRhiTexture::BC4:
3430 return MTLPixelFormatBC4_RUnorm;
3431 case QRhiTexture::BC5:
3432 qWarning("QRhiMetal does not support BC5");
3433 return MTLPixelFormatInvalid;
3434 case QRhiTexture::BC6H:
3435 return MTLPixelFormatBC6H_RGBUfloat;
3436 case QRhiTexture::BC7:
3437 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3438#else
3439 case QRhiTexture::BC1:
3440 case QRhiTexture::BC2:
3441 case QRhiTexture::BC3:
3442 case QRhiTexture::BC4:
3443 case QRhiTexture::BC5:
3444 case QRhiTexture::BC6H:
3445 case QRhiTexture::BC7:
3446 qWarning("QRhiMetal: BCx compression not supported on this platform");
3447 return MTLPixelFormatInvalid;
3448#endif
3449
3450#ifndef Q_OS_MACOS
3452 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3454 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3456 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3457
3459 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3461 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3463 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3465 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3467 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3469 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3471 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3473 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3475 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3477 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3479 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3481 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3483 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3485 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3486#else
3488 if (d->caps.isAppleGPU) {
3489 if (@available(macOS 11.0, *))
3490 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3491 }
3492 qWarning("QRhiMetal: ETC2 compression not supported on this platform");
3493 return MTLPixelFormatInvalid;
3495 if (d->caps.isAppleGPU) {
3496 if (@available(macOS 11.0, *))
3497 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3498 }
3499 qWarning("QRhiMetal: ETC2 compression not supported on this platform");
3500 return MTLPixelFormatInvalid;
3502 if (d->caps.isAppleGPU) {
3503 if (@available(macOS 11.0, *))
3504 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3505 }
3506 qWarning("QRhiMetal: ETC2 compression not supported on this platform");
3507 return MTLPixelFormatInvalid;
3509 if (d->caps.isAppleGPU) {
3510 if (@available(macOS 11.0, *))
3511 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_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_5x4_sRGB : MTLPixelFormatASTC_5x4_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_5x5_sRGB : MTLPixelFormatASTC_5x5_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_6x5_sRGB : MTLPixelFormatASTC_6x5_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_6x6_sRGB : MTLPixelFormatASTC_6x6_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_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3547 }
3548 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3549 return MTLPixelFormatInvalid;
3551 if (d->caps.isAppleGPU) {
3552 if (@available(macOS 11.0, *))
3553 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3554 }
3555 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3556 return MTLPixelFormatInvalid;
3558 if (d->caps.isAppleGPU) {
3559 if (@available(macOS 11.0, *))
3560 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3561 }
3562 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3563 return MTLPixelFormatInvalid;
3565 if (d->caps.isAppleGPU) {
3566 if (@available(macOS 11.0, *))
3567 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3568 }
3569 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3570 return MTLPixelFormatInvalid;
3572 if (d->caps.isAppleGPU) {
3573 if (@available(macOS 11.0, *))
3574 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3575 }
3576 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3577 return MTLPixelFormatInvalid;
3579 if (d->caps.isAppleGPU) {
3580 if (@available(macOS 11.0, *))
3581 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3582 }
3583 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3584 return MTLPixelFormatInvalid;
3586 if (d->caps.isAppleGPU) {
3587 if (@available(macOS 11.0, *))
3588 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3589 }
3590 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3591 return MTLPixelFormatInvalid;
3593 if (d->caps.isAppleGPU) {
3594 if (@available(macOS 11.0, *))
3595 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3596 }
3597 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3598 return MTLPixelFormatInvalid;
3600 if (d->caps.isAppleGPU) {
3601 if (@available(macOS 11.0, *))
3602 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3603 }
3604 qWarning("QRhiMetal: ASTC compression not supported on this platform");
3605 return MTLPixelFormatInvalid;
3606#endif
3607
3608 default:
3609 Q_UNREACHABLE();
3610 return MTLPixelFormatInvalid;
3611 }
3612}
3613
3615 int sampleCount, QRhiRenderBuffer::Flags flags,
3616 QRhiTexture::Format backingFormatHint)
3617 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint),
3619{
3620}
3621
3623{
3624 destroy();
3625 delete d;
3626}
3627
3629{
3630 if (!d->tex)
3631 return;
3632
3636
3637 e.renderbuffer.texture = d->tex;
3638 d->tex = nil;
3639
3641 if (rhiD) {
3642 rhiD->d->releaseQueue.append(e);
3643 rhiD->unregisterResource(this);
3644 }
3645}
3646
3648{
3649 if (d->tex)
3650 destroy();
3651
3652 if (m_pixelSize.isEmpty())
3653 return false;
3654
3656 samples = rhiD->effectiveSampleCount(m_sampleCount);
3657
3658 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3659 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3660 desc.width = NSUInteger(m_pixelSize.width());
3661 desc.height = NSUInteger(m_pixelSize.height());
3662 if (samples > 1)
3663 desc.sampleCount = NSUInteger(samples);
3664 desc.resourceOptions = MTLResourceStorageModePrivate;
3665 desc.usage = MTLTextureUsageRenderTarget;
3666
3667 switch (m_type) {
3668 case DepthStencil:
3669#ifdef Q_OS_MACOS
3670 if (rhiD->caps.isAppleGPU) {
3671 if (@available(macOS 11.0, *)) {
3672 desc.storageMode = MTLStorageModeMemoryless;
3673 d->format = MTLPixelFormatDepth32Float_Stencil8;
3674 } else {
3675 Q_UNREACHABLE();
3676 }
3677 } else {
3678 desc.storageMode = MTLStorageModePrivate;
3679 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
3680 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3681 }
3682#else
3683 desc.storageMode = MTLStorageModeMemoryless;
3684 d->format = MTLPixelFormatDepth32Float_Stencil8;
3685#endif
3686 desc.pixelFormat = d->format;
3687 break;
3688 case Color:
3689 desc.storageMode = MTLStorageModePrivate;
3692 else
3693 d->format = MTLPixelFormatRGBA8Unorm;
3694 desc.pixelFormat = d->format;
3695 break;
3696 default:
3697 Q_UNREACHABLE();
3698 break;
3699 }
3700
3701 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3702 [desc release];
3703
3704 if (!m_objectName.isEmpty())
3705 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3706
3708 generation += 1;
3709 rhiD->registerResource(this);
3710 return true;
3711}
3712
3720
3722 int arraySize, int sampleCount, Flags flags)
3723 : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags),
3725{
3726 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3727 d->stagingBuf[i] = nil;
3728
3729 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3730 d->perLevelViews[i] = nil;
3731}
3732
3734{
3735 destroy();
3736 delete d;
3737}
3738
3740{
3741 if (!d->tex)
3742 return;
3743
3747
3748 e.texture.texture = d->owns ? d->tex : nil;
3749 d->tex = nil;
3750
3751 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3752 e.texture.stagingBuffers[i] = d->stagingBuf[i];
3753 d->stagingBuf[i] = nil;
3754 }
3755
3756 for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
3757 e.texture.views[i] = d->perLevelViews[i];
3758 d->perLevelViews[i] = nil;
3759 }
3760
3762 if (rhiD) {
3763 rhiD->d->releaseQueue.append(e);
3764 rhiD->unregisterResource(this);
3765 }
3766}
3767
3769{
3770 if (d->tex)
3771 destroy();
3772
3773 const bool isCube = m_flags.testFlag(CubeMap);
3774 const bool is3D = m_flags.testFlag(ThreeDimensional);
3775 const bool isArray = m_flags.testFlag(TextureArray);
3776 const bool hasMipMaps = m_flags.testFlag(MipMapped);
3777 const bool is1D = m_flags.testFlag(OneDimensional);
3778
3779 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
3780 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
3781
3784 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
3785 samples = rhiD->effectiveSampleCount(m_sampleCount);
3786 if (samples > 1) {
3787 if (isCube) {
3788 qWarning("Cubemap texture cannot be multisample");
3789 return false;
3790 }
3791 if (is3D) {
3792 qWarning("3D texture cannot be multisample");
3793 return false;
3794 }
3795 if (hasMipMaps) {
3796 qWarning("Multisample texture cannot have mipmaps");
3797 return false;
3798 }
3799 }
3800 if (isCube && is3D) {
3801 qWarning("Texture cannot be both cube and 3D");
3802 return false;
3803 }
3804 if (isArray && is3D) {
3805 qWarning("Texture cannot be both array and 3D");
3806 return false;
3807 }
3808 if (is1D && is3D) {
3809 qWarning("Texture cannot be both 1D and 3D");
3810 return false;
3811 }
3812 if (is1D && isCube) {
3813 qWarning("Texture cannot be both 1D and cube");
3814 return false;
3815 }
3816 if (m_depth > 1 && !is3D) {
3817 qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
3818 return false;
3819 }
3820 if (m_arraySize > 0 && !isArray) {
3821 qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
3822 return false;
3823 }
3824 if (m_arraySize < 1 && isArray) {
3825 qWarning("Texture is an array but array size is %d", m_arraySize);
3826 return false;
3827 }
3828
3829 if (adjustedSize)
3830 *adjustedSize = size;
3831
3832 return true;
3833}
3834
3836{
3837 QSize size;
3838 if (!prepareCreate(&size))
3839 return false;
3840
3841 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3842
3843 const bool isCube = m_flags.testFlag(CubeMap);
3844 const bool is3D = m_flags.testFlag(ThreeDimensional);
3845 const bool isArray = m_flags.testFlag(TextureArray);
3846 const bool is1D = m_flags.testFlag(OneDimensional);
3847 if (isCube) {
3848 desc.textureType = MTLTextureTypeCube;
3849 } else if (is3D) {
3850 desc.textureType = MTLTextureType3D;
3851 } else if (is1D) {
3852 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
3853 } else if (isArray) {
3854#ifdef Q_OS_IOS
3855 if (@available(iOS 14, *)) {
3856 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
3857 } else {
3858 desc.textureType = MTLTextureType2DArray;
3859 }
3860#else
3861 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
3862#endif
3863 } else {
3864 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3865 }
3866 desc.pixelFormat = d->format;
3867 desc.width = NSUInteger(size.width());
3868 desc.height = NSUInteger(size.height());
3869 desc.depth = is3D ? qMax(1, m_depth) : 1;
3870 desc.mipmapLevelCount = NSUInteger(mipLevelCount);
3871 if (samples > 1)
3872 desc.sampleCount = NSUInteger(samples);
3873 if (isArray)
3874 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
3875 desc.resourceOptions = MTLResourceStorageModePrivate;
3876 desc.storageMode = MTLStorageModePrivate;
3877 desc.usage = MTLTextureUsageShaderRead;
3878 if (m_flags.testFlag(RenderTarget))
3879 desc.usage |= MTLTextureUsageRenderTarget;
3880 if (m_flags.testFlag(UsedWithLoadStore))
3881 desc.usage |= MTLTextureUsageShaderWrite;
3882
3884 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3885 [desc release];
3886
3887 if (!m_objectName.isEmpty())
3888 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3889
3890 d->owns = true;
3891
3893 generation += 1;
3894 rhiD->registerResource(this);
3895 return true;
3896}
3897
3899{
3900 id<MTLTexture> tex = id<MTLTexture>(src.object);
3901 if (tex == 0)
3902 return false;
3903
3904 if (!prepareCreate())
3905 return false;
3906
3907 d->tex = tex;
3908
3909 d->owns = false;
3910
3912 generation += 1;
3914 rhiD->registerResource(this);
3915 return true;
3916}
3917
3922
3924{
3925 Q_ASSERT(level >= 0 && level < int(q->mipLevelCount));
3926 if (perLevelViews[level])
3927 return perLevelViews[level];
3928
3929 const MTLTextureType type = [tex textureType];
3930 const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap);
3931 const bool isArray = q->m_flags.testFlag(QRhiTexture::TextureArray);
3932 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
3933 levels: NSMakeRange(NSUInteger(level), 1)
3934 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
3935
3937 return view;
3938}
3939
3942 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w),
3943 d(new QMetalSamplerData)
3944{
3945}
3946
3948{
3949 destroy();
3950 delete d;
3951}
3952
3954{
3955 if (!d->samplerState)
3956 return;
3957
3961
3962 e.sampler.samplerState = d->samplerState;
3963 d->samplerState = nil;
3964
3966 if (rhiD) {
3967 rhiD->d->releaseQueue.append(e);
3968 rhiD->unregisterResource(this);
3969 }
3970}
3971
3972static inline MTLSamplerMinMagFilter toMetalFilter(QRhiSampler::Filter f)
3973{
3974 switch (f) {
3976 return MTLSamplerMinMagFilterNearest;
3978 return MTLSamplerMinMagFilterLinear;
3979 default:
3980 Q_UNREACHABLE();
3981 return MTLSamplerMinMagFilterNearest;
3982 }
3983}
3984
3985static inline MTLSamplerMipFilter toMetalMipmapMode(QRhiSampler::Filter f)
3986{
3987 switch (f) {
3988 case QRhiSampler::None:
3989 return MTLSamplerMipFilterNotMipmapped;
3991 return MTLSamplerMipFilterNearest;
3993 return MTLSamplerMipFilterLinear;
3994 default:
3995 Q_UNREACHABLE();
3996 return MTLSamplerMipFilterNotMipmapped;
3997 }
3998}
3999
4000static inline MTLSamplerAddressMode toMetalAddressMode(QRhiSampler::AddressMode m)
4001{
4002 switch (m) {
4004 return MTLSamplerAddressModeRepeat;
4006 return MTLSamplerAddressModeClampToEdge;
4008 return MTLSamplerAddressModeMirrorRepeat;
4009 default:
4010 Q_UNREACHABLE();
4011 return MTLSamplerAddressModeClampToEdge;
4012 }
4013}
4014
4015static inline MTLCompareFunction toMetalTextureCompareFunction(QRhiSampler::CompareOp op)
4016{
4017 switch (op) {
4018 case QRhiSampler::Never:
4019 return MTLCompareFunctionNever;
4020 case QRhiSampler::Less:
4021 return MTLCompareFunctionLess;
4022 case QRhiSampler::Equal:
4023 return MTLCompareFunctionEqual;
4025 return MTLCompareFunctionLessEqual;
4027 return MTLCompareFunctionGreater;
4029 return MTLCompareFunctionNotEqual;
4031 return MTLCompareFunctionGreaterEqual;
4033 return MTLCompareFunctionAlways;
4034 default:
4035 Q_UNREACHABLE();
4036 return MTLCompareFunctionNever;
4037 }
4038}
4039
4041{
4042 if (d->samplerState)
4043 destroy();
4044
4045 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
4046 desc.minFilter = toMetalFilter(m_minFilter);
4047 desc.magFilter = toMetalFilter(m_magFilter);
4048 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
4049 desc.sAddressMode = toMetalAddressMode(m_addressU);
4050 desc.tAddressMode = toMetalAddressMode(m_addressV);
4051 desc.rAddressMode = toMetalAddressMode(m_addressW);
4052 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
4053
4055 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4056 [desc release];
4057
4059 generation += 1;
4060 rhiD->registerResource(this);
4061 return true;
4062}
4063
4064// dummy, no Vulkan-style RenderPass+Framebuffer concept here.
4065// We do have MTLRenderPassDescriptor of course, but it will be created on the fly for each pass.
4071
4076
4078{
4080 if (rhiD)
4081 rhiD->unregisterResource(this);
4082}
4083
4085{
4086 if (!other)
4087 return false;
4088
4090
4091 if (colorAttachmentCount != o->colorAttachmentCount)
4092 return false;
4093
4094 if (hasDepthStencil != o->hasDepthStencil)
4095 return false;
4096
4097 for (int i = 0; i < colorAttachmentCount; ++i) {
4098 if (colorFormat[i] != o->colorFormat[i])
4099 return false;
4100 }
4101
4102 if (hasDepthStencil) {
4103 if (dsFormat != o->dsFormat)
4104 return false;
4105 }
4106
4107 return true;
4108}
4109
4111{
4112 serializedFormatData.clear();
4113 auto p = std::back_inserter(serializedFormatData);
4114
4116 *p++ = hasDepthStencil;
4117 for (int i = 0; i < colorAttachmentCount; ++i)
4118 *p++ = colorFormat[i];
4119 *p++ = hasDepthStencil ? dsFormat : 0;
4120}
4121
4123{
4125 rpD->colorAttachmentCount = colorAttachmentCount;
4126 rpD->hasDepthStencil = hasDepthStencil;
4127 memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
4128 rpD->dsFormat = dsFormat;
4129
4130 rpD->updateSerializedFormat();
4131
4133 rhiD->registerResource(rpD, false);
4134 return rpD;
4135}
4136
4138{
4139 return serializedFormatData;
4140}
4141
4147
4153
4155{
4156 // nothing to do here
4157}
4158
4163
4165{
4166 return d->dpr;
4167}
4168
4170{
4171 return d->sampleCount;
4172}
4173
4181
4187
4189{
4191 if (rhiD)
4192 rhiD->unregisterResource(this);
4193}
4194
4196{
4197 const int colorAttachmentCount = int(m_desc.colorAttachmentCount());
4199 rpD->colorAttachmentCount = colorAttachmentCount;
4200 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4201
4202 for (int i = 0; i < colorAttachmentCount; ++i) {
4203 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4204 QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAtt->texture());
4206 rpD->colorFormat[i] = int(texD ? texD->d->format : rbD->d->format);
4207 }
4208
4209 if (m_desc.depthTexture())
4210 rpD->dsFormat = int(QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4211 else if (m_desc.depthStencilBuffer())
4212 rpD->dsFormat = int(QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
4213
4214 rpD->updateSerializedFormat();
4215
4217 rhiD->registerResource(rpD, false);
4218 return rpD;
4219}
4220
4222{
4226 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4227
4228 d->colorAttCount = 0;
4229 int attIndex = 0;
4230 for (auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4231 d->colorAttCount += 1;
4232 QMetalTexture *texD = QRHI_RES(QMetalTexture, it->texture());
4233 QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, it->renderBuffer());
4234 Q_ASSERT(texD || rbD);
4235 id<MTLTexture> dst = nil;
4236 bool is3D = false;
4237 if (texD) {
4238 dst = texD->d->tex;
4239 if (attIndex == 0) {
4240 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4241 d->sampleCount = texD->samples;
4242 }
4243 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4244 } else if (rbD) {
4245 dst = rbD->d->tex;
4246 if (attIndex == 0) {
4247 d->pixelSize = rbD->pixelSize();
4248 d->sampleCount = rbD->samples;
4249 }
4250 }
4252 colorAtt.tex = dst;
4253 colorAtt.arrayLayer = is3D ? 0 : it->layer();
4254 colorAtt.slice = is3D ? it->layer() : 0;
4255 colorAtt.level = it->level();
4256 QMetalTexture *resTexD = QRHI_RES(QMetalTexture, it->resolveTexture());
4257 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4258 colorAtt.resolveLayer = it->resolveLayer();
4259 colorAtt.resolveLevel = it->resolveLevel();
4260 d->fb.colorAtt[attIndex] = colorAtt;
4261 }
4262 d->dpr = 1;
4263
4264 if (hasDepthStencil) {
4265 if (m_desc.depthTexture()) {
4267 d->fb.dsTex = depthTexD->d->tex;
4268 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4269 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4271 if (d->colorAttCount == 0) {
4272 d->pixelSize = depthTexD->pixelSize();
4273 d->sampleCount = depthTexD->samples;
4274 }
4275 } else {
4277 d->fb.dsTex = depthRbD->d->tex;
4278 d->fb.hasStencil = true;
4279 d->fb.depthNeedsStore = false;
4280 d->fb.preserveDs = false;
4281 if (d->colorAttCount == 0) {
4282 d->pixelSize = depthRbD->pixelSize();
4283 d->sampleCount = depthRbD->samples;
4284 }
4285 }
4288 d->fb.dsResolveTex = depthResolveTexD->d->tex;
4289 }
4290 d->dsAttCount = 1;
4291 } else {
4292 d->dsAttCount = 0;
4293 }
4294
4295 if (d->colorAttCount > 0)
4297
4298 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4299
4300 rhiD->registerResource(this, false);
4301 return true;
4302}
4303
4305{
4306 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4307 const_cast<QMetalTextureRenderTarget *>(this)->create();
4308
4309 return d->pixelSize;
4310}
4311
4313{
4314 return d->dpr;
4315}
4316
4318{
4319 return d->sampleCount;
4320}
4321
4326
4331
4333{
4335 maxBinding = -1;
4336
4338 if (rhiD)
4339 rhiD->unregisterResource(this);
4340}
4341
4343{
4344 if (!sortedBindings.isEmpty())
4345 destroy();
4346
4348 if (!rhiD->sanityCheckShaderResourceBindings(this))
4349 return false;
4350
4351 rhiD->updateLayoutDesc(this);
4352
4353 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4355 if (!sortedBindings.isEmpty())
4357 else
4358 maxBinding = -1;
4359
4361
4363 memset(&bd, 0, sizeof(BoundResourceData));
4364
4365 generation += 1;
4366 rhiD->registerResource(this, false);
4367 return true;
4368}
4369
4371{
4373 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4374 if (!flags.testFlag(BindingsAreSorted))
4376
4378 memset(&bd, 0, sizeof(BoundResourceData));
4379
4380 generation += 1;
4381}
4382
4390
4396
4398{
4399 d->vs.destroy();
4400 d->fs.destroy();
4401
4402 d->tess.compVs[0].destroy();
4403 d->tess.compVs[1].destroy();
4404 d->tess.compVs[2].destroy();
4405
4406 d->tess.compTesc.destroy();
4407 d->tess.vertTese.destroy();
4408
4409 qDeleteAll(d->extraBufMgr.deviceLocalWorkBuffers);
4410 d->extraBufMgr.deviceLocalWorkBuffers.clear();
4411 qDeleteAll(d->extraBufMgr.hostVisibleWorkBuffers);
4412 d->extraBufMgr.hostVisibleWorkBuffers.clear();
4413
4414 delete d->bufferSizeBuffer;
4415 d->bufferSizeBuffer = nullptr;
4416
4417 if (!d->ps && !d->ds
4418 && !d->tess.vertexComputeState[0] && !d->tess.vertexComputeState[1] && !d->tess.vertexComputeState[2]
4419 && !d->tess.tessControlComputeState)
4420 {
4421 return;
4422 }
4423
4427 e.graphicsPipeline.pipelineState = d->ps;
4428 e.graphicsPipeline.depthStencilState = d->ds;
4429 e.graphicsPipeline.tessVertexComputeState = d->tess.vertexComputeState;
4430 e.graphicsPipeline.tessTessControlComputeState = d->tess.tessControlComputeState;
4431 d->ps = nil;
4432 d->ds = nil;
4433 d->tess.vertexComputeState = {};
4434 d->tess.tessControlComputeState = nil;
4435
4437 if (rhiD) {
4438 rhiD->d->releaseQueue.append(e);
4439 rhiD->unregisterResource(this);
4440 }
4441}
4442
4444{
4445 switch (format) {
4447 return MTLVertexFormatFloat4;
4449 return MTLVertexFormatFloat3;
4451 return MTLVertexFormatFloat2;
4453 return MTLVertexFormatFloat;
4455 return MTLVertexFormatUChar4Normalized;
4457 return MTLVertexFormatUChar2Normalized;
4459 return MTLVertexFormatUCharNormalized;
4461 return MTLVertexFormatUInt4;
4463 return MTLVertexFormatUInt3;
4465 return MTLVertexFormatUInt2;
4467 return MTLVertexFormatUInt;
4469 return MTLVertexFormatInt4;
4471 return MTLVertexFormatInt3;
4473 return MTLVertexFormatInt2;
4475 return MTLVertexFormatInt;
4477 return MTLVertexFormatHalf4;
4479 return MTLVertexFormatHalf3;
4481 return MTLVertexFormatHalf2;
4483 return MTLVertexFormatHalf;
4485 return MTLVertexFormatUShort4;
4487 return MTLVertexFormatUShort3;
4489 return MTLVertexFormatUShort2;
4491 return MTLVertexFormatUShort;
4493 return MTLVertexFormatShort4;
4495 return MTLVertexFormatShort3;
4497 return MTLVertexFormatShort2;
4499 return MTLVertexFormatShort;
4500 default:
4501 Q_UNREACHABLE();
4502 return MTLVertexFormatFloat4;
4503 }
4504}
4505
4507{
4508 switch (f) {
4510 return MTLBlendFactorZero;
4512 return MTLBlendFactorOne;
4514 return MTLBlendFactorSourceColor;
4516 return MTLBlendFactorOneMinusSourceColor;
4518 return MTLBlendFactorDestinationColor;
4520 return MTLBlendFactorOneMinusDestinationColor;
4522 return MTLBlendFactorSourceAlpha;
4524 return MTLBlendFactorOneMinusSourceAlpha;
4526 return MTLBlendFactorDestinationAlpha;
4528 return MTLBlendFactorOneMinusDestinationAlpha;
4530 return MTLBlendFactorBlendColor;
4532 return MTLBlendFactorBlendAlpha;
4534 return MTLBlendFactorOneMinusBlendColor;
4536 return MTLBlendFactorOneMinusBlendAlpha;
4538 return MTLBlendFactorSourceAlphaSaturated;
4540 return MTLBlendFactorSource1Color;
4542 return MTLBlendFactorOneMinusSource1Color;
4544 return MTLBlendFactorSource1Alpha;
4546 return MTLBlendFactorOneMinusSource1Alpha;
4547 default:
4548 Q_UNREACHABLE();
4549 return MTLBlendFactorZero;
4550 }
4551}
4552
4553static inline MTLBlendOperation toMetalBlendOp(QRhiGraphicsPipeline::BlendOp op)
4554{
4555 switch (op) {
4557 return MTLBlendOperationAdd;
4559 return MTLBlendOperationSubtract;
4561 return MTLBlendOperationReverseSubtract;
4563 return MTLBlendOperationMin;
4565 return MTLBlendOperationMax;
4566 default:
4567 Q_UNREACHABLE();
4568 return MTLBlendOperationAdd;
4569 }
4570}
4571
4572static inline uint toMetalColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
4573{
4574 uint f = 0;
4575 if (c.testFlag(QRhiGraphicsPipeline::R))
4576 f |= MTLColorWriteMaskRed;
4577 if (c.testFlag(QRhiGraphicsPipeline::G))
4578 f |= MTLColorWriteMaskGreen;
4579 if (c.testFlag(QRhiGraphicsPipeline::B))
4580 f |= MTLColorWriteMaskBlue;
4581 if (c.testFlag(QRhiGraphicsPipeline::A))
4582 f |= MTLColorWriteMaskAlpha;
4583 return f;
4584}
4585
4586static inline MTLCompareFunction toMetalCompareOp(QRhiGraphicsPipeline::CompareOp op)
4587{
4588 switch (op) {
4590 return MTLCompareFunctionNever;
4592 return MTLCompareFunctionLess;
4594 return MTLCompareFunctionEqual;
4596 return MTLCompareFunctionLessEqual;
4598 return MTLCompareFunctionGreater;
4600 return MTLCompareFunctionNotEqual;
4602 return MTLCompareFunctionGreaterEqual;
4604 return MTLCompareFunctionAlways;
4605 default:
4606 Q_UNREACHABLE();
4607 return MTLCompareFunctionAlways;
4608 }
4609}
4610
4611static inline MTLStencilOperation toMetalStencilOp(QRhiGraphicsPipeline::StencilOp op)
4612{
4613 switch (op) {
4615 return MTLStencilOperationZero;
4617 return MTLStencilOperationKeep;
4619 return MTLStencilOperationReplace;
4621 return MTLStencilOperationIncrementClamp;
4623 return MTLStencilOperationDecrementClamp;
4625 return MTLStencilOperationInvert;
4627 return MTLStencilOperationIncrementWrap;
4629 return MTLStencilOperationDecrementWrap;
4630 default:
4631 Q_UNREACHABLE();
4632 return MTLStencilOperationKeep;
4633 }
4634}
4635
4637{
4638 switch (t) {
4640 return MTLPrimitiveTypeTriangle;
4642 return MTLPrimitiveTypeTriangleStrip;
4644 return MTLPrimitiveTypeLine;
4646 return MTLPrimitiveTypeLineStrip;
4648 return MTLPrimitiveTypePoint;
4649 default:
4650 Q_UNREACHABLE();
4651 return MTLPrimitiveTypeTriangle;
4652 }
4653}
4654
4656{
4657 switch (t) {
4661 return MTLPrimitiveTopologyClassTriangle;
4664 return MTLPrimitiveTopologyClassLine;
4666 return MTLPrimitiveTopologyClassPoint;
4667 default:
4668 Q_UNREACHABLE();
4669 return MTLPrimitiveTopologyClassTriangle;
4670 }
4671}
4672
4674{
4675 switch (c) {
4677 return MTLCullModeNone;
4679 return MTLCullModeFront;
4681 return MTLCullModeBack;
4682 default:
4683 Q_UNREACHABLE();
4684 return MTLCullModeNone;
4685 }
4686}
4687
4689{
4690 switch (mode) {
4692 return MTLTriangleFillModeFill;
4694 return MTLTriangleFillModeLines;
4695 default:
4696 Q_UNREACHABLE();
4697 return MTLTriangleFillModeFill;
4698 }
4699}
4700
4702{
4703 switch (w) {
4705 return MTLWindingClockwise;
4707 return MTLWindingCounterClockwise;
4708 default:
4709 // this is reachable, consider a tess.eval. shader not declaring it, the value is then Unknown
4710 return MTLWindingCounterClockwise;
4711 }
4712}
4713
4715{
4716 switch (p) {
4718 return MTLTessellationPartitionModePow2;
4720 return MTLTessellationPartitionModeFractionalEven;
4722 return MTLTessellationPartitionModeFractionalOdd;
4723 default:
4724 // this is reachable, consider a tess.eval. shader not declaring it, the value is then Unknown
4725 return MTLTessellationPartitionModePow2;
4726 }
4727}
4728
4729static inline MTLLanguageVersion toMetalLanguageVersion(const QShaderVersion &version)
4730{
4731 int v = version.version();
4732 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
4733}
4734
4736 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
4737{
4738 QVarLengthArray<int, 8> versions;
4739 if (@available(macOS 13, iOS 16, *))
4740 versions << 30;
4741 if (@available(macOS 12, iOS 15, *))
4742 versions << 24;
4743 if (@available(macOS 11, iOS 14, *))
4744 versions << 23;
4745 if (@available(macOS 10.15, iOS 13, *))
4746 versions << 22;
4747 if (@available(macOS 10.14, iOS 12, *))
4748 versions << 21;
4749 versions << 20 << 12;
4750
4751 const QList<QShaderKey> shaders = shader.availableShaders();
4752
4754
4755 for (const int &version : versions) {
4756 key = { QShader::Source::MetalLibShader, version, shaderVariant };
4757 if (shaders.contains(key))
4758 break;
4759 }
4760
4761 QShaderCode mtllib = shader.shader(key);
4762 if (!mtllib.shader().isEmpty()) {
4763 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
4764 size_t(mtllib.shader().size()),
4765 dispatch_get_global_queue(0, 0),
4766 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
4767 NSError *err = nil;
4768 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
4769 dispatch_release(data);
4770 if (!err) {
4771 *entryPoint = mtllib.entryPoint();
4772 *activeKey = key;
4773 return lib;
4774 } else {
4775 const QString msg = QString::fromNSString(err.localizedDescription);
4776 qWarning("Failed to load metallib from baked shader: %s", qPrintable(msg));
4777 }
4778 }
4779
4780 for (const int &version : versions) {
4781 key = { QShader::Source::MslShader, version, shaderVariant };
4782 if (shaders.contains(key))
4783 break;
4784 }
4785
4786 QShaderCode mslSource = shader.shader(key);
4787 if (mslSource.shader().isEmpty()) {
4788 qWarning() << "No MSL 2.0 or 1.2 code found in baked shader" << shader;
4789 return nil;
4790 }
4791
4792 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
4793 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
4794 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
4795 NSError *err = nil;
4796 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
4797 [opts release];
4798 // src is autoreleased
4799
4800 // if lib is null and err is non-null, we had errors (fail)
4801 // if lib is non-null and err is non-null, we had warnings (success)
4802 // if lib is non-null and err is null, there were no errors or warnings (success)
4803 if (!lib) {
4804 const QString msg = QString::fromNSString(err.localizedDescription);
4805 *error = msg;
4806 return nil;
4807 }
4808
4809 *entryPoint = mslSource.entryPoint();
4810 *activeKey = key;
4811 return lib;
4812}
4813
4814id<MTLFunction> QRhiMetalData::createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint)
4815{
4816 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
4817}
4818
4820{
4821 MTLRenderPipelineDescriptor *rpDesc = reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
4822
4823 if (rpD->colorAttachmentCount) {
4824 // defaults when no targetBlends are provided
4825 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD->colorFormat[0]);
4826 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
4827 rpDesc.colorAttachments[0].blendingEnabled = false;
4828
4829 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
4830 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
4831
4832 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
4834 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD->colorFormat[i]);
4835 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
4836 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
4837 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
4838 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
4839 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
4840 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
4841 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
4842 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
4843 }
4844 }
4845
4846 if (rpD->hasDepthStencil) {
4847 // Must only be set when a depth-stencil buffer will actually be bound,
4848 // validation blows up otherwise.
4849 MTLPixelFormat fmt = MTLPixelFormat(rpD->dsFormat);
4850 rpDesc.depthAttachmentPixelFormat = fmt;
4851#if defined(Q_OS_MACOS)
4852 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
4853#else
4854 if (fmt != MTLPixelFormatDepth32Float)
4855#endif
4856 rpDesc.stencilAttachmentPixelFormat = fmt;
4857 }
4858
4860 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
4861}
4862
4864{
4865 MTLDepthStencilDescriptor *dsDesc = reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
4866
4867 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
4868 dsDesc.depthWriteEnabled = m_depthWrite;
4869 if (m_stencilTest) {
4870 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
4871 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
4872 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
4873 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
4874 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
4875 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
4876 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
4877
4878 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
4879 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
4880 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
4881 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
4882 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
4883 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
4884 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
4885 }
4886}
4887
4889{
4890 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
4893 d->depthBias = float(m_depthBias);
4895}
4896
4898{
4899 // same binding space for vertex and constant buffers - work it around
4900 // should be in native resource binding not SPIR-V, but this will work anyway
4901 const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, q->shaderResourceBindings())->maxBinding + 1;
4902
4903 QRhiVertexInputLayout vertexInputLayout = q->vertexInputLayout();
4904 for (auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
4905 it != itEnd; ++it)
4906 {
4907 const uint loc = uint(it->location());
4908 desc.attributes[loc].format = decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
4909 desc.attributes[loc].offset = NSUInteger(it->offset());
4910 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
4911 }
4912 int bindingIndex = 0;
4913 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
4914 for (auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
4915 it != itEnd; ++it, ++bindingIndex)
4916 {
4917 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
4918 desc.layouts[layoutIdx].stepFunction =
4919 it->classification() == QRhiVertexInputBinding::PerInstance
4920 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
4921 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
4922 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
4923 desc.layouts[layoutIdx].stepRate *= viewCount;
4924 desc.layouts[layoutIdx].stride = it->stride();
4925 }
4926}
4927
4928void QMetalGraphicsPipelineData::setupStageInputDescriptor(MTLStageInputOutputDescriptor *desc)
4929{
4930 // same binding space for vertex and constant buffers - work it around
4931 // should be in native resource binding not SPIR-V, but this will work anyway
4932 const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, q->shaderResourceBindings())->maxBinding + 1;
4933
4934 QRhiVertexInputLayout vertexInputLayout = q->vertexInputLayout();
4935 for (auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
4936 it != itEnd; ++it)
4937 {
4938 const uint loc = uint(it->location());
4939 desc.attributes[loc].format = decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
4940 desc.attributes[loc].offset = NSUInteger(it->offset());
4941 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
4942 }
4943 int bindingIndex = 0;
4944 for (auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
4945 it != itEnd; ++it, ++bindingIndex)
4946 {
4947 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
4948 if (desc.indexBufferIndex) {
4949 desc.layouts[layoutIdx].stepFunction =
4950 it->classification() == QRhiVertexInputBinding::PerInstance
4951 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
4952 } else {
4953 desc.layouts[layoutIdx].stepFunction =
4954 it->classification() == QRhiVertexInputBinding::PerInstance
4955 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
4956 }
4957 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
4958 desc.layouts[layoutIdx].stride = it->stride();
4959 }
4960}
4961
4962void QRhiMetalData::trySeedingRenderPipelineFromBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
4963{
4964 if (@available(macOS 11.0, iOS 14.0, *)) {
4965 if (binArch) {
4966 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
4967 rpDesc.binaryArchives = binArchArray;
4968 }
4969 }
4970}
4971
4972void QRhiMetalData::addRenderPipelineToBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
4973{
4974 if (@available(macOS 11.0, iOS 14.0, *)) {
4975 if (binArch) {
4976 NSError *err = nil;
4977 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
4978 const QString msg = QString::fromNSString(err.localizedDescription);
4979 qWarning("Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
4980 }
4981 }
4982 }
4983}
4984
4986{
4988
4989 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
4990 d->setupVertexInputDescriptor(vertexDesc);
4991
4992 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
4993 rpDesc.vertexDescriptor = vertexDesc;
4994
4995 // Mutability cannot be determined (slotted buffers could be set as
4996 // MTLMutabilityImmutable, but then we potentially need a different
4997 // descriptor for each buffer combination as this depends on the actual
4998 // buffers not just the resource binding layout), so leave
4999 // rpDesc.vertex/fragmentBuffers at the defaults.
5000
5001 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5002 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5003 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5004 switch (shaderStage.type()) {
5006 d->vs = *cacheIt;
5007 [d->vs.lib retain];
5008 [d->vs.func retain];
5009 rpDesc.vertexFunction = d->vs.func;
5010 break;
5012 d->fs = *cacheIt;
5013 [d->fs.lib retain];
5014 [d->fs.func retain];
5015 rpDesc.fragmentFunction = d->fs.func;
5016 break;
5017 default:
5018 break;
5019 }
5020 } else {
5021 const QShader shader = shaderStage.shader();
5022 QString error;
5023 QByteArray entryPoint;
5024 QShaderKey activeKey;
5025 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5026 &error, &entryPoint, &activeKey);
5027 if (!lib) {
5028 qWarning("MSL shader compilation failed: %s", qPrintable(error));
5029 return false;
5030 }
5031 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5032 if (!func) {
5033 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5034 [lib release];
5035 return false;
5036 }
5037 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5038 // Use the simplest strategy: too many cached shaders -> drop them all.
5039 for (QMetalShader &s : rhiD->d->shaderCache)
5040 s.destroy();
5041 rhiD->d->shaderCache.clear();
5042 }
5043 switch (shaderStage.type()) {
5045 d->vs.lib = lib;
5046 d->vs.func = func;
5047 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5048 d->vs.desc = shader.description();
5049 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5050 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5051 [d->vs.lib retain];
5052 [d->vs.func retain];
5053 rpDesc.vertexFunction = func;
5054 break;
5056 d->fs.lib = lib;
5057 d->fs.func = func;
5058 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5059 d->fs.desc = shader.description();
5060 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5061 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5062 [d->fs.lib retain];
5063 [d->fs.func retain];
5064 rpDesc.fragmentFunction = func;
5065 break;
5066 default:
5067 [func release];
5068 [lib release];
5069 break;
5070 }
5071 }
5072 }
5073
5076
5077 if (m_multiViewCount >= 2)
5078 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5079
5080 rhiD->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5081
5082 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5083 rhiD->d->addRenderPipelineToBinaryArchive(rpDesc);
5084
5085 NSError *err = nil;
5086 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5087 [rpDesc release];
5088 if (!d->ps) {
5089 const QString msg = QString::fromNSString(err.localizedDescription);
5090 qWarning("Failed to create render pipeline state: %s", qPrintable(msg));
5091 return false;
5092 }
5093
5094 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5096 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5097 [dsDesc release];
5098
5100 mapStates();
5101
5102 return true;
5103}
5104
5106{
5107 switch (vertexCompVariant) {
5109 return 0;
5111 return 1;
5113 return 2;
5114 default:
5115 break;
5116 }
5117 return -1;
5118}
5119
5121{
5122 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5123 if (varIndex >= 0 && vertexComputeState[varIndex])
5124 return vertexComputeState[varIndex];
5125
5126 id<MTLFunction> func = nil;
5127 if (varIndex >= 0)
5128 func = compVs[varIndex].func;
5129
5130 if (!func) {
5131 qWarning("No compute function found for vertex shader translated for tessellation, this should not happen");
5132 return nil;
5133 }
5134
5135 const QMap<int, int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5136 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5137
5138 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor new];
5139 cpDesc.computeFunction = func;
5140 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5141 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5142 if (indexBufferBinding >= 0) {
5143 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5144 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5145 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5146 } else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5147 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5148 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5149 }
5150 }
5151 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5152
5153 rhiD->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5154
5155 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5156 rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
5157
5158 NSError *err = nil;
5159 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5160 options: MTLPipelineOptionNone
5161 reflection: nil
5162 error: &err];
5163 [cpDesc release];
5164 if (!ps) {
5165 const QString msg = QString::fromNSString(err.localizedDescription);
5166 qWarning("Failed to create compute pipeline state: %s", qPrintable(msg));
5167 } else {
5168 vertexComputeState[varIndex] = ps;
5169 }
5170 // not retained, the only owner is vertexComputeState and so the QRhiGraphicsPipeline
5171 return ps;
5172}
5173
5175{
5176 if (tessControlComputeState)
5177 return tessControlComputeState;
5178
5179 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor new];
5180 cpDesc.computeFunction = compTesc.func;
5181
5182 rhiD->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5183
5184 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5185 rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
5186
5187 NSError *err = nil;
5188 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5189 options: MTLPipelineOptionNone
5190 reflection: nil
5191 error: &err];
5192 [cpDesc release];
5193 if (!ps) {
5194 const QString msg = QString::fromNSString(err.localizedDescription);
5195 qWarning("Failed to create compute pipeline state: %s", qPrintable(msg));
5196 } else {
5197 tessControlComputeState = ps;
5198 }
5199 // not retained, the only owner is tessControlComputeState and so the QRhiGraphicsPipeline
5200 return ps;
5201}
5202
5204{
5205 return (indices >> index) & 0x1;
5206}
5207
5209{
5210 indices |= 1 << index;
5211}
5212
5214{
5215 // Maximum number of vertex attributes per vertex descriptor. There does
5216 // not appear to be a way to query this from the implementation.
5217 // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf indicates
5218 // that all GPU families have a value of 31.
5219 static const int maxVertexAttributes = 31;
5220
5221 for (int index = 0; index < maxVertexAttributes; ++index) {
5222 if (!indexTaken(index, indices))
5223 return index;
5224 }
5225
5226 Q_UNREACHABLE_RETURN(-1);
5227}
5228
5230{
5231 return ((offset + alignment - 1) / alignment) * alignment;
5232}
5233
5234template<typename T>
5235static void addUnusedVertexAttribute(const T &variable, QRhiMetal *rhiD, quint32 &offset, quint32 &vertexAlignment)
5236{
5237
5238 int elements = 1;
5239 for (const int dim : variable.arrayDims)
5240 elements *= dim;
5241
5243 for (int element = 0; element < elements; ++element) {
5244 for (const auto &member : variable.structMembers) {
5245 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5246 }
5247 }
5248 } else {
5249 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5250 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5251
5252 // MSL specification 3.0 says alignment = size for non packed scalars and vectors
5253 const quint32 alignment = size;
5254 vertexAlignment = std::max(vertexAlignment, alignment);
5255
5256 for (int element = 0; element < elements; ++element) {
5257 // adjust alignment
5259 offset += size;
5260 }
5261 }
5262}
5263
5264template<typename T>
5265static void addVertexAttribute(const T &variable, int binding, QRhiMetal *rhiD, int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5266{
5267
5268 int elements = 1;
5269 for (const int dim : variable.arrayDims)
5270 elements *= dim;
5271
5273 for (int element = 0; element < elements; ++element) {
5274 for (const auto &member : variable.structMembers) {
5275 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5276 }
5277 }
5278 } else {
5279 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5280 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5281
5282 // MSL specification 3.0 says alignment = size for non packed scalars and vectors
5283 const quint32 alignment = size;
5284 vertexAlignment = std::max(vertexAlignment, alignment);
5285
5286 for (int element = 0; element < elements; ++element) {
5288
5289 // adjust alignment
5291
5292 attributes[index].bufferIndex = binding;
5293 attributes[index].format = toMetalAttributeFormat(format);
5294 attributes[index].offset = offset;
5295
5297 index++;
5298 if (indexTaken(index, indices))
5300
5301 offset += size;
5302 }
5303 }
5304}
5305
5306static inline bool matches(const QList<QShaderDescription::BlockVariable> &a, const QList<QShaderDescription::BlockVariable> &b)
5307{
5308 if (a.size() == b.size()) {
5309 bool match = true;
5310 for (int i = 0; i < a.size() && match; ++i) {
5311 match &= a[i].type == b[i].type
5312 && a[i].arrayDims == b[i].arrayDims
5313 && matches(a[i].structMembers, b[i].structMembers);
5314 }
5315 return match;
5316 }
5317
5318 return false;
5319}
5320
5322{
5323 return a.location == b.location
5324 && a.type == b.type
5325 && a.perPatch == b.perPatch
5326 && matches(a.structMembers, b.structMembers);
5327}
5328
5329//
5330// Create the tessellation evaluation render pipeline state
5331//
5332// The tesc runs as a compute shader in a compute pipeline and writes per patch and per patch
5333// control point data into separate storage buffers. The tese runs as a vertex shader in a render
5334// pipeline. Our task is to generate a render pipeline descriptor for the tese that pulls vertices
5335// from these buffers.
5336//
5337// As the buffers we are pulling vertices from are written by a compute pipeline, they follow the
5338// MSL alignment conventions which we must take into account when generating our
5339// MTLVertexDescriptor. We must include the user defined tese input attributes, and any builtins
5340// that were used.
5341//
5342// SPIRV-Cross generates the MSL tese shader code with input attribute indices that reflect the
5343// specified GLSL locations. Interface blocks are flattened with each member having an incremented
5344// attribute index. SPIRV-Cross reports an error on compilation if there are clashes in the index
5345// address space.
5346//
5347// After the user specified attributes are processed, SPIRV-Cross places the in-use builtins at the
5348// next available (lowest value) attribute index. Tese builtins are processed in the following
5349// order:
5350//
5351// in gl_PerVertex
5352// {
5353// vec4 gl_Position;
5354// float gl_PointSize;
5355// float gl_ClipDistance[];
5356// };
5357//
5358// patch in float gl_TessLevelOuter[4];
5359// patch in float gl_TessLevelInner[2];
5360//
5361// Enumerations in QShaderDescription::BuiltinType are defined in this order.
5362//
5363// For quads, SPIRV-Cross places MTLQuadTessellationFactorsHalf per patch in the tessellation
5364// factor buffer. For triangles it uses MTLTriangleTessellationFactorsHalf.
5365//
5366// It should be noted that SPIRV-Cross handles the following builtin inputs internally, with no
5367// host side support required.
5368//
5369// in vec3 gl_TessCoord;
5370// in int gl_PatchVerticesIn;
5371// in int gl_PrimitiveID;
5372//
5374{
5375 if (pipeline->d->ps)
5376 return pipeline->d->ps;
5377
5378 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5379 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5380
5381 // tesc output buffers
5382 const QMap<int, int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5383 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5384 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5385 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5386 quint32 offsetInTescOutput = 0;
5387 quint32 offsetInTescPatchOutput = 0;
5388 quint32 offsetInTessFactorBuffer = 0;
5389 quint32 tescOutputAlignment = 0;
5390 quint32 tescPatchOutputAlignment = 0;
5391 quint32 tessFactorAlignment = 0;
5392 QSet<int> usedBuffers;
5393
5394 // tesc output variables in ascending location order
5395 QMap<int, QShaderDescription::InOutVariable> tescOutVars;
5396 for (const auto &tescOutVar : compTesc.desc.outputVariables())
5397 tescOutVars[tescOutVar.location] = tescOutVar;
5398
5399 // tese input variables in ascending location order
5400 QMap<int, QShaderDescription::InOutVariable> teseInVars;
5401 for (const auto &teseInVar : vertTese.desc.inputVariables())
5402 teseInVars[teseInVar.location] = teseInVar;
5403
5404 // bit mask tracking usage of vertex attribute indices
5405 quint64 indices = 0;
5406
5407 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5408
5409 int index = tescOutVar.location;
5410 int binding = -1;
5411 quint32 *offset = nullptr;
5412 quint32 *alignment = nullptr;
5413
5414 if (tescOutVar.perPatch) {
5415 binding = tescPatchOutputBufferBinding;
5416 offset = &offsetInTescPatchOutput;
5417 alignment = &tescPatchOutputAlignment;
5418 } else {
5419 tescOutVar.arrayDims.removeLast();
5420 binding = tescOutputBufferBinding;
5421 offset = &offsetInTescOutput;
5422 alignment = &tescOutputAlignment;
5423 }
5424
5425 if (teseInVars.contains(index)) {
5426
5427 if (!matches(teseInVars[index], tescOutVar)) {
5428 qWarning() << "mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5429 qWarning() << " tesc out:" << tescOutVar;
5430 qWarning() << " tese in:" << teseInVars[index];
5431 }
5432
5433 if (binding != -1) {
5434 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5435 usedBuffers << binding;
5436 } else {
5437 qWarning() << "baked tessellation control shader missing output buffer binding information";
5438 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5439 }
5440
5441 } else {
5442 qWarning() << "missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5443 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5444 }
5445
5446 teseInVars.remove(tescOutVar.location);
5447 }
5448
5449 for (const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5450 qWarning() << "missing tessellation control output for tessellation evaluation input:" << teseInVar;
5451
5452 // tesc output builtins in ascending location order
5453 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5454 for (const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5455 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5456
5457 // tese input builtins in ascending location order
5458 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5459 for (const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5460 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5461
5462 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5463 bool tessLevelAdded = false;
5464
5465 for (const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5466
5468 int binding = -1;
5469 quint32 *offset = nullptr;
5470 quint32 *alignment = nullptr;
5471
5472 switch (builtin.type) {
5475 binding = tescOutputBufferBinding;
5476 offset = &offsetInTescOutput;
5477 alignment = &tescOutputAlignment;
5478 break;
5481 binding = tescOutputBufferBinding;
5482 offset = &offsetInTescOutput;
5483 alignment = &tescOutputAlignment;
5484 break;
5487 variable.arrayDims = builtin.arrayDims;
5488 binding = tescOutputBufferBinding;
5489 offset = &offsetInTescOutput;
5490 alignment = &tescOutputAlignment;
5491 break;
5494 binding = tessFactorBufferBinding;
5495 offset = &offsetInTessFactorBuffer;
5496 tessLevelAdded = trianglesMode;
5497 alignment = &tessFactorAlignment;
5498 break;
5500 if (trianglesMode) {
5501 if (!tessLevelAdded) {
5503 binding = tessFactorBufferBinding;
5504 offsetInTessFactorBuffer = 0;
5505 offset = &offsetInTessFactorBuffer;
5506 alignment = &tessFactorAlignment;
5507 tessLevelAdded = true;
5508 } else {
5509 teseInBuiltins.remove(builtin.type);
5510 continue;
5511 }
5512 } else {
5514 binding = tessFactorBufferBinding;
5515 offsetInTessFactorBuffer = 8;
5516 offset = &offsetInTessFactorBuffer;
5517 alignment = &tessFactorAlignment;
5518 }
5519 break;
5520 default:
5521 Q_UNREACHABLE();
5522 break;
5523 }
5524
5525 if (teseInBuiltins.contains(builtin.type)) {
5526 if (binding != -1) {
5528 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5529 usedBuffers << binding;
5530 } else {
5531 qWarning() << "baked tessellation control shader missing output buffer binding information";
5533 }
5534 } else {
5536 }
5537
5538 teseInBuiltins.remove(builtin.type);
5539 }
5540
5541 for (const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
5542 switch (builtin.type) {
5546 qWarning() << "missing tessellation control output for tessellation evaluation builtin input:" << builtin;
5547 break;
5548 default:
5549 break;
5550 }
5551 }
5552
5553 if (usedBuffers.contains(tescOutputBufferBinding)) {
5554 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
5555 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
5556 }
5557
5558 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
5559 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5560 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
5561 }
5562
5563 if (usedBuffers.contains(tessFactorBufferBinding)) {
5564 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5565 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ? sizeof(MTLTriangleTessellationFactorsHalf) : sizeof(MTLQuadTessellationFactorsHalf);
5566 }
5567
5568 rpDesc.vertexDescriptor = vertexDesc;
5569 rpDesc.vertexFunction = vertTese.func;
5570 rpDesc.fragmentFunction = pipeline->d->fs.func;
5571
5572 // The portable, cross-API approach is to use CCW, the results are then
5573 // identical (assuming the applied clipSpaceCorrMatrix) for all the 3D
5574 // APIs. The tess.eval. GLSL shader is thus expected to specify ccw. If it
5575 // doesn't, things may not work as expected.
5576 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
5577
5578 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
5579
5581 pipeline->setupAttachmentsInMetalRenderPassDescriptor(rpDesc, rpD);
5582
5583 rhiD->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5584
5585 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5586 rhiD->d->addRenderPipelineToBinaryArchive(rpDesc);
5587
5588 NSError *err = nil;
5589 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5590 [rpDesc release];
5591 if (!ps) {
5592 const QString msg = QString::fromNSString(err.localizedDescription);
5593 qWarning("Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
5594 } else {
5595 // ps is stored in the QMetalGraphicsPipelineData so the end result in this
5596 // regard is no different from what createVertexFragmentPipeline does
5597 pipeline->d->ps = ps;
5598 }
5599 return ps;
5600}
5601
5603{
5604 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
5605
5606 // Check if something is reusable as-is.
5607 for (QMetalBuffer *workBuf : *workBuffers) {
5608 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
5609 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5610 return workBuf;
5611 }
5612 }
5613
5614 // Once the pool is above a certain threshold, see if there is something
5615 // unused (but too small) and recreate that our size.
5616 if (workBuffers->count() > QMTL_FRAMES_IN_FLIGHT * 8) {
5617 for (QMetalBuffer *workBuf : *workBuffers) {
5618 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
5619 workBuf->setSize(size);
5620 if (workBuf->create()) {
5621 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5622 return workBuf;
5623 }
5624 }
5625 }
5626 }
5627
5628 // Add a new buffer to the pool.
5630 if (type == WorkBufType::DeviceLocal) {
5631 // for GPU->GPU data (non-slotted, not necessarily host writable)
5632 buf = new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5633 } else {
5634 // for CPU->GPU (non-slotted, host writable/coherent)
5635 buf = new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5636 }
5637 if (buf->create()) {
5638 buf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5639 workBuffers->append(buf);
5640 return buf;
5641 }
5642
5643 qWarning("Failed to acquire work buffer of size %u", size);
5644 return nullptr;
5645}
5646
5647bool QMetalGraphicsPipeline::createTessellationPipelines(const QShader &tessVert, const QShader &tesc, const QShader &tese, const QShader &tessFrag)
5648{
5650 QString error;
5651 QByteArray entryPoint;
5652 QShaderKey activeKey;
5653
5654 const QShaderDescription tescDesc = tesc.description();
5655 const QShaderDescription teseDesc = tese.description();
5656 d->tess.inControlPointCount = uint(m_patchControlPointCount);
5657 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
5658 if (!d->tess.outControlPointCount)
5659 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
5660
5661 if (!d->tess.outControlPointCount) {
5662 qWarning("Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
5663 d->tess.enabled = false;
5664 d->tess.failed = true;
5665 return false;
5666 }
5667
5668 if (m_multiViewCount >= 2)
5669 qWarning("Multiview is not supported with tessellation");
5670
5671 // Now the vertex shader is a compute shader.
5672 // It should have three dedicated *VertexAsComputeShader variants.
5673 // What the requested variant was (Standard or Batchable) plays no role here.
5674 // (the Qt Quick scenegraph does not use tessellation with its materials)
5675 // Create all three versions.
5676
5677 bool variantsPresent[3] = {};
5678 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
5679 for (const QShaderKey &k : tessVertKeys) {
5680 switch (k.sourceVariant()) {
5682 variantsPresent[0] = true;
5683 break;
5685 variantsPresent[1] = true;
5686 break;
5688 variantsPresent[2] = true;
5689 break;
5690 default:
5691 break;
5692 }
5693 }
5694 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
5695 qWarning("Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
5696 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
5697 "Try passing --msltess to qsb.");
5698 d->tess.enabled = false;
5699 d->tess.failed = true;
5700 return false;
5701 }
5702
5703 int varIndex = 0; // Will map NonIndexed as 0, UInt32 as 1, UInt16 as 2. Do not change this ordering.
5704 for (QShader::Variant variant : {
5708 {
5709 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
5710 if (!lib) {
5711 qWarning("MSL shader compilation failed for vertex-as-compute shader %d: %s", int(variant), qPrintable(error));
5712 d->tess.enabled = false;
5713 d->tess.failed = true;
5714 return false;
5715 }
5716 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5717 if (!func) {
5718 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5719 [lib release];
5720 d->tess.enabled = false;
5721 d->tess.failed = true;
5722 return false;
5723 }
5724 QMetalShader &compVs(d->tess.compVs[varIndex]);
5725 compVs.lib = lib;
5726 compVs.func = func;
5727 compVs.desc = tessVert.description();
5728 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
5729 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
5730
5731 // pre-create all three MTLComputePipelineStates
5732 if (!d->tess.vsCompPipeline(rhiD, variant)) {
5733 qWarning("Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)", int(variant));
5734 d->tess.enabled = false;
5735 d->tess.failed = true;
5736 return false;
5737 }
5738
5739 ++varIndex;
5740 }
5741
5742 // Pipeline #2 is a compute that runs the tessellation control (compute) shader
5743 id<MTLLibrary> tessControlLib = rhiD->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
5744 if (!tessControlLib) {
5745 qWarning("MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
5746 d->tess.enabled = false;
5747 d->tess.failed = true;
5748 return false;
5749 }
5750 id<MTLFunction> tessControlFunc = rhiD->d->createMSLShaderFunction(tessControlLib, entryPoint);
5751 if (!tessControlFunc) {
5752 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5753 [tessControlLib release];
5754 d->tess.enabled = false;
5755 d->tess.failed = true;
5756 return false;
5757 }
5758 d->tess.compTesc.lib = tessControlLib;
5759 d->tess.compTesc.func = tessControlFunc;
5760 d->tess.compTesc.desc = tesc.description();
5761 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
5762 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
5763 if (!d->tess.tescCompPipeline(rhiD)) {
5764 qWarning("Failed to pre-generate compute pipeline for tessellation control shader");
5765 d->tess.enabled = false;
5766 d->tess.failed = true;
5767 return false;
5768 }
5769
5770 // Pipeline #3 is a render pipeline with the tessellation evaluation (vertex) + the fragment shader
5771 id<MTLLibrary> tessEvalLib = rhiD->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
5772 if (!tessEvalLib) {
5773 qWarning("MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
5774 d->tess.enabled = false;
5775 d->tess.failed = true;
5776 return false;
5777 }
5778 id<MTLFunction> tessEvalFunc = rhiD->d->createMSLShaderFunction(tessEvalLib, entryPoint);
5779 if (!tessEvalFunc) {
5780 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5781 [tessEvalLib release];
5782 d->tess.enabled = false;
5783 d->tess.failed = true;
5784 return false;
5785 }
5786 d->tess.vertTese.lib = tessEvalLib;
5787 d->tess.vertTese.func = tessEvalFunc;
5788 d->tess.vertTese.desc = tese.description();
5789 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
5790 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
5791
5792 id<MTLLibrary> fragLib = rhiD->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
5793 if (!fragLib) {
5794 qWarning("MSL shader compilation failed for fragment shader: %s", qPrintable(error));
5795 d->tess.enabled = false;
5796 d->tess.failed = true;
5797 return false;
5798 }
5799 id<MTLFunction> fragFunc = rhiD->d->createMSLShaderFunction(fragLib, entryPoint);
5800 if (!fragFunc) {
5801 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5802 [fragLib release];
5803 d->tess.enabled = false;
5804 d->tess.failed = true;
5805 return false;
5806 }
5807 d->fs.lib = fragLib;
5808 d->fs.func = fragFunc;
5809 d->fs.desc = tessFrag.description();
5810 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
5811 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
5812
5813 if (!d->tess.teseFragRenderPipeline(rhiD, this)) {
5814 qWarning("Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
5815 d->tess.enabled = false;
5816 d->tess.failed = true;
5817 return false;
5818 }
5819
5820 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5821 setupMetalDepthStencilDescriptor(dsDesc);
5822 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5823 [dsDesc release];
5824
5825 // no primitiveType
5826 mapStates();
5827
5828 return true;
5829}
5830
5832{
5833 destroy(); // no early test, always invoke and leave it to destroy to decide what to clean up
5834
5836 rhiD->pipelineCreationStart();
5837 if (!rhiD->sanityCheckGraphicsPipeline(this))
5838 return false;
5839
5840 // See if tessellation is involved. Things will be very different, if so.
5841 QShader tessVert;
5842 QShader tesc;
5843 QShader tese;
5844 QShader tessFrag;
5845 for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5846 switch (shaderStage.type()) {
5848 tessVert = shaderStage.shader();
5849 break;
5851 tesc = shaderStage.shader();
5852 break;
5854 tese = shaderStage.shader();
5855 break;
5857 tessFrag = shaderStage.shader();
5858 break;
5859 default:
5860 break;
5861 }
5862 }
5863 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
5864 d->tess.failed = false;
5865
5866 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
5867 if (!ok)
5868 return false;
5869
5870 // SPIRV-Cross buffer size buffers
5871 int buffers = 0;
5872 QVarLengthArray<QMetalShader *, 6> shaders;
5873 if (d->tess.enabled) {
5874 shaders.append(&d->tess.compVs[0]);
5875 shaders.append(&d->tess.compVs[1]);
5876 shaders.append(&d->tess.compVs[2]);
5877 shaders.append(&d->tess.compTesc);
5878 shaders.append(&d->tess.vertTese);
5879 } else {
5880 shaders.append(&d->vs);
5881 }
5882 shaders.append(&d->fs);
5883
5884 for (QMetalShader *shader : shaders) {
5885 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
5886 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
5887 shader->nativeResourceBindingMap[binding] = qMakePair(binding, -1);
5888 int maxNativeBinding = 0;
5889 for (const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
5890 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
5891
5892 // we use one buffer to hold data for all graphics shader stages, each with a different offset.
5893 // buffer offsets must be 32byte aligned - adjust buffer count accordingly
5894 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
5895 }
5896 }
5897
5898 if (buffers) {
5899 if (!d->bufferSizeBuffer)
5900 d->bufferSizeBuffer = new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers * sizeof(int));
5901
5902 d->bufferSizeBuffer->setSize(buffers * sizeof(int));
5903 d->bufferSizeBuffer->create();
5904 }
5905
5906 rhiD->pipelineCreationEnd();
5907 lastActiveFrameSlot = -1;
5908 generation += 1;
5909 rhiD->registerResource(this);
5910 return true;
5911}
5912
5918
5924
5926{
5927 d->cs.destroy();
5928
5929 if (!d->ps)
5930 return;
5931
5932 delete d->bufferSizeBuffer;
5933 d->bufferSizeBuffer = nullptr;
5934
5938 e.computePipeline.pipelineState = d->ps;
5939 d->ps = nil;
5940
5942 if (rhiD) {
5943 rhiD->d->releaseQueue.append(e);
5944 rhiD->unregisterResource(this);
5945 }
5946}
5947
5948void QRhiMetalData::trySeedingComputePipelineFromBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
5949{
5950 if (@available(macOS 11.0, iOS 14.0, *)) {
5951 if (binArch) {
5952 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5953 cpDesc.binaryArchives = binArchArray;
5954 }
5955 }
5956}
5957
5958void QRhiMetalData::addComputePipelineToBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
5959{
5960 if (@available(macOS 11.0, iOS 14.0, *)) {
5961 if (binArch) {
5962 NSError *err = nil;
5963 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
5964 const QString msg = QString::fromNSString(err.localizedDescription);
5965 qWarning("Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
5966 }
5967 }
5968 }
5969}
5970
5972{
5973 if (d->ps)
5974 destroy();
5975
5977 rhiD->pipelineCreationStart();
5978
5979 auto cacheIt = rhiD->d->shaderCache.constFind(m_shaderStage);
5980 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5981 d->cs = *cacheIt;
5982 } else {
5984 QString error;
5985 QByteArray entryPoint;
5986 QShaderKey activeKey;
5987 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
5988 &error, &entryPoint, &activeKey);
5989 if (!lib) {
5990 qWarning("MSL shader compilation failed: %s", qPrintable(error));
5991 return false;
5992 }
5993 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5994 if (!func) {
5995 qWarning("MSL function for entry point %s not found", entryPoint.constData());
5996 [lib release];
5997 return false;
5998 }
5999 d->cs.lib = lib;
6000 d->cs.func = func;
6001 d->cs.localSize = shader.description().computeShaderLocalSize();
6002 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6003 d->cs.desc = shader.description();
6004 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6005
6006 // SPIRV-Cross buffer size buffers
6009 d->cs.nativeResourceBindingMap[binding] = qMakePair(binding, -1);
6010 }
6011
6012 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6013 for (QMetalShader &s : rhiD->d->shaderCache)
6014 s.destroy();
6015 rhiD->d->shaderCache.clear();
6016 }
6017 rhiD->d->shaderCache.insert(m_shaderStage, d->cs);
6018 }
6019
6020 [d->cs.lib retain];
6021 [d->cs.func retain];
6022
6023 d->localSize = MTLSizeMake(d->cs.localSize[0], d->cs.localSize[1], d->cs.localSize[2]);
6024
6025 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor new];
6026 cpDesc.computeFunction = d->cs.func;
6027
6028 rhiD->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6029
6030 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6031 rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
6032
6033 NSError *err = nil;
6034 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6035 options: MTLPipelineOptionNone
6036 reflection: nil
6037 error: &err];
6038 [cpDesc release];
6039 if (!d->ps) {
6040 const QString msg = QString::fromNSString(err.localizedDescription);
6041 qWarning("Failed to create compute pipeline state: %s", qPrintable(msg));
6042 return false;
6043 }
6044
6045 // SPIRV-Cross buffer size buffers
6047 int buffers = 0;
6050
6051 buffers += 1;
6052
6053 if (!d->bufferSizeBuffer)
6055
6056 d->bufferSizeBuffer->setSize(buffers * sizeof(int));
6058 }
6059
6060 rhiD->pipelineCreationEnd();
6062 generation += 1;
6063 rhiD->registerResource(this);
6064 return true;
6065}
6066
6073
6075{
6076 destroy();
6077 delete d;
6078}
6079
6081{
6082 // nothing to do here, we do not own the MTL cb object
6083}
6084
6086{
6087 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6088 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6089 return &nativeHandlesStruct;
6090}
6091
6092void QMetalCommandBuffer::resetState(double lastGpuTime)
6093{
6094 d->lastGpuTime = lastGpuTime;
6098 d->currentPassRpDesc = nil;
6100}
6101
6108
6132
6134 : QRhiSwapChain(rhi),
6135 rtWrapper(rhi, this),
6136 cbWrapper(rhi),
6138{
6139 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6140 d->sem[i] = nullptr;
6141 d->msaaTex[i] = nil;
6142 }
6143}
6144
6146{
6147 destroy();
6148 delete d;
6149}
6150
6152{
6153 if (!d->layer)
6154 return;
6155
6156 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6157 if (d->sem[i]) {
6158 // the semaphores cannot be released if they do not have the initial value
6160
6161 dispatch_release(d->sem[i]);
6162 d->sem[i] = nullptr;
6163 }
6164 }
6165
6166 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6167 [d->msaaTex[i] release];
6168 d->msaaTex[i] = nil;
6169 }
6170
6171#ifdef Q_OS_MACOS
6172 d->liveResizeStartObserver.remove();
6173 d->liveResizeEndObserver.remove();
6174 d->liveResizeObserverSet = false;
6175#endif
6176
6177 d->layer = nullptr;
6178 m_proxyData = {};
6179
6181 d->curDrawable = nil;
6182
6184 if (rhiD) {
6185 rhiD->swapchains.remove(this);
6186 rhiD->unregisterResource(this);
6187 }
6188}
6189
6194
6199
6200// view.layer should ideally be called on the main thread, otherwise the UI
6201// Thread Checker in Xcode drops a warning. Hence trying to proxy it through
6202// QRhiSwapChainProxyData instead of just calling this function directly.
6203static inline CAMetalLayer *layerForWindow(QWindow *window)
6204{
6206#ifdef Q_OS_MACOS
6207 NSView *view = reinterpret_cast<NSView *>(window->winId());
6208#else
6209 UIView *view = reinterpret_cast<UIView *>(window->winId());
6210#endif
6211 Q_ASSERT(view);
6212 return static_cast<CAMetalLayer *>(view.layer);
6213}
6214
6215// If someone calls this, it is hopefully from the main thread, and they will
6216// then set the returned data on the QRhiSwapChain, so it won't need to query
6217// the layer on its own later on.
6224
6226{
6228 CAMetalLayer *layer = d->layer;
6229 if (!layer)
6230 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6231
6232 Q_ASSERT(layer);
6233 int height = (int)layer.bounds.size.height;
6234 int width = (int)layer.bounds.size.width;
6235 width *= layer.contentsScale;
6236 height *= layer.contentsScale;
6237 return QSize(width, height);
6238}
6239
6241{
6242 if (f == HDRExtendedSrgbLinear) {
6243 if (@available(macOS 10.11, iOS 16.0, *))
6244 return hdrInfo().limits.colorComponentValue.maxPotentialColorComponentValue > 1.0f;
6245 else
6246 return false;
6247 } else if (f == HDRExtendedDisplayP3Linear) {
6248 if (@available(macOS 11.0, iOS 14.0, *))
6249 return hdrInfo().limits.colorComponentValue.maxPotentialColorComponentValue > 1.0f;
6250 else
6251 return false;
6252 }
6253 return f == SDR;
6254}
6255
6257{
6259
6260 chooseFormats(); // ensure colorFormat and similar are filled out
6261
6263 rpD->colorAttachmentCount = 1;
6264 rpD->hasDepthStencil = m_depthStencil != nullptr;
6265
6266 rpD->colorFormat[0] = int(d->colorFormat);
6267
6268#ifdef Q_OS_MACOS
6269 // m_depthStencil may not be built yet so cannot rely on computed fields in it
6270 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6271 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6272#else
6273 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6274#endif
6275
6276 rpD->updateSerializedFormat();
6277
6278 rhiD->registerResource(rpD, false);
6279 return rpD;
6280}
6281
6283{
6285 samples = rhiD->effectiveSampleCount(m_sampleCount);
6286 // pick a format that is allowed for CAMetalLayer.pixelFormat
6288 d->colorFormat = MTLPixelFormatRGBA16Float;
6290 return;
6291 }
6292 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6294}
6295
6297{
6298 // wait+signal is the general pattern to ensure the commands for a
6299 // given frame slot have completed (if sem is 1, we go 0 then 1; if
6300 // sem is 0 we go -1, block, completion increments to 0, then us to 1)
6301
6302 dispatch_semaphore_t sem = d->sem[slot];
6303 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6304 dispatch_semaphore_signal(sem);
6305}
6306
6308{
6310
6311 const bool needsRegistration = !window || window != m_window;
6312
6313 if (window && window != m_window)
6314 destroy();
6315 // else no destroy(), this is intentional
6316
6318 if (needsRegistration)
6319 rhiD->swapchains.insert(this);
6320
6321 window = m_window;
6322
6324 qWarning("QMetalSwapChain only supports MetalSurface windows");
6325 return false;
6326 }
6327
6328 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6329 Q_ASSERT(d->layer);
6330
6331 chooseFormats();
6332 if (d->colorFormat != d->layer.pixelFormat)
6333 d->layer.pixelFormat = d->colorFormat;
6334
6336 if (@available(macOS 10.11, iOS 16.0, *)) {
6337 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6338 d->layer.wantsExtendedDynamicRangeContent = YES;
6339 }
6340 } else if (m_format == HDRExtendedDisplayP3Linear) {
6341 if (@available(macOS 11.0, iOS 16.0, *)) {
6342 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6343 d->layer.wantsExtendedDynamicRangeContent = YES;
6344 }
6345 }
6346
6347 if (m_flags.testFlag(UsedAsTransferSource))
6348 d->layer.framebufferOnly = NO;
6349
6350#ifdef Q_OS_MACOS
6351 if (m_flags.testFlag(NoVSync))
6352 d->layer.displaySyncEnabled = NO;
6353#endif
6354
6355 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6356 d->layer.opaque = NO;
6357 } else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6358 // The CoreAnimation compositor is said to expect premultiplied alpha,
6359 // so this is then wrong when it comes to the blending operations but
6360 // there's nothing we can do. Fortunately Qt Quick always outputs
6361 // premultiplied alpha so it is not a problem there.
6362 d->layer.opaque = NO;
6363 } else {
6364 d->layer.opaque = YES;
6365 }
6366
6367 // Now set the layer's drawableSize which will stay set to the same value
6368 // until the next createOrResize(), thus ensuring atomicity with regards to
6369 // the drawable size in frames.
6370 int width = (int)d->layer.bounds.size.width;
6371 int height = (int)d->layer.bounds.size.height;
6372 CGSize layerSize = CGSizeMake(width, height);
6373 const float scaleFactor = d->layer.contentsScale;
6374 layerSize.width *= scaleFactor;
6375 layerSize.height *= scaleFactor;
6376 d->layer.drawableSize = layerSize;
6377
6378 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6380
6381 [d->layer setDevice: rhiD->d->dev];
6382
6383#ifdef Q_OS_MACOS
6384 // Can only use presentsWithTransaction (to get smooth resizing) when
6385 // presenting from the main (gui) thread. We predict that based on the
6386 // thread this function is called on since if the QRhiSwapChain is
6387 // initialied on a given thread then that's almost certainly the thread on
6388 // which the QRhi renders and presents.
6389 const bool canUsePresentsWithTransaction = NSThread.isMainThread;
6390
6391 // Have an env.var. just in case it turns out presentsWithTransaction is
6392 // not desired in some specific case.
6393 static bool allowPresentsWithTransaction = !qEnvironmentVariableIntValue("QT_MTL_NO_TRANSACTION");
6394
6395 if (allowPresentsWithTransaction && canUsePresentsWithTransaction && !d->liveResizeObserverSet) {
6396 d->liveResizeObserverSet = true;
6397 NSView *view = reinterpret_cast<NSView *>(window->winId());
6398 NSWindow *window = view.window;
6399 if (window) {
6400 qCDebug(QRHI_LOG_INFO, "will set presentsWithTransaction during live resize");
6401 d->liveResizeStartObserver = QMacNotificationObserver(window, NSWindowWillStartLiveResizeNotification, [this] {
6402 d->layer.presentsWithTransaction = true;
6403 });
6404 d->liveResizeEndObserver = QMacNotificationObserver(window, NSWindowDidEndLiveResizeNotification, [this] {
6405 d->layer.presentsWithTransaction = false;
6406 });
6407 }
6408 }
6409#endif
6410
6412 d->curDrawable = nil;
6413
6414 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6415 d->lastGpuTime[i] = 0;
6416 if (!d->sem[i])
6417 d->sem[i] = dispatch_semaphore_create(QMTL_FRAMES_IN_FLIGHT - 1);
6418 }
6419
6420 currentFrameSlot = 0;
6421 frameCount = 0;
6422
6425 qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6427 }
6431 if (!m_depthStencil->create())
6432 qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6434 } else {
6435 qWarning("Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6438 }
6439 }
6440
6441 rtWrapper.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
6443 rtWrapper.d->dpr = scaleFactor;
6446 rtWrapper.d->dsAttCount = ds ? 1 : 0;
6447
6448 qCDebug(QRHI_LOG_INFO, "got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6449 pixelSize.width(), pixelSize.height(), scaleFactor);
6450
6451 if (samples > 1) {
6452 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6453 desc.textureType = MTLTextureType2DMultisample;
6454 desc.pixelFormat = d->colorFormat;
6455 desc.width = NSUInteger(pixelSize.width());
6456 desc.height = NSUInteger(pixelSize.height());
6457 desc.sampleCount = NSUInteger(samples);
6458 desc.resourceOptions = MTLResourceStorageModePrivate;
6459 desc.storageMode = MTLStorageModePrivate;
6460 desc.usage = MTLTextureUsageRenderTarget;
6461 for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6462 [d->msaaTex[i] release];
6463 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6464 }
6465 [desc release];
6466 }
6467
6468 if (needsRegistration)
6469 rhiD->registerResource(this);
6470
6471 return true;
6472}
6473
6475{
6478 info.limits.colorComponentValue.maxColorComponentValue = 1;
6479 info.limits.colorComponentValue.maxPotentialColorComponentValue = 1;
6480 info.luminanceBehavior = QRhiSwapChainHdrInfo::DisplayReferred; // 1.0 = SDR white
6481 info.sdrWhiteLevel = 200; // typical value, but dummy (don't know the real one); won't matter due to being display-referred
6482
6483 if (m_window) {
6484 // Must use m_window, not window, given this may be called before createOrResize().
6485#if defined(Q_OS_MACOS)
6486 NSView *view = reinterpret_cast<NSView *>(m_window->winId());
6487 NSScreen *screen = view.window.screen;
6488 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6489 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6490#elif defined(Q_OS_IOS)
6491 if (@available(iOS 16.0, *)) {
6492 UIView *view = reinterpret_cast<UIView *>(m_window->winId());
6493 UIScreen *screen = view.window.windowScene.screen;
6494 info.limits.colorComponentValue.maxColorComponentValue = view.window.windowScene.screen.currentEDRHeadroom;
6495 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.potentialEDRHeadroom;
6496 }
6497#endif
6498 }
6499
6500 return info;
6501}
6502
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:499
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
QString absoluteFilePath() const
\inmodule QtCore
Definition qfile.h:93
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:951
\inmodule QtGui
Definition qimage.h:37
qsizetype sizeInBytes() const
Definition qimage.cpp:1548
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition qimage.cpp:1222
bool contains(const Key &key) const
Definition qmap.h:341
const_iterator cend() const
Definition qmap.h:605
const_iterator constFind(const Key &key) const
Definition qmap.h:655
void clear()
Definition qmap.h:289
bool isEmpty() const
Definition qmap.h:269
T & first()
Definition qmap.h:419
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
static Q_CORE_EXPORT QOperatingSystemVersionBase current()
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr bool isNull() const noexcept
Returns true if both the x and y coordinates are set to 0, otherwise returns false.
Definition qpoint.h:125
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
quint32 size() const
Definition qrhi_p.h:357
\inmodule QtGui
Definition qrhi.h:846
UsageFlags m_usage
Definition qrhi.h:888
Type m_type
Definition qrhi.h:887
Type
Specifies storage type of buffer resource.
Definition qrhi.h:848
@ Dynamic
Definition qrhi.h:851
@ Static
Definition qrhi.h:850
@ UniformBuffer
Definition qrhi.h:857
@ StorageBuffer
Definition qrhi.h:858
void setSize(quint32 sz)
Sets the size of the buffer in bytes.
Definition qrhi.h:876
quint32 m_size
Definition qrhi.h:889
\inmodule QtGui
Definition qrhi.h:576
QRhiRenderBuffer * renderBuffer() const
Definition qrhi.h:585
QRhiTexture * texture() const
Definition qrhi.h:582
\inmodule QtGui
Definition qrhi.h:1651
void draw(quint32 vertexCount, quint32 instanceCount=1, quint32 firstVertex=0, quint32 firstInstance=0)
Records a non-indexed draw.
Definition qrhi.cpp:9688
QPair< int, quint32 > DynamicOffset
Synonym for QPair<int, quint32>.
Definition qrhi.h:1676
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1680
IndexFormat
Specifies the index data type.
Definition qrhi.h:1653
\inmodule QtGui
Definition qrhi.h:1622
QRhiShaderStage m_shaderStage
Definition qrhi.h:1644
\inmodule QtGui
Definition qrhi.h:44
\inmodule QtGui
Definition qrhi.h:1270
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1503
quint32 m_stencilReadMask
Definition qrhi.h:1491
BlendOp
Specifies the blend operation.
Definition qrhi.h:1331
void setCullMode(CullMode mode)
Sets the specified face culling mode.
Definition qrhi.h:1393
PolygonMode
Specifies the polygon rasterization mode.
Definition qrhi.h:1379
BlendFactor
Specifies the blend factor.
Definition qrhi.h:1309
StencilOpState m_stencilFront
Definition qrhi.h:1489
quint32 m_stencilWriteMask
Definition qrhi.h:1492
QRhiShaderResourceBindings * shaderResourceBindings() const
Definition qrhi.h:1461
CompareOp
Specifies the depth or stencil comparison function.
Definition qrhi.h:1350
Topology m_topology
Definition qrhi.h:1481
CullMode
Specifies the culling mode.
Definition qrhi.h:1290
QVarLengthArray< QRhiShaderStage, 4 > m_shaderStages
Definition qrhi.h:1500
QRhiRenderPassDescriptor * renderPassDescriptor() const
Definition qrhi.h:1464
QVarLengthArray< TargetBlend, 8 > m_targetBlends
Definition qrhi.h:1484
PolygonMode m_polygonMode
Definition qrhi.h:1498
float m_slopeScaledDepthBias
Definition qrhi.h:1496
Topology
Specifies the primitive topology.
Definition qrhi.h:1280
StencilOpState m_stencilBack
Definition qrhi.h:1490
FrontFace m_frontFace
Definition qrhi.h:1483
void setDepthBias(int bias)
Sets the depth bias.
Definition qrhi.h:1441
StencilOp
Specifies the stencil operation.
Definition qrhi.h:1361
CullMode m_cullMode
Definition qrhi.h:1482
CompareOp m_depthOp
Definition qrhi.h:1487
bool isCompressedFormat(QRhiTexture::Format format) const
Definition qrhi.cpp:8058
static const QRhiShaderResourceBinding::Data * shaderResourceBindingData(const QRhiShaderResourceBinding &binding)
Definition qrhi_p.h:220
quint32 pipelineCacheRhiId() const
Definition qrhi_p.h:196
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, QSize *blockDim) const
Definition qrhi.cpp:8065
static const int MAX_SHADER_CACHE_ENTRIES
Definition qrhi_p.h:239
static bool sortedBindingLessThan(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
Definition qrhi_p.h:230
qint64 totalPipelineCreationTime() const
Definition qrhi_p.h:212
void textureFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const
Definition qrhi.cpp:8185
\inmodule QtRhi
\inmodule QtRhi
struct QRhiMetal::@295 caps
static QRhiSwapChainProxyData updateSwapChainProxyData(QWindow *window)
QMetalSwapChain * currentSwapChain
bool isDeviceLost() const override
Definition qrhimetal.mm:939
QRhiMetalNativeHandles nativeHandlesStruct
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override
QRhiStats statistics() override
Definition qrhimetal.mm:918
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:704
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override
Definition qrhimetal.mm:738
void endExternal(QRhiCommandBuffer *cb) override
QRhiMetalData * d
QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice=nullptr)
Definition qrhimetal.mm:471
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:694
QRhiGraphicsPipeline * createGraphicsPipeline() override
bool create(QRhi::Flags flags) override
Definition qrhimetal.mm:548
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:714
int resourceLimit(QRhi::ResourceLimit limit) const override
Definition qrhimetal.mm:871
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:724
const QRhiNativeHandles * nativeHandles() override
Definition qrhimetal.mm:908
void executeDeferredReleases(bool forced=false)
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QRhiComputePipeline * createComputePipeline() override
bool isClipDepthZeroToOne() const override
Definition qrhimetal.mm:719
QVector< int > supportedSampleCounts
QRhiTextureRenderTarget * createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags) override
bool isYUpInFramebuffer() const override
Definition qrhimetal.mm:709
QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override
quint32 osMinor
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
void setPipelineCacheData(const QByteArray &data) override
QByteArray pipelineCacheData() override
Definition qrhimetal.mm:954
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:925
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:699
void setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override
void releaseCachedResources() override
Definition qrhimetal.mm:931
QRhiDriverInfo driverInfoStruct
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override
void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override
QRhiDriverInfo driverInfo() const override
Definition qrhimetal.mm:913
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:502
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override
bool isFeatureSupported(QRhi::Feature feature) const override
Definition qrhimetal.mm:771
void enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr, int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc, qsizetype *curOfs)
void destroy() override
Definition qrhimetal.mm:663
void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates, QRhiCommandBuffer::BeginPassFlags flags) override
\inmodule QtGui
Definition qrhi.h:777
\inmodule QtGui
Definition qrhi.h:1094
Flags flags() const
Definition qrhi.h:1121
void setPixelSize(const QSize &sz)
Sets the size (in pixels) to sz.
Definition qrhi.h:1116
QSize pixelSize() const
Definition qrhi.h:1115
int sampleCount() const
Definition qrhi.h:1118
int m_sampleCount
Definition qrhi.h:1134
QRhiTexture::Format m_backingFormatHint
Definition qrhi.h:1136
QSize m_pixelSize
Definition qrhi.h:1133
Type
Specifies the type of the renderbuffer.
Definition qrhi.h:1096
virtual bool create()=0
Creates the corresponding native graphics resources.
@ UsedWithSwapChainOnly
Definition qrhi.h:1102
\inmodule QtGui
Definition qrhi.h:1142
\inmodule QtGui
Definition qrhi.h:1158
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Sets the QRhiRenderPassDescriptor desc for use with this render target.
Definition qrhi.h:1165
virtual QSize pixelSize() const =0
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Definition qrhi_p.h:536
\inmodule QtGui
Definition qrhi.h:1731
QByteArray m_objectName
Definition qrhi.h:842
@ SwapChainRenderTarget
Definition qrhi.h:812
@ TextureRenderTarget
Definition qrhi.h:813
virtual Type resourceType() const =0
QRhiImplementation * m_rhi
Definition qrhi.h:840
\inmodule QtGui
Definition qrhi.h:1030
Filter m_minFilter
Definition qrhi.h:1085
Filter
Specifies the minification, magnification, or mipmap filtering.
Definition qrhi.h:1032
AddressMode m_addressV
Definition qrhi.h:1088
Filter m_mipmapMode
Definition qrhi.h:1086
AddressMode m_addressU
Definition qrhi.h:1087
AddressMode
Specifies the addressing mode.
Definition qrhi.h:1038
@ ClampToEdge
Definition qrhi.h:1040
CompareOp
Specifies the texture comparison function.
Definition qrhi.h:1044
@ LessOrEqual
Definition qrhi.h:1048
@ GreaterOrEqual
Definition qrhi.h:1051
CompareOp m_compareOp
Definition qrhi.h:1090
AddressMode m_addressW
Definition qrhi.h:1089
Filter m_magFilter
Definition qrhi.h:1084
\inmodule QtGui
Definition qrhi.h:138
std::array< int, 4 > scissor() const
Definition qrhi.h:143
\inmodule QtGui
Definition qrhi.h:439
static QRhiShaderResourceBinding bufferLoad(int binding, StageFlags stage, QRhiBuffer *buf)
Definition qrhi.cpp:5946
StageFlag
Flag values to indicate which stages the shader resource is visible in.
Definition qrhi.h:454
\inmodule QtGui
Definition qrhi.h:1214
QVarLengthArray< QRhiShaderResourceBinding, BINDING_PREALLOC > m_bindings
Definition qrhi.h:1246
\inmodule QtGui
Definition qrhi.h:379
QShader::Variant shaderVariant() const
Definition qrhi.h:400
QShader shader() const
Definition qrhi.h:397
@ TessellationControl
Definition qrhi.h:383
@ TessellationEvaluation
Definition qrhi.h:384
\inmodule QtGui
Definition qrhi.h:1173
\inmodule QtGui
Definition qrhi.h:1549
QWindow * m_window
Definition qrhi.h:1609
QRhiSwapChainProxyData m_proxyData
Definition qrhi.h:1616
int m_sampleCount
Definition qrhi.h:1613
@ SurfaceHasNonPreMulAlpha
Definition qrhi.h:1553
@ UsedAsTransferSource
Definition qrhi.h:1555
@ SurfaceHasPreMulAlpha
Definition qrhi.h:1552
QRhiRenderPassDescriptor * m_renderPassDesc
Definition qrhi.h:1614
QSize m_currentPixelSize
Definition qrhi.h:1615
Flags m_flags
Definition qrhi.h:1610
Format
Describes the swapchain format.
Definition qrhi.h:1561
@ HDRExtendedSrgbLinear
Definition qrhi.h:1563
@ HDRExtendedDisplayP3Linear
Definition qrhi.h:1565
Format m_format
Definition qrhi.h:1611
QRhiRenderBuffer * m_depthStencil
Definition qrhi.h:1612
const QRhiColorAttachment * cbeginColorAttachments() const
Definition qrhi.h:634
QRhiTexture * depthTexture() const
Definition qrhi.h:642
const QRhiColorAttachment * cendColorAttachments() const
Definition qrhi.h:635
QRhiRenderBuffer * depthStencilBuffer() const
Definition qrhi.h:639
const QRhiColorAttachment * colorAttachmentAt(qsizetype index) const
Definition qrhi.h:636
qsizetype colorAttachmentCount() const
Definition qrhi.h:637
QRhiTexture * depthResolveTexture() const
Definition qrhi.h:645
\inmodule QtGui
Definition qrhi.h:1184
QRhiTextureRenderTargetDescription m_desc
Definition qrhi.h:1207
\inmodule QtGui
Definition qrhi.h:895
QSize m_pixelSize
Definition qrhi.h:1016
int m_arraySize
Definition qrhi.h:1018
int m_depth
Definition qrhi.h:1017
int arraySize() const
Definition qrhi.h:981
@ ThreeDimensional
Definition qrhi.h:907
@ UsedWithLoadStore
Definition qrhi.h:904
@ MipMapped
Definition qrhi.h:900
@ RenderTarget
Definition qrhi.h:898
@ OneDimensional
Definition qrhi.h:910
@ TextureArray
Definition qrhi.h:909
@ CubeMap
Definition qrhi.h:899
Format
Specifies the texture format.
Definition qrhi.h:914
@ ASTC_10x8
Definition qrhi.h:959
@ ASTC_12x12
Definition qrhi.h:962
@ ASTC_8x5
Definition qrhi.h:954
@ ASTC_10x5
Definition qrhi.h:957
@ RGBA32F
Definition qrhi.h:926
@ ETC2_RGBA8
Definition qrhi.h:947
@ ASTC_5x5
Definition qrhi.h:951
@ ASTC_4x4
Definition qrhi.h:949
@ ASTC_6x6
Definition qrhi.h:953
@ ASTC_12x10
Definition qrhi.h:961
@ ETC2_RGB8
Definition qrhi.h:945
@ ASTC_5x4
Definition qrhi.h:950
@ RED_OR_ALPHA8
Definition qrhi.h:923
@ ASTC_6x5
Definition qrhi.h:952
@ ASTC_8x8
Definition qrhi.h:956
@ RGBA16F
Definition qrhi.h:925
@ RGB10A2
Definition qrhi.h:930
@ ASTC_10x6
Definition qrhi.h:958
@ ASTC_10x10
Definition qrhi.h:960
@ UnknownFormat
Definition qrhi.h:915
@ ETC2_RGB8A1
Definition qrhi.h:946
@ ASTC_8x6
Definition qrhi.h:955
Format m_format
Definition qrhi.h:1015
Flags m_flags
Definition qrhi.h:1020
int m_sampleCount
Definition qrhi.h:1019
Format
Specifies the type of the element data.
Definition qrhi.h:234
\inmodule QtGui
Definition qrhi.h:321
const QRhiVertexInputAttribute * cendAttributes() const
Definition qrhi.h:345
const QRhiVertexInputBinding * cendBindings() const
Definition qrhi.h:333
const QRhiVertexInputAttribute * cbeginAttributes() const
Definition qrhi.h:344
const QRhiVertexInputBinding * cbeginBindings() const
Definition qrhi.h:332
\inmodule QtGui
Definition qrhi.h:85
static constexpr int MAX_MIP_LEVELS
Definition qrhi.h:1997
@ Metal
Definition qrhi.h:1811
ResourceLimit
Describes the resource limit to query.
Definition qrhi.h:1886
@ MaxThreadsPerThreadGroup
Definition qrhi.h:1893
@ MaxThreadGroupZ
Definition qrhi.h:1896
@ FramesInFlight
Definition qrhi.h:1890
@ TextureSizeMin
Definition qrhi.h:1887
@ MaxThreadGroupsPerDimension
Definition qrhi.h:1892
@ MaxAsyncReadbackFrames
Definition qrhi.h:1891
@ TextureArraySizeMax
Definition qrhi.h:1897
@ MaxColorAttachments
Definition qrhi.h:1889
@ MaxThreadGroupY
Definition qrhi.h:1895
@ MaxVertexInputs
Definition qrhi.h:1899
@ MaxThreadGroupX
Definition qrhi.h:1894
@ TextureSizeMax
Definition qrhi.h:1888
@ MaxVertexOutputs
Definition qrhi.h:1900
@ MaxUniformBufferRange
Definition qrhi.h:1898
@ SkipPresent
Definition qrhi.h:1882
Feature
Flag values to indicate what features are supported by the backend currently in use.
Definition qrhi.h:1831
@ HalfAttributes
Definition qrhi.h:1869
@ CustomInstanceStepRate
Definition qrhi.h:1837
@ NonDynamicUniformBuffers
Definition qrhi.h:1839
@ ElementIndexUint
Definition qrhi.h:1843
@ RenderToNonBaseMipLevel
Definition qrhi.h:1853
@ MultisampleRenderBuffer
Definition qrhi.h:1833
@ RenderTo3DTextureSlice
Definition qrhi.h:1861
@ Tessellation
Definition qrhi.h:1863
@ IntAttributes
Definition qrhi.h:1854
@ TextureArrays
Definition qrhi.h:1862
@ PipelineCacheDataLoadSave
Definition qrhi.h:1857
@ ReadBackNonUniformBuffer
Definition qrhi.h:1850
@ MultiView
Definition qrhi.h:1872
@ TexelFetch
Definition qrhi.h:1852
@ TextureArrayRange
Definition qrhi.h:1865
@ RenderToOneDimensionalTexture
Definition qrhi.h:1870
@ BaseVertex
Definition qrhi.h:1847
@ GeometryShader
Definition qrhi.h:1864
@ Compute
Definition qrhi.h:1844
@ OneDimensionalTextureMipmaps
Definition qrhi.h:1868
@ WideLines
Definition qrhi.h:1845
@ TriangleFanTopology
Definition qrhi.h:1849
@ OneDimensionalTextures
Definition qrhi.h:1867
@ ImageDataStride
Definition qrhi.h:1858
@ TextureViewFormat
Definition qrhi.h:1873
@ BaseInstance
Definition qrhi.h:1848
@ DebugMarkers
Definition qrhi.h:1834
@ ReadBackNonBaseMipLevel
Definition qrhi.h:1851
@ MultisampleTexture
Definition qrhi.h:1832
@ ThreeDimensionalTextureMipmaps
Definition qrhi.h:1871
@ NonFourAlignedEffectiveIndexBufferOffset
Definition qrhi.h:1840
@ RedOrAlpha8IsRed
Definition qrhi.h:1842
@ NonFillPolygonMode
Definition qrhi.h:1866
@ Timestamps
Definition qrhi.h:1835
@ ThreeDimensionalTextures
Definition qrhi.h:1860
@ PrimitiveRestart
Definition qrhi.h:1838
@ ReadBackAnyTextureFormat
Definition qrhi.h:1856
@ RenderBufferImport
Definition qrhi.h:1859
@ ScreenSpaceDerivatives
Definition qrhi.h:1855
@ VertexShaderPointSize
Definition qrhi.h:1846
@ NPOTTextureRepeat
Definition qrhi.h:1841
@ Instancing
Definition qrhi.h:1836
@ ResolveDepthStencil
Definition qrhi.h:1874
FrameOpResult
Describes the result of operations that can have a soft failure.
Definition qrhi.h:1824
@ FrameOpSuccess
Definition qrhi.h:1825
@ EnablePipelineCacheDataSave
Definition qrhi.h:1818
\inmodule QtGui
Definition qshader.h:60
QByteArray shader() const
Definition qshader.h:65
TessellationWindingOrder
\value UnknownTessellationWindingOrder \value CwTessellationWindingOrder \value CcwTessellationWindin...
QList< StorageBlock > storageBlocks() const
TessellationPartitioning
\value UnknownTessellationPartitioning \value EqualTessellationPartitioning \value FractionalEvenTess...
\inmodule QtGui
Definition qshader.h:178
\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:395
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
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
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:124
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toUtf8() const &
Definition qstring.h:634
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
@ MetalSurface
Definition qsurface.h:36
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3368
bool isEmpty() const
const_iterator cbegin() const noexcept
qsizetype count() const
const_iterator cend() const noexcept
iterator end() noexcept
iterator begin() noexcept
\inmodule QtGui
Definition qwindow.h:63
SurfaceType surfaceType() const override
Returns the surface type of the window.
Definition qwindow.cpp:665
#define this
Definition dialogs.cpp:9
QString str
[2]
QMap< QString, QString > map
[6]
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
uint alignment
Combined button and popup list for selecting options.
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:108
unsigned long NSUInteger
#define Q_FALLTHROUGH()
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:333
Flags
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
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
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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
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
GLdouble s
[6]
Definition qopenglext.h:235
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]
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
QT_BEGIN_NAMESPACE 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)
Int aligned(Int v, Int byteAlign)
Definition qrhimetal.mm:497
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:1531
#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)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
@ Q_RELOCATABLE_TYPE
Definition qtypeinfo.h:158
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:180
unsigned int quint32
Definition qtypes.h:50
int qint32
Definition qtypes.h:49
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
QVideoFrameFormat::PixelFormat fmt
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]
QHostInfo info
[0]
QQuickView * view
[0]
QJSValueList args
QVarLengthArray< BufferUpdate, 16 > pendingUpdates[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:279
id< MTLBuffer > buf[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:274
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:335
QRhiBatchedBindings< NSUInteger > currentVertexInputOffsets
Definition qrhimetal.mm:343
QMetalShaderResourceBindingsData currentShaderResourceBindingState
Definition qrhimetal.mm:345
id< MTLComputeCommandEncoder > tessellationComputeEncoder
Definition qrhimetal.mm:339
id< MTLRenderCommandEncoder > currentRenderPassEncoder
Definition qrhimetal.mm:337
QRhiBatchedBindings< id< MTLBuffer > > currentVertexInputsBuffers
Definition qrhimetal.mm:342
MTLRenderPassDescriptor * currentPassRpDesc
Definition qrhimetal.mm:340
id< MTLDepthStencilState > currentDepthStencilState
Definition qrhimetal.mm:344
id< MTLComputeCommandEncoder > currentComputePassEncoder
Definition qrhimetal.mm:338
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:451
id< MTLComputePipelineState > ps
Definition qrhimetal.mm:446
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:401
QMetalBuffer * acquireWorkBuffer(QRhiMetal *rhiD, quint32 size, WorkBufType type=WorkBufType::DeviceLocal)
QVector< QMetalBuffer * > deviceLocalWorkBuffers
Definition qrhimetal.mm:400
quint32 tescCompOutputBufferSize(quint32 patchCount) const
Definition qrhimetal.mm:419
quint32 tescCompPatchOutputBufferSize(quint32 patchCount) const
Definition qrhimetal.mm:423
static int vsCompVariantToIndex(QShader::Variant vertexCompVariant)
id< MTLComputePipelineState > tessControlComputeState
Definition qrhimetal.mm:411
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:428
quint32 vsCompOutputBufferSize(quint32 vertexOrIndexCount, quint32 instanceCount) const
Definition qrhimetal.mm:414
std::array< id< MTLComputePipelineState >, 3 > vertexComputeState
Definition qrhimetal.mm:410
QMetalBuffer * bufferSizeBuffer
Definition qrhimetal.mm:441
MTLPrimitiveType primitiveType
Definition qrhimetal.mm:386
id< MTLDepthStencilState > ds
Definition qrhimetal.mm:385
id< MTLRenderPipelineState > ps
Definition qrhimetal.mm:384
void setupVertexInputDescriptor(MTLVertexDescriptor *desc)
void setupStageInputDescriptor(MTLStageInputOutputDescriptor *desc)
struct QMetalGraphicsPipelineData::ExtraBufferManager extraBufMgr
MTLTriangleFillMode triangleFillMode
Definition qrhimetal.mm:389
QMetalGraphicsPipeline * q
Definition qrhimetal.mm:383
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:284
id< MTLTexture > tex
Definition qrhimetal.mm:285
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
static const int MAX_COLOR_ATTACHMENTS
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() const override
struct QMetalRenderTargetData::@363 fb
ColorAtt colorAtt[QMetalRenderPassDescriptor::MAX_COLOR_ATTACHMENTS]
Definition qrhimetal.mm:369
id< MTLTexture > dsResolveTex
Definition qrhimetal.mm:371
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList
Definition qrhimetal.mm:378
id< MTLTexture > dsTex
Definition qrhimetal.mm:370
id< MTLSamplerState > samplerState
Definition qrhimetal.mm:304
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:327
QRhiBatchedBindings< id< MTLSamplerState > > samplerBatches
Definition qrhimetal.mm:328
QVarLengthArray< Buffer, 8 > buffers
Definition qrhimetal.mm:322
QRhiBatchedBindings< NSUInteger > bufferOffsetBatches
Definition qrhimetal.mm:326
QVarLengthArray< Texture, 8 > textures
Definition qrhimetal.mm:323
QRhiBatchedBindings< id< MTLBuffer > > bufferBatches
Definition qrhimetal.mm:325
QVarLengthArray< Sampler, 8 > samplers
Definition qrhimetal.mm:324
struct QMetalShaderResourceBindingsData::Stage res[QRhiMetal::SUPPORTED_STAGES]
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:146
void destroy()
Definition qrhimetal.mm:155
uint outputVertexCount
Definition qrhimetal.mm:150
QShader::NativeShaderInfo nativeShaderInfo
Definition qrhimetal.mm:153
std::array< uint, 3 > localSize
Definition qrhimetal.mm:149
id< MTLFunction > func
Definition qrhimetal.mm:148
QShader::NativeResourceBindingMap nativeResourceBindingMap
Definition qrhimetal.mm:152
id< MTLLibrary > lib
Definition qrhimetal.mm:147
QShaderDescription desc
Definition qrhimetal.mm:151
double lastGpuTime[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:459
dispatch_semaphore_t sem[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:458
id< CAMetalDrawable > curDrawable
Definition qrhimetal.mm:457
MTLPixelFormat colorFormat
Definition qrhimetal.mm:463
CAMetalLayer * layer
Definition qrhimetal.mm:456
MTLRenderPassDescriptor * rp
Definition qrhimetal.mm:460
id< MTLTexture > msaaTex[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:461
QRhiTexture::Format rhiColorFormat
Definition qrhimetal.mm:462
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::limitsType
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:292
id< MTLTexture > viewForLevel(int level)
id< MTLTexture > tex
Definition qrhimetal.mm:294
QMetalTextureData(QMetalTexture *t)
Definition qrhimetal.mm:290
id< MTLBuffer > stagingBuf[QMTL_FRAMES_IN_FLIGHT]
Definition qrhimetal.mm:295
MTLPixelFormat format
Definition qrhimetal.mm:293
id< MTLTexture > perLevelViews[QRhi::MAX_MIP_LEVELS]
Definition qrhimetal.mm:297
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< Batch, 4 > batches
Definition qrhi_p.h:582
\inmodule QtGui
Definition qrhi.h:862
\inmodule QtGui
Definition qrhi.h:1759
QByteArray deviceName
Definition qrhi.h:1769
@ IntegratedDevice
Definition qrhi.h:1762
DeviceType deviceType
Definition qrhi.h:1772
QRhiReadbackResult * result
Definition qrhimetal.mm:251
id< MTLComputePipelineState > pipelineState
Definition qrhimetal.mm:224
struct QRhiMetalData::DeferredReleaseEntry::@353::@361 computePipeline
struct QRhiMetalData::DeferredReleaseEntry::@353::@356 renderbuffer
struct QRhiMetalData::DeferredReleaseEntry::@353::@359 stagingBuffer
id< MTLDepthStencilState > depthStencilState
Definition qrhimetal.mm:219
struct QRhiMetalData::DeferredReleaseEntry::@353::@360 graphicsPipeline
std::array< id< MTLComputePipelineState >, 3 > tessVertexComputeState
Definition qrhimetal.mm:220
id< MTLSamplerState > samplerState
Definition qrhimetal.mm:212
struct QRhiMetalData::DeferredReleaseEntry::@353::@355 buffer
id< MTLComputePipelineState > tessTessControlComputeState
Definition qrhimetal.mm:221
struct QRhiMetalData::DeferredReleaseEntry::@353::@358 sampler
id< MTLRenderPipelineState > pipelineState
Definition qrhimetal.mm:218
QMetalCommandBuffer cbWrapper
Definition qrhimetal.mm:234
OffscreenFrame(QRhiImplementation *rhi)
Definition qrhimetal.mm:231
QRhiReadbackResult * result
Definition qrhimetal.mm:240
QRhiReadbackDescription desc
Definition qrhimetal.mm:239
QRhiTexture::Format format
Definition qrhimetal.mm:244
void trySeedingRenderPipelineFromBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
QRhiMetalData(QRhiMetal *rhi)
Definition qrhimetal.mm:166
QVarLengthArray< BufferReadback, 2 > activeBufferReadbacks
Definition qrhimetal.mm:257
bool setupBinaryArchive(NSURL *sourceFileUrl=nil)
Definition qrhimetal.mm:525
id< MTLCommandQueue > cmdQueue
Definition qrhimetal.mm:170
void addRenderPipelineToBinaryArchive(MTLRenderPipelineDescriptor *rpDesc)
static const int TEXBUF_ALIGN
Definition qrhimetal.mm:262
id< MTLDevice > dev
Definition qrhimetal.mm:169
void trySeedingComputePipelineFromBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
QVarLengthArray< TextureReadback, 2 > activeTextureReadbacks
Definition qrhimetal.mm:246
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:260
struct QRhiMetalData::OffscreenFrame ofr
QRhiMetal * q
Definition qrhimetal.mm:168
QHash< QRhiShaderStage, QMetalShader > shaderCache
Definition qrhimetal.mm:264
API_AVAILABLE(macosx(11.0), ios(14.0)) id< MTLBinaryArchive > binArch
MTLCaptureManager * captureMgr
Definition qrhimetal.mm:259
id< MTLCommandBuffer > newCommandBuffer()
Definition qrhimetal.mm:513
void addComputePipelineToBinaryArchive(MTLComputePipelineDescriptor *cpDesc)
QVector< DeferredReleaseEntry > releaseQueue
Definition qrhimetal.mm:228
QMetalCommandBuffer * cbD
\variable QRhiReadbackResult::completed
Definition qrhi.h:800
\inmodule QtGui
Definition qrhi.h:1723
\inmodule QtGui
Definition qrhi.h:1782
qint64 totalPipelineCreationTime
Definition qrhi.h:1783
\inmodule QtGui
Definition qrhi.h:1511
\inmodule QtGui
Definition qrhi.h:1544
\inmodule QtGui
Definition qrhi.h:965
\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:159
QMap< int, int > extraBufferBindings
Definition qshader.h:161
Definition moc.h:23