8#include <QGuiApplication>
12#include <QTemporaryFile>
15#include <QtCore/qendian.h>
16#include <QOperatingSystemVersion>
18#include <QtCore/private/qcore_mac_p.h>
19#include <QtGui/private/qmetallayer_p.h>
20#include <QtGui/qpa/qplatformwindow_p.h>
23#include <AppKit/AppKit.h>
25#include <UIKit/UIKit.h>
28#include <QuartzCore/CATransaction.h>
30#include <Metal/Metal.h>
37
38
39
40
41
42
43
44
45
48#error ARC not supported
57#define QRHI_METAL_DISABLE_BINARY_ARCHIVE
62#define QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
105
106
107
108
109
110
111
112
115
116
117
118
121
122
123
124
125
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
149
150
153
154
169 nativeResourceBindingMap.clear();
174 [argumentEncoder release];
175 argumentEncoder = nil;
191 const QColor &colorClearValue,
192 const QRhiDepthStencilClearValue &depthStencilClearValue,
194 QRhiShadingRateMap *shadingRateMap);
196 bool preferArgumentBuffers,
197 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
221 id<MTLBuffer> buffers[QMTL_FRAMES_IN_FLIGHT];
224 id<MTLTexture> texture;
227 id<MTLTexture> texture;
228 id<MTLBuffer> stagingBuffers[QMTL_FRAMES_IN_FLIGHT];
229 id<MTLTexture> views[QRhi::MAX_MIP_LEVELS];
230 id<MTLTexture> samplingView;
231 id<MTLTexture> writeView;
234 id<MTLSamplerState> samplerState;
237 id<MTLBuffer> buffer;
240 id<MTLRenderPipelineState> pipelineState;
241 id<MTLDepthStencilState> depthStencilState;
242 std::array<id<MTLComputePipelineState>, 3> tessVertexComputeState;
243 id<MTLComputePipelineState> tessTessControlComputeState;
246 id<MTLComputePipelineState> pipelineState;
249 id<MTLRasterizationRateMap> rateMap;
252 id<MTLIndirectCommandBuffer> icb;
253 id<MTLBuffer> argBuffer;
332 int frameSlot, quint32 minBlockSize, quint32 *offset);
479 id<MTLTexture> dsTex = nil;
480 id<MTLTexture> dsResolveTex = nil;
481 bool hasStencil =
false;
482 bool depthNeedsStore =
false;
483 bool preserveColor =
false;
484 bool preserveDs =
false;
528 return vertexOrIndexCount * instanceCount *
sizeof(
float) * 60;
537 return patchCount *
sizeof(
float) * 128;
585 if (importDevice->dev) {
586 d->dev = (id<MTLDevice>) importDevice->dev;
588 if (importedCmdQueue)
589 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
591 qWarning(
"No MTLDevice given, cannot import");
605 return (v + byteAlign - 1) & ~(byteAlign - 1);
610 QMacAutoReleasePool pool;
613 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
627 return [cmdQueue commandBufferWithUnretainedReferences];
629 return [cmdQueue commandBuffer];
640 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor
new];
641 binArchDesc.url = sourceFileUrl;
643 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
644 [binArchDesc release];
646 const QString msg = QString::fromNSString(err.localizedDescription);
647 qWarning(
"newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
660 d->dev = MTLCreateSystemDefaultDevice();
663 qWarning(
"No MTLDevice");
667 const QString deviceName = QString::fromNSString([d->dev name]);
668 qCDebug(QRHI_LOG_INFO,
"Metal device: %s", qPrintable(deviceName));
669 driverInfoStruct.deviceName = deviceName.toUtf8();
676 const MTLDeviceLocation deviceLocation = [d->dev location];
677 switch (deviceLocation) {
678 case MTLDeviceLocationBuiltIn:
679 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
681 case MTLDeviceLocationSlot:
682 driverInfoStruct.deviceType = QRhiDriverInfo::DiscreteDevice;
684 case MTLDeviceLocationExternal:
685 driverInfoStruct.deviceType = QRhiDriverInfo::ExternalDevice;
691 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
694 const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
695 osMajor = ver.majorVersion();
696 osMinor = ver.minorVersion();
699 [d->cmdQueue retain];
701 d->cmdQueue = [d->dev newCommandQueue];
703 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
707 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
708 const QString label = QString::asprintf(
"Qt capture scope for QRhi %p",
this);
709 d->captureScope.label = label.toNSString();
711#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
712 caps.maxTextureSize = 16384;
713 caps.baseVertexAndInstance =
true;
714 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
715 caps.maxThreadGroupSize = 1024;
716 caps.multiView =
true;
717#elif defined(Q_OS_TVOS)
718 if ([d->dev supportsFamily:MTLGPUFamilyApple3])
719 caps.maxTextureSize = 16384;
721 caps.maxTextureSize = 8192;
722 caps.baseVertexAndInstance =
false;
723 caps.isAppleGPU =
true;
724#elif defined(Q_OS_IOS)
725 if ([d->dev supportsFamily:MTLGPUFamilyApple3]) {
726 caps.maxTextureSize = 16384;
727 caps.baseVertexAndInstance =
true;
728 }
else if ([d->dev supportsFamily:MTLGPUFamilyApple2]) {
729 caps.maxTextureSize = 8192;
730 caps.baseVertexAndInstance =
false;
732 caps.maxTextureSize = 4096;
733 caps.baseVertexAndInstance =
false;
735 caps.isAppleGPU =
true;
736 if ([d->dev supportsFamily:MTLGPUFamilyApple4])
737 caps.maxThreadGroupSize = 1024;
738 if ([d->dev supportsFamily:MTLGPUFamilyApple5])
739 caps.multiView =
true;
745 caps.usePrivateStaticBuffers = caps.isAppleGPU;
746 if (qEnvironmentVariableIntValue(
"QT_METAL_NO_PRIVATE_STATIC_BUFFERS"))
747 caps.usePrivateStaticBuffers =
false;
749 caps.supportedSampleCounts = { 1 };
750 for (
int sampleCount : { 2, 4, 8 }) {
751 if ([d->dev supportsTextureSampleCount: sampleCount])
752 caps.supportedSampleCounts.append(sampleCount);
755 caps.indirectCommandBuffers = ([d->dev supportsFamily:MTLGPUFamilyApple5]
756 || [d->dev supportsFamily:MTLGPUFamilyMac2])
757 && [d->dev supportsFamily:MTLGPUFamilyMetal3];
759 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
760 if (caps.shadingRateMap && caps.multiView)
761 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
764 caps.depthClamp = [d->dev supportsFamily:MTLGPUFamilyApple3];
766#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
767 caps.fragmentStoresAndAtomics =
true;
769 caps.fragmentStoresAndAtomics = [d->dev supportsFamily:MTLGPUFamilyApple3];
772 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
773 d->setupBinaryArchive();
775 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
776 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
786 for (QMetalShader &s : d->shaderCache)
788 d->shaderCache.clear();
790 [d->captureScope release];
791 d->captureScope = nil;
793 for (
auto *pools : { d->argBufPool, d->bufStagingPool }) {
794 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
795 [pools[i].buf release];
797 pools[i].capacity = 0;
802 [d->icbArgumentBuffer release];
803 d->icbArgumentBuffer = nil;
805 [d->icbRangeBuffer release];
806 d->icbRangeBuffer = nil;
808 [d->icbNoCountBuffer release];
809 d->icbNoCountBuffer = nil;
811 [d->icbEncodeFunction release];
812 d->icbEncodeFunction = nil;
814 [d->icbEncodeFunctionU32 release];
815 d->icbEncodeFunctionU32 = nil;
817 [d->icbEncodeFunctionU16 release];
818 d->icbEncodeFunctionU16 = nil;
820 [d->icbEncodePipeline release];
821 d->icbEncodePipeline = nil;
823 [d->icbEncodePipelineU32 release];
824 d->icbEncodePipelineU32 = nil;
826 [d->icbEncodePipelineU16 release];
827 d->icbEncodePipelineU16 = nil;
835 [d->binArch release];
838 [d->cmdQueue release];
849 return caps.supportedSampleCounts;
854 Q_UNUSED(sampleCount);
855 return { QSize(1, 1) };
860 return new QMetalSwapChain(
this);
863QRhiBuffer *
QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
865 return new QMetalBuffer(
this, type, usage, size);
892 static constexpr QMatrix4x4 m(1.0f, 0.0f, 0.0f, 0.0f,
893 0.0f, 1.0f, 0.0f, 0.0f,
894 0.0f, 0.0f, 0.5f, 0.5f,
895 0.0f, 0.0f, 0.0f, 1.0f);
903 bool supportsFamilyMac2 =
false;
904 bool supportsFamilyApple3 =
false;
907 supportsFamilyMac2 =
true;
909 supportsFamilyApple3 =
true;
911 supportsFamilyApple3 =
true;
915 if (format == QRhiTexture::BC5)
918 if (!supportsFamilyApple3) {
919 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
921 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
925 if (!supportsFamilyMac2)
926 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
935 case QRhi::MultisampleTexture:
937 case QRhi::MultisampleRenderBuffer:
939 case QRhi::DebugMarkers:
941 case QRhi::Timestamps:
943 case QRhi::Instancing:
945 case QRhi::CustomInstanceStepRate:
947 case QRhi::PrimitiveRestart:
949 case QRhi::NonDynamicUniformBuffers:
951 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
953 case QRhi::NPOTTextureRepeat:
955 case QRhi::RedOrAlpha8IsRed:
957 case QRhi::ElementIndexUint:
961 case QRhi::FragmentStoresAndAtomics:
962 return caps.fragmentStoresAndAtomics;
963 case QRhi::WideLines:
965 case QRhi::VertexShaderPointSize:
967 case QRhi::BaseVertex:
968 return caps.baseVertexAndInstance;
969 case QRhi::BaseInstance:
970 return caps.baseVertexAndInstance;
971 case QRhi::TriangleFanTopology:
973 case QRhi::ReadBackNonUniformBuffer:
975 case QRhi::ReadBackNonBaseMipLevel:
977 case QRhi::TexelFetch:
979 case QRhi::RenderToNonBaseMipLevel:
981 case QRhi::IntAttributes:
983 case QRhi::ScreenSpaceDerivatives:
985 case QRhi::ReadBackAnyTextureFormat:
987 case QRhi::PipelineCacheDataLoadSave:
993 case QRhi::ImageDataStride:
995 case QRhi::RenderBufferImport:
997 case QRhi::ThreeDimensionalTextures:
999 case QRhi::RenderTo3DTextureSlice:
1001 case QRhi::TextureArrays:
1003 case QRhi::Tessellation:
1005 case QRhi::GeometryShader:
1007 case QRhi::TextureArrayRange:
1009 case QRhi::NonFillPolygonMode:
1011 case QRhi::OneDimensionalTextures:
1013 case QRhi::OneDimensionalTextureMipmaps:
1015 case QRhi::HalfAttributes:
1017 case QRhi::RenderToOneDimensionalTexture:
1019 case QRhi::ThreeDimensionalTextureMipmaps:
1021 case QRhi::MultiView:
1022 return caps.multiView;
1023 case QRhi::TextureViewFormat:
1025 case QRhi::ResolveDepthStencil:
1027 case QRhi::VariableRateShading:
1029 case QRhi::VariableRateShadingMap:
1030 return caps.shadingRateMap;
1031 case QRhi::VariableRateShadingMapWithTexture:
1033 case QRhi::PerRenderTargetBlending:
1034 case QRhi::SampleVariables:
1036 case QRhi::InstanceIndexIncludesBaseInstance:
1038 case QRhi::DepthClamp:
1039 return caps.depthClamp;
1040 case QRhi::DrawIndirect:
1042 case QRhi::DrawIndirectMulti:
1043 return caps.indirectCommandBuffers;
1044 case QRhi::ShaderDrawParameters:
1046 case QRhi::PushConstants:
1048 case QRhi::DrawIndirectCount:
1049 return caps.indirectCommandBuffers;
1050 case QRhi::DispatchIndirect:
1052 case QRhi::BufferToBufferCopy:
1054 case QRhi::StaticBuffersOnGpuTimeline:
1055 return caps.usePrivateStaticBuffers;
1065 case QRhi::TextureSizeMin:
1067 case QRhi::TextureSizeMax:
1068 return caps.maxTextureSize;
1069 case QRhi::MaxColorAttachments:
1071 case QRhi::FramesInFlight:
1072 return QMTL_FRAMES_IN_FLIGHT;
1073 case QRhi::MaxAsyncReadbackFrames:
1074 return QMTL_FRAMES_IN_FLIGHT;
1075 case QRhi::MaxThreadGroupsPerDimension:
1077 case QRhi::MaxThreadsPerThreadGroup:
1079 case QRhi::MaxThreadGroupX:
1081 case QRhi::MaxThreadGroupY:
1083 case QRhi::MaxThreadGroupZ:
1084 return caps.maxThreadGroupSize;
1085 case QRhi::TextureArraySizeMax:
1087 case QRhi::MaxUniformBufferRange:
1089 case QRhi::MaxVertexInputs:
1091 case QRhi::MaxVertexOutputs:
1093 case QRhi::MaxPushConstantsSize:
1095 case QRhi::MaxVertexStorageBuffers:
1096 case QRhi::MaxFragmentStorageBuffers:
1098 case QRhi::ShadingRateImageTileSize:
1108 return &nativeHandlesStruct;
1113 return driverInfoStruct;
1119 result.totalPipelineCreationTime = totalPipelineCreationTime();
1136 for (QMetalShader &s : d->shaderCache)
1139 d->shaderCache.clear();
1161 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
1166 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1171 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1172 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1174 if (![d->binArch serializeToURL: url error: &err]) {
1175 const QString msg = QString::fromNSString(err.localizedDescription);
1177 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
1182 if (!f.open(QIODevice::ReadOnly)) {
1183 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1186 const QByteArray blob = f.readAll();
1190 const quint32 dataSize = quint32(blob.size());
1192 data.resize(headerSize + dataSize);
1195 header.rhiId = pipelineCacheRhiId();
1196 header.arch = quint32(
sizeof(
void*));
1197 header.dataSize = quint32(dataSize);
1198 header.osMajor = osMajor;
1199 header.osMinor = osMinor;
1200 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1202 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1203 header.driver[driverStrLen] =
'\0';
1205 memcpy(data.data(), &header, headerSize);
1206 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1216 if (data.size() < qsizetype(headerSize)) {
1217 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1221 const size_t dataOffset = headerSize;
1223 memcpy(&header, data.constData(), headerSize);
1225 const quint32 rhiId = pipelineCacheRhiId();
1226 if (header.rhiId != rhiId) {
1227 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1228 rhiId, header.rhiId);
1232 const quint32 arch = quint32(
sizeof(
void*));
1233 if (header.arch != arch) {
1234 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1239 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1240 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1241 osMajor, osMinor, header.osMajor, header.osMinor);
1245 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1246 if (strncmp(header
.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1247 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1251 if (quint64(data.size()) < quint64(dataOffset) + header.dataSize) {
1252 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1256 const char *p = data.constData() + dataOffset;
1260 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1263 tmp.write(p, header.dataSize);
1266 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1267 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1268 if (
d->setupBinaryArchive(url))
1269 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1272QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1273 int sampleCount, QRhiRenderBuffer::Flags flags,
1274 QRhiTexture::Format backingFormatHint)
1276 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1280 const QSize &pixelSize,
int depth,
int arraySize,
1281 int sampleCount, QRhiTexture::Flags flags)
1283 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1287 QRhiSampler::Filter mipmapMode,
1288 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1290 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1295 return new QMetalShadingRateMap(
this);
1299 QRhiTextureRenderTarget::Flags flags)
1306 return new QMetalGraphicsPipeline(
this);
1311 return new QMetalComputePipeline(
this);
1316 return new QMetalShaderResourceBindings(
this);
1327 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1330 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1331 if (!map || map->isEmpty())
1334 auto it = map->constFind(binding);
1335 if (it != map->cend())
1347 case QRhiShaderResourceBinding::ImageLoad:
1348 return MTLResourceUsageRead;
1349 case QRhiShaderResourceBinding::ImageStore:
1350 return MTLResourceUsageWrite;
1352 return MTLResourceUsageRead | MTLResourceUsageWrite;
1360 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : res.textures) {
1361 switch (encoderStage) {
1362 case QMetalShaderResourceBindingsData::VERTEX:
1363 [cbD->d->currentRenderPassEncoder useResource: t.mtltex usage: t.usage stages: MTLRenderStageVertex];
1365 case QMetalShaderResourceBindingsData::FRAGMENT:
1366 [cbD->d->currentRenderPassEncoder useResource: t.mtltex usage: t.usage stages: MTLRenderStageFragment];
1368 case QMetalShaderResourceBindingsData::COMPUTE:
1369 [cbD->d->currentComputePassEncoder useResource: t.mtltex usage: t.usage];
1379 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1380 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1383 case QMetalShaderResourceBindingsData::VERTEX:
1384 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1385 offsets: offsetBatch.resources.constData()
1386 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1388 case QMetalShaderResourceBindingsData::FRAGMENT:
1389 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1390 offsets: offsetBatch.resources.constData()
1391 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1393 case QMetalShaderResourceBindingsData::COMPUTE:
1394 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1395 offsets: offsetBatch.resources.constData()
1396 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1410 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1413 case QMetalShaderResourceBindingsData::VERTEX:
1414 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1415 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1417 case QMetalShaderResourceBindingsData::FRAGMENT:
1418 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1419 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1421 case QMetalShaderResourceBindingsData::COMPUTE:
1422 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1423 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1437 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1439 switch (encoderStage) {
1440 case QMetalShaderResourceBindingsData::VERTEX:
1441 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1442 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1444 case QMetalShaderResourceBindingsData::FRAGMENT:
1445 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1446 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1448 case QMetalShaderResourceBindingsData::COMPUTE:
1449 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1450 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1472 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1473 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1474 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1475 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1478 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1479 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1480 bindStageTextures(cbD, encoderStage, batch);
1483 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1484 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1485 bindStageSamplers(cbD, encoderStage, batch);
1488 if (bindingData->res[resourceStage].usesArgumentBuffer)
1489 declareStageArgumentBufferResources(cbD, encoderStage, bindingData->res[resourceStage]);
1495 case QMetalShaderResourceBindingsData::VERTEX:
1496 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1497 case QMetalShaderResourceBindingsData::TESSCTRL:
1498 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1499 case QMetalShaderResourceBindingsData::TESSEVAL:
1500 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1501 case QMetalShaderResourceBindingsData::FRAGMENT:
1502 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1503 case QMetalShaderResourceBindingsData::COMPUTE:
1504 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1507 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1512 int dynamicOffsetCount,
1513 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1514 bool offsetOnlyChange,
1515 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES],
1520 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1521 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1522 switch (b->type()) {
1523 case QRhiShaderResourceBinding::UniformBuffer:
1525 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->ubuf().buf);
1526 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1527 quint32 offset = b->ubuf().offset;
1528 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1529 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1530 if (dynOfs.first == b->binding) {
1531 offset = dynOfs.second;
1536 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1537 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1538 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1539 if (nativeBinding >= 0)
1540 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1545 case QRhiShaderResourceBinding::SampledTexture:
1546 case QRhiShaderResourceBinding::Texture:
1547 case QRhiShaderResourceBinding::Sampler:
1549 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->stex();
1550 for (
int elem = 0; elem < data->count(); ++elem) {
1551 QMetalTexture *texD =
QRHI_RES(QMetalTexture, data->texSamplers[elem].tex);
1552 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler);
1554 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1555 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1560 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1561 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1562 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1563 if (textureBinding >= 0 && texD)
1564 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->textureForSampling(), MTLResourceUsageRead });
1565 if (samplerBinding >= 0)
1566 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1572 case QRhiShaderResourceBinding::ImageLoad:
1573 case QRhiShaderResourceBinding::ImageStore:
1574 case QRhiShaderResourceBinding::ImageLoadStore:
1576 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->simage().tex);
1577 id<MTLTexture> t = texD->d->viewForLevel(b->simage().level);
1579 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1580 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1581 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1582 if (nativeBinding >= 0)
1583 bindingData.res[stage].textures.append({ nativeBinding, t, storageImageUsage(b->type()) });
1588 case QRhiShaderResourceBinding::BufferLoad:
1589 case QRhiShaderResourceBinding::BufferStore:
1590 case QRhiShaderResourceBinding::BufferLoadStore:
1592 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->sbuf().buf);
1593 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1594 quint32 offset = b->sbuf().offset;
1595 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1596 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1597 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1598 if (nativeBinding >= 0)
1599 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1615 if (!shader || !shader->argumentEncoder)
1622 if (offsetOnlyChange) {
1624 cbD
->d->currentShaderResourceBindingState.res[stage]);
1626 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &b : prev.buffers) {
1627 if (b.nativeBinding == shader->argumentBufferIndex) {
1628 res.samplers.clear();
1629 res.buffers.append(b);
1630 res.usesArgumentBuffer =
true;
1643 const quint32 argBufAlignment = qMax(quint32(shader->argumentEncoder.alignment),
1645 quint32 argBufOffset = 0;
1646 id<MTLBuffer> argBuf =
d->allocArgumentBuffer(quint32(shader->argumentEncoder.encodedLength),
1647 argBufAlignment, currentFrameSlot, &argBufOffset);
1653 qWarning(
"Failed to allocate Metal argument buffer");
1654 res.textures.clear();
1655 res.samplers.clear();
1658 [shader->argumentEncoder setArgumentBuffer: argBuf offset: argBufOffset];
1659 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(res.textures))
1660 [shader->argumentEncoder setTexture: t.mtltex atIndex: NSUInteger(t.nativeBinding)];
1661 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &sm : std::as_const(res.samplers))
1662 [shader->argumentEncoder setSamplerState: sm.mtlsampler atIndex: NSUInteger(sm.nativeBinding)];
1663 res.samplers.clear();
1684 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1685 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1686 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1689 bindingData.res[stage].bufferBatches.finish();
1690 bindingData.res[stage].bufferOffsetBatches.finish();
1692 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1693 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1694 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1696 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1697 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1698 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1699 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1703 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1706 if (offsetOnlyChange)
1709 if (bindingData.res[stage].usesArgumentBuffer) {
1710 declareStageArgumentBufferResources(cbD, stage, bindingData.res[stage]);
1722 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1723 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1725 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1726 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1728 bindingData.res[stage].textureBatches.finish();
1729 bindingData.res[stage].samplerBatches.finish();
1731 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1732 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1734 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1735 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1739 bindStageTextures(cbD, stage, batch);
1742 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1743 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1745 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1746 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1750 bindStageSamplers(cbD, stage, batch);
1754 cbD
->d->currentShaderResourceBindingState = bindingData;
1759 const QList<QShaderDescription::PushConstantBlock> blocks = s.desc.pushConstantBlocks();
1760 return blocks.isEmpty() ? 0 : quint32(blocks.first().size);
1765 return s.nativeShaderInfo.extraBufferBindings.value(QShaderPrivate::MslPushConstantBufferBinding, -1);
1778 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1780 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1781 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1782 cbD
->d->currentDepthStencilState =
d->ds;
1785 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1789 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1792 if (rhiD->caps.depthClamp) {
1794 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1799 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1802 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1805 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1806 slopeScale: d->slopeScaledDepthBias
1811 if (m_stencilTest) {
1812 const quint32 ref = m_flags.testFlag(QRhiGraphicsPipeline::UsesStencilRef)
1813 ? cbD->currentStencilRef : 0;
1814 if (cbD->appliedStencilRef != ref) {
1815 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: ref];
1816 cbD->appliedStencilRef = ref;
1821 const int vsIdx = mtlPushConstantBufferIndex(
d->vs);
1822 const int fsIdx = mtlPushConstantBufferIndex(
d->fs);
1823 if (vsIdx >= 0 || fsIdx >= 0) {
1824 const quint32 blockSize = qMax(mtlPushConstantBlockSize(d->vs), mtlPushConstantBlockSize(d->fs));
1825 if (quint32(cbD->pushConstantData.size()) < blockSize)
1826 cbD->pushConstantData.resize(
int(blockSize), 0);
1827 const NSUInteger total = NSUInteger(cbD->pushConstantData.size());
1829 [cbD->d->currentRenderPassEncoder setVertexBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(vsIdx)];
1831 [cbD->d->currentRenderPassEncoder setFragmentBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(fsIdx)];
1853 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1858 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1859 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1860 workBuf->lastActiveFrameSlot = -1;
1862 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1863 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1864 workBuf->lastActiveFrameSlot = -1;
1871 int dynamicOffsetCount,
1872 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1881 srb = gfxPsD->m_shaderResourceBindings;
1883 srb = compPsD->m_shaderResourceBindings;
1887 bool hasSlottedResourceInSrb =
false;
1888 bool hasDynamicOffsetInSrb =
false;
1889 bool resNeedsRebind =
false;
1891 bool pipelineChanged =
false;
1904 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1907 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1908 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1910 switch (b->type()) {
1911 case QRhiShaderResourceBinding::UniformBuffer:
1914 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1915 sanityCheckResourceOwnership(bufD);
1918 hasSlottedResourceInSrb =
true;
1919 if (b->ubuf().hasDynamicOffset)
1920 hasDynamicOffsetInSrb =
true;
1921 if (bufD
->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1922 resNeedsRebind =
true;
1923 bd.ubuf.id = bufD->m_id;
1929 case QRhiShaderResourceBinding::SampledTexture:
1930 case QRhiShaderResourceBinding::Texture:
1931 case QRhiShaderResourceBinding::Sampler:
1933 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->stex();
1934 if (bd.stex.d.size() != data->count()) {
1935 bd.stex.d.resize(data->count());
1936 resNeedsRebind =
true;
1938 for (
int elem = 0; elem < data->count(); ++elem) {
1941 Q_ASSERT(texD || samplerD);
1942 sanityCheckResourceOwnership(texD);
1943 sanityCheckResourceOwnership(samplerD);
1944 const quint64 texId = texD ? texD->m_id : 0;
1946 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1947 const uint samplerGen = samplerD ? samplerD
->generation : 0;
1948 if (texGen != bd.stex.d[elem].texGeneration
1949 || texId != bd.stex.d[elem].texId
1950 || samplerGen != bd.stex.d[elem].samplerGeneration
1951 || samplerId != bd.stex.d[elem].samplerId)
1953 resNeedsRebind =
true;
1954 bd.stex.d[elem].texId = texId;
1955 bd.stex.d[elem].texGeneration = texGen;
1956 bd.stex.d[elem].samplerId = samplerId;
1957 bd.stex.d[elem].samplerGeneration = samplerGen;
1966 case QRhiShaderResourceBinding::ImageLoad:
1967 case QRhiShaderResourceBinding::ImageStore:
1968 case QRhiShaderResourceBinding::ImageLoadStore:
1971 sanityCheckResourceOwnership(texD);
1972 if (texD
->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1973 resNeedsRebind =
true;
1974 bd.simage.id = texD->m_id;
1980 case QRhiShaderResourceBinding::BufferLoad:
1981 case QRhiShaderResourceBinding::BufferStore:
1982 case QRhiShaderResourceBinding::BufferLoadStore:
1985 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1986 sanityCheckResourceOwnership(bufD);
1988 if (needsBufferSizeBuffer) {
1989 for (
int i = 0; i < 6; ++i) {
1990 const QRhiShaderResourceBinding::StageFlag stage =
1991 QRhiShaderResourceBinding::StageFlag(1 << i);
1992 if (b->stage.testFlag(stage)) {
1993 storageBufferSizes[stage][b->binding] = b->sbuf().maybeSize ? b->sbuf().maybeSize : bufD->size();
1999 if (bufD
->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
2000 resNeedsRebind =
true;
2001 bd.sbuf.id = bufD->m_id;
2013 if (needsBufferSizeBuffer) {
2015 QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
2019 Q_ASSERT(compPsD
->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
2020 shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage});
2023 if (gfxPsD
->d->tess.enabled) {
2033 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
2034 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
2035 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
2036 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
2037 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
2038 == gfxPsD
->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
2039 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
2040 == gfxPsD
->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
2041 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
2042 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
2043 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
2044 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
2046 if (gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2047 shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage});
2049 if (gfxPsD
->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2050 shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage});
2052 if (gfxPsD
->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2053 shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage});
2056 if (gfxPsD
->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2057 shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage});
2059 if (gfxPsD
->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2060 shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage});
2064 for (
const auto &shader : shaders) {
2066 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
2069 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
2071 int maxNativeBinding = 0;
2072 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
2073 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
2075 const int size = (maxNativeBinding + 1) *
sizeof(
int);
2077 Q_ASSERT(offset + size <= bufD->size());
2078 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
2080 QMetalShaderResourceBindings::BoundResourceData bd;
2081 bd.sbuf.id = bufD->m_id;
2082 bd.sbuf.generation = bufD->generation;
2083 srbD->boundResourceData.append(bd);
2087 QVarLengthArray<
int, 8> bufferSizeBufferData;
2088 Q_ASSERT(storageBufferSizes.contains(shader.second));
2089 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
2090 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
2091 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
2097 if (bufferSizeBufferData.size() <= index)
2098 bufferSizeBufferData.resize(index + 1);
2100 Q_ASSERT(sizes.contains(block.binding));
2101 bufferSizeBufferData[index] = sizes[block.binding];
2104 QRhiBufferData data;
2105 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
2106 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
2107 Q_ASSERT(offset + size <= bufD->size());
2108 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
2111 offset += ((size + 31) / 32) * 32;
2119 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
2121 resNeedsRebind =
true;
2127 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
2128 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
2133 if (gfxPsD
->d->tess.enabled) {
2136 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
2137 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
2156 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind
2157 && !srbChanged && !srbRebuilt && !pipelineChanged;
2158 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange,
2159 resBindMaps, shaders);
2164 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
2165 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
2170 QRhiBatchedBindings<id<MTLBuffer> > buffers;
2171 QRhiBatchedBindings<NSUInteger> offsets;
2172 for (
int i = 0; i < bindingCount; ++i) {
2176 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
2177 buffers.feed(startBinding + i, mtlbuf);
2178 offsets.feed(startBinding + i, bindings[i].second);
2193 || buffers != cbD
->d->currentVertexInputsBuffers
2194 || offsets != cbD
->d->currentVertexInputOffsets)
2197 cbD
->d->currentVertexInputsBuffers = buffers;
2198 cbD
->d->currentVertexInputOffsets = offsets;
2200 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
2201 const auto &bufferBatch(buffers.batches[i]);
2202 const auto &offsetBatch(offsets.batches[i]);
2203 [cbD->d->currentRenderPassEncoder setVertexBuffers:
2204 bufferBatch.resources.constData()
2205 offsets: offsetBatch.resources.constData()
2206 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2215 cbD->currentIndexOffset = indexOffset;
2216 cbD->currentIndexFormat = indexFormat;
2227QSize
QRhiMetal::outputSizeForTarget(QRhiRenderTarget *target)
2229 QRhiShadingRateMap *srm =
nullptr;
2230 switch (target->resourceType()) {
2231 case QRhiResource::TextureRenderTarget:
2232 srm =
QRHI_RES(QMetalTextureRenderTarget, target)->m_desc.shadingRateMap();
2234 case QRhiResource::SwapChainRenderTarget:
2235 srm =
QRHI_RES(QMetalSwapChainRenderTarget, target)->swapChain()->shadingRateMap();
2242 const QSize logicalSize = srm->logicalSize();
2243 if (logicalSize.isValid())
2247 return target->pixelSize();
2254 const QSize outputSize = outputSizeForTarget(cbD->currentTarget);
2255 std::array<
float, 4> vp = cbD->currentViewport.viewport();
2256 float x = 0, y = 0, w = 0, h = 0;
2258 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
2261 w = outputSize.width();
2262 h = outputSize.height();
2265 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, vp, &x, &y, &w, &h);
2269 s.x = NSUInteger(x);
2270 s.y = NSUInteger(y);
2271 s.width = NSUInteger(w);
2272 s.height = NSUInteger(h);
2273 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2280 const QSize outputSize = outputSizeForTarget(cbD->currentTarget);
2284 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
2288 vp.originX =
double(x);
2289 vp.originY =
double(y);
2290 vp.width =
double(w);
2291 vp.height =
double(h);
2292 vp.znear =
double(viewport.minDepth());
2293 vp.zfar =
double(viewport.maxDepth());
2295 [cbD->d->currentRenderPassEncoder setViewport: vp];
2297 cbD->currentViewport = viewport;
2311 const QSize outputSize = outputSizeForTarget(cbD->currentTarget);
2315 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
2319 s.x = NSUInteger(x);
2320 s.y = NSUInteger(y);
2321 s.width = NSUInteger(w);
2322 s.height = NSUInteger(h);
2324 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2327 cbD->currentScissor = scissor;
2335 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
2336 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
2339 cbD->currentBlendConstants = c;
2347 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
2349 cbD->currentStencilRef = refValue;
2350 cbD->appliedStencilRef = refValue;
2360 auto patch = [cbD, offset, size, data](quint32 blockSize) {
2361 const quint32 total = qMax(blockSize, offset + size);
2362 if (quint32(cbD->pushConstantData.size()) < total)
2363 cbD->pushConstantData.resize(
int(total), 0);
2364 memcpy(cbD->pushConstantData.data() + offset, data, size);
2372 const int idx = mtlPushConstantBufferIndex(psD
->d->cs);
2374 qWarning(
"No pipeline with a push constant block is active; setPushConstants ignored");
2377 const quint32 total = patch(mtlPushConstantBlockSize(psD->d->cs));
2378 [cbD->d->currentComputePassEncoder setBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(idx)];
2383 if (psD
->d->tess.enabled) {
2384 qWarning(
"Push constants are not supported with tessellation on Metal");
2387 const int vsIdx = mtlPushConstantBufferIndex(psD
->d->vs);
2388 const int fsIdx = mtlPushConstantBufferIndex(psD
->d->fs);
2389 if (vsIdx < 0 && fsIdx < 0) {
2390 qWarning(
"No pipeline with a push constant block is active; setPushConstants ignored");
2393 const quint32 total = patch(qMax(mtlPushConstantBlockSize(psD->d->vs), mtlPushConstantBlockSize(psD->d->fs)));
2395 [cbD->d->currentRenderPassEncoder setVertexBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(vsIdx)];
2397 [cbD->d->currentRenderPassEncoder setFragmentBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(fsIdx)];
2404 Q_UNUSED(coarsePixelSize);
2409 switch (cbD->currentTarget->resourceType()) {
2410 case QRhiResource::SwapChainRenderTarget:
2412 case QRhiResource::TextureRenderTarget:
2423 return tex && tex.storageMode != MTLStorageModeMemoryless;
2433 return finalAction == MTLStoreActionDontCare ? MTLStoreActionStore
2434 : MTLStoreActionStoreAndMultisampleResolve;
2444 for (
const auto &[index, finalAction] : std::as_const(cbD->d->deferredColorStoreActions))
2445 [cbD->d->currentRenderPassEncoder setColorStoreAction:
2446 interruptionStoreAction(finalAction, passIsEnding) atIndex: index];
2448 if (cbD->d->deferredDepthStoreAction != MTLStoreActionUnknown) {
2449 [cbD->d->currentRenderPassEncoder setDepthStoreAction:
2450 interruptionStoreAction(cbD->d->deferredDepthStoreAction, passIsEnding)];
2452 if (cbD->d->deferredStencilStoreAction != MTLStoreActionUnknown) {
2453 [cbD->d->currentRenderPassEncoder setStencilStoreAction:
2454 interruptionStoreAction(cbD->d->deferredStencilStoreAction, passIsEnding)];
2463 for (qsizetype i = 0; i < cbD->d->openDebugGroups.size(); ++i)
2464 [cbD->d->currentRenderPassEncoder popDebugGroup];
2465 [cbD->d->currentRenderPassEncoder endEncoding];
2466 cbD
->d->currentRenderPassEncoder = nil;
2471 id<MTLComputeCommandEncoder> maybeComputeEncoder)
2473 if (cbD
->d->currentRenderPassEncoder)
2476 if (!maybeComputeEncoder)
2477 maybeComputeEncoder = [cbD->d->cb computeCommandEncoder];
2479 return maybeComputeEncoder;
2483 id<MTLComputeCommandEncoder> computeEncoder)
2485 if (computeEncoder) {
2486 [computeEncoder endEncoding];
2487 computeEncoder = nil;
2496 const auto canLoad = [](MTLRenderPassAttachmentDescriptor *att) {
2497 return att.storeAction != MTLStoreActionDontCare && canStoreAttachment(att.texture);
2500 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2502 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2503 if (canLoad(cbD
->d->currentPassRpDesc.colorAttachments[i]))
2504 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2507 MTLLoadAction oldDepthLoad;
2508 MTLLoadAction oldStencilLoad;
2510 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2511 if (canLoad(cbD
->d->currentPassRpDesc.depthAttachment))
2512 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2514 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2515 if (canLoad(cbD
->d->currentPassRpDesc.stencilAttachment))
2516 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2523 const QRhiViewport prevViewport = cbD->currentViewport;
2525 const QRhiScissor prevScissor = cbD->currentScissor;
2527 const QColor prevBlendConstants = cbD->currentBlendConstants;
2528 const quint32 prevStencilRef = cbD->currentStencilRef;
2536 const QVarLengthArray<
char, 128> prevPushConstantData = cbD->pushConstantData;
2538 cbD
->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2541 for (
const QByteArray &name : std::as_const(cbD->d->openDebugGroups))
2542 [cbD->d->currentRenderPassEncoder pushDebugGroup: [NSString stringWithUTF8String: name.constData()]];
2544 cbD->pushConstantData = prevPushConstantData;
2550 if (!qFuzzyIsNull(prevViewport.viewport()[2]) || !qFuzzyIsNull(prevViewport.viewport()[3]))
2551 rhiD->setViewport(cbD, prevViewport);
2553 rhiD->setScissor(cbD, prevScissor);
2554 else if (prevHasDefaultScissor)
2556 if (prevHasBlendConstants)
2557 rhiD->setBlendConstants(cbD, prevBlendConstants);
2558 cbD->currentStencilRef = prevStencilRef;
2561 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2565 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2566 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2575 if (graphicsPipeline
->d->tess.failed)
2579 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2580 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2584 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2590 id<MTLComputeCommandEncoder> vertTescComputeEncoder
2591 = tempComputeEncoder(
this, cbD, cbD->d->tessellationComputeEncoder);
2592 cbD
->d->tessellationComputeEncoder = vertTescComputeEncoder;
2596 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2597 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2599 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2601 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2602 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2603 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2604 [computeEncoder setComputePipelineState: computePipelineState];
2609 cbD
->d->currentComputePassEncoder = computeEncoder;
2611 cbD
->d->currentComputePassEncoder = nil;
2613 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2614 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2615 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2617 if (outputBufferBinding >= 0) {
2618 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2619 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2622 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2625 if (indexBufferBinding >= 0)
2626 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2628 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2629 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2630 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2631 [computeEncoder setBuffers: bufferBatch.resources.constData()
2632 offsets: offsetBatch.resources.constData()
2633 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2637 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2638 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2640 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2641 args.draw.vertexCount, args.draw.instanceCount)];
2644 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2645 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2650 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2651 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2652 [computeEncoder setComputePipelineState: computePipelineState];
2654 cbD
->d->currentComputePassEncoder = computeEncoder;
2656 cbD
->d->currentComputePassEncoder = nil;
2658 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2659 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2660 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2661 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2662 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2663 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2665 if (outputBufferBinding >= 0) {
2666 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2667 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2670 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2673 if (patchOutputBufferBinding >= 0) {
2674 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2675 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2676 if (!tescPatchOutBuf)
2678 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2681 if (tessFactorBufferBinding >= 0) {
2682 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2683 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2686 if (paramsBufferBinding >= 0) {
2688 quint32 inControlPointCount;
2695 params.patchCount = patchCount;
2696 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2697 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2698 memcpy(p, ¶ms,
sizeof(params));
2699 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2702 if (vertOutBuf && inputBufferBinding >= 0)
2703 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2705 int sgSize =
int(computePipelineState.threadExecutionWidth);
2706 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2707 while (wgSize > caps.maxThreadGroupSize) {
2709 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2711 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2712 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2720 endTempComputeEncoding(
this, cbD, cbD
->d->tessellationComputeEncoder);
2721 cbD
->d->tessellationComputeEncoder = nil;
2730 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2735 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2736 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2737 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2738 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2740 if (outputBufferBinding >= 0 && tescOutBuf)
2741 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2743 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2744 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2746 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2747 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2748 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2751 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2753 patchCount: patchCount
2754 patchIndexBuffer: nil
2755 patchIndexBufferOffset: 0
2765 if (multiViewCount <= 1)
2769 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2770 if (viewMaskBufBinding == -1) {
2771 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2778 multiViewInfo.viewOffset = 0;
2779 multiViewInfo.viewCount = quint32(multiViewCount);
2783 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2784 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2785 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2786 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2790 *instanceCount *= multiViewCount;
2795 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2804 a.draw.vertexCount = vertexCount;
2805 a.draw.instanceCount = instanceCount;
2806 a.draw.firstVertex = firstVertex;
2807 a.draw.firstInstance = firstInstance;
2812 adjustForMultiViewDraw(&instanceCount, cb);
2814 if (caps.baseVertexAndInstance) {
2815 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2816 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2818 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2819 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2824 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2832 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2833 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2836 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2841 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2842 a.drawIndexed.indexCount = indexCount;
2843 a.drawIndexed.instanceCount = instanceCount;
2844 a.drawIndexed.firstIndex = firstIndex;
2845 a.drawIndexed.vertexOffset = vertexOffset;
2846 a.drawIndexed.firstInstance = firstInstance;
2847 a.drawIndexed.indexBuffer = mtlibuf;
2852 adjustForMultiViewDraw(&instanceCount, cb);
2854 if (caps.baseVertexAndInstance) {
2855 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2856 indexCount: indexCount
2857 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2858 indexBuffer: mtlibuf
2859 indexBufferOffset: indexOffset
2860 instanceCount: instanceCount
2861 baseVertex: vertexOffset
2862 baseInstance: firstInstance];
2864 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2865 indexCount: indexCount
2866 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2867 indexBuffer: mtlibuf
2868 indexBufferOffset: indexOffset
2869 instanceCount: instanceCount];
2876 if (!caps.indirectCommandBuffers)
2877 return "indirect command buffers are not supported on this device";
2881 return "the current graphics pipeline was not created with UsesIndirectDraws";
2884 return "the current graphics pipeline uses tessellation";
2886 return "the shaders of the current graphics pipeline sample textures but have no "
2887 "argument buffer variant, which Metal requires for a pipeline that supports "
2888 "indirect command buffers; rebuild them with qsb --msl-argument-buffers "
2889 "(or use MSLARGUMENTBUFFERS with qt_add_shaders)";
2899 if (!
d->icbEncodePipeline) {
2901 NSString *src = [NSString stringWithUTF8String:s_icbEncodeMsl];
2902 MTLCompileOptions *opts = [MTLCompileOptions
new];
2903 opts.languageVersion = MTLLanguageVersion2_1;
2904 id<MTLLibrary> lib = [d->dev newLibraryWithSource:src options:opts error:&err];
2907 qWarning(
"Failed to compile ICB encode kernel: %s",
2908 qPrintable(QString::fromNSString(err.localizedDescription)));
2912 d->icbEncodeFunction = [lib newFunctionWithName:@
"encode_icb"];
2913 d->icbEncodeFunctionU32 = [lib newFunctionWithName:@
"encode_icb_indexed_u32"];
2914 d->icbEncodeFunctionU16 = [lib newFunctionWithName:@
"encode_icb_indexed_u16"];
2916 if (!
d->icbEncodeFunction || !
d->icbEncodeFunctionU32 || !
d->icbEncodeFunctionU16) {
2917 qWarning(
"ICB encode kernel functions not found");
2921 NSError *errU32 = nil;
2922 NSError *errU16 = nil;
2923 d->icbEncodePipeline = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunction error:&err];
2924 d->icbEncodePipelineU32 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU32 error:&errU32];
2925 d->icbEncodePipelineU16 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU16 error:&errU16];
2926 if (!
d->icbEncodePipeline || !
d->icbEncodePipelineU32 || !
d->icbEncodePipelineU16) {
2927 NSError *firstErr = !
d->icbEncodePipeline ? err
2928 : (!
d->icbEncodePipelineU32 ? errU32 : errU16);
2929 qWarning(
"Failed to create ICB encode compute pipeline: %s",
2930 qPrintable(QString::fromNSString(firstErr.localizedDescription)));
2936 if (!
d->icbRangeBuffer) {
2937 d->icbRangeBuffer = [d->dev newBufferWithLength:
sizeof(MTLIndirectCommandBufferExecutionRange)
2938 options:MTLResourceStorageModePrivate];
2939 static constexpr quint32 noCount = 0xFFFFFFFFu;
2940 d->icbNoCountBuffer = [d->dev newBufferWithBytes:&noCount
2941 length:
sizeof(noCount)
2942 options:MTLResourceStorageModeShared];
2943 if (!
d->icbRangeBuffer || !
d->icbNoCountBuffer) {
2944 qWarning(
"Failed to create ICB helper buffers");
2958 if (!
d->icb ||
d->icbCapacity < maxDrawCount) {
2963 e.stagingIcbBuffer.icb =
d->icb;
2964 e.stagingIcbBuffer.argBuffer =
d->icbArgumentBuffer;
2965 d->releaseQueue.append(e);
2968 d->icbArgumentBuffer = nil;
2970 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
2971 icbDesc.commandTypes = MTLIndirectCommandTypeDraw | MTLIndirectCommandTypeDrawIndexed;
2972 icbDesc.inheritPipelineState = YES;
2973 icbDesc.inheritBuffers = YES;
2974 icbDesc.maxVertexBufferBindCount = 0;
2975 icbDesc.maxFragmentBufferBindCount = 0;
2976 d->icb = [d->dev newIndirectCommandBufferWithDescriptor:icbDesc
2977 maxCommandCount:maxDrawCount
2978 options:MTLResourceStorageModePrivate];
2981 qWarning(
"Failed to create MTLIndirectCommandBuffer");
2985 d->icbCapacity = maxDrawCount;
2987 id<MTLArgumentEncoder> argEnc = [d->icbEncodeFunction newArgumentEncoderWithBufferIndex:1];
2988 d->icbArgumentBuffer = [d->dev newBufferWithLength:argEnc.encodedLength
2989 options:MTLResourceStorageModeShared];
2990 [argEnc setArgumentBuffer:d->icbArgumentBuffer offset:0];
2991 [argEnc setIndirectCommandBuffer:d->icb atIndex:0];
3002 id<MTLComputeCommandEncoder> computeEncoder,
3003 id<MTLIndirectCommandBuffer> targetIcb,
3004 id<MTLBuffer> targetArgBuffer,
3005 id<MTLBuffer> targetRangeBuffer,
3007 QRhiCommandBuffer::IndexFormat indexFormat,
3008 MTLPrimitiveType primitiveType,
3009 id<MTLBuffer> indirectBufMtl, quint32 indirectBufferOffset,
3010 id<MTLBuffer> indexBufMtl, quint32 indexBufferOffset,
3011 id<MTLBuffer> countBufMtl, quint32 countBufferOffset,
3012 quint32 maxDrawCount, quint32 stride)
3014 id<MTLComputePipelineState> computePipeline = d->icbEncodePipeline;
3016 computePipeline = indexFormat == QRhiCommandBuffer::IndexUInt16
3017 ? d->icbEncodePipelineU16 : d->icbEncodePipelineU32;
3019 uint32_t maxDrawCountVal = maxDrawCount;
3020 uint32_t metalPrimType = uint32_t(primitiveType);
3021 uint32_t strideVal = stride;
3023 [computeEncoder setComputePipelineState:computePipeline];
3024 [computeEncoder setBuffer:indirectBufMtl offset:indirectBufferOffset atIndex:0];
3025 [computeEncoder setBuffer:targetArgBuffer offset:0 atIndex:1];
3026 [computeEncoder setBytes:&maxDrawCountVal length:
sizeof(uint32_t) atIndex:2];
3028 [computeEncoder setBuffer:indexBufMtl offset:indexBufferOffset atIndex:3];
3029 [computeEncoder setBytes:&metalPrimType length:
sizeof(uint32_t) atIndex:4];
3030 [computeEncoder setBytes:&strideVal length:
sizeof(uint32_t) atIndex:5];
3031 [computeEncoder setBuffer:countBufMtl ? countBufMtl : d->icbNoCountBuffer
3032 offset:countBufMtl ? countBufferOffset : 0
3034 [computeEncoder setBuffer:targetRangeBuffer offset:0 atIndex:7];
3035 [computeEncoder useResource:targetIcb usage:MTLResourceUsageWrite];
3036 [computeEncoder useResource:indirectBufMtl usage:MTLResourceUsageRead];
3038 [computeEncoder useResource:indexBufMtl usage:MTLResourceUsageRead];
3040 NSUInteger tw = computePipeline.threadExecutionWidth;
3041 [computeEncoder dispatchThreads:MTLSizeMake(maxDrawCount, 1, 1)
3042 threadsPerThreadgroup:MTLSizeMake(tw, 1, 1)];
3052 QMetalBuffer *indirectBufD, quint32 indirectBufferOffset,
3054 quint32 maxDrawCount, quint32 stride)
3060 if (indexed && !indexBufD)
3063 if (!prepareIcb(maxDrawCount))
3068 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3070 id<MTLBuffer> countBufMtl = nil;
3074 countBufMtl = countBufD->d->buf[countBufD->d->slotted ? currentFrameSlot : 0];
3081 const auto savedVertexBuffers = cbD
->d->currentVertexInputsBuffers;
3082 const auto savedVertexOffsets = cbD
->d->currentVertexInputOffsets;
3083 const quint32 savedIndexOffset = cbD->currentIndexOffset;
3084 const QRhiCommandBuffer::IndexFormat savedIndexFormat = cbD->currentIndexFormat;
3085 id<MTLBuffer> indexBufMtl = indexed
3086 ? indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0] : nil;
3091 id<MTLComputeCommandEncoder> computeEncoder = [cbD->d->cb computeCommandEncoder];
3092 encodeIcbWithCompute(
d, computeEncoder,
d->icb,
d->icbArgumentBuffer,
d->icbRangeBuffer,
3093 indexed, savedIndexFormat, savedPipeline
->d->primitiveType,
3094 indirectBufMtl, indirectBufferOffset,
3095 indexBufMtl, savedIndexOffset,
3096 countBufMtl, countBufferOffset,
3097 maxDrawCount, stride);
3100 endTempComputeEncoding(
this, cbD, computeEncoder);
3109 if (savedFirstVertexBinding >= 0) {
3111 cbD
->d->currentVertexInputsBuffers = savedVertexBuffers;
3112 cbD
->d->currentVertexInputOffsets = savedVertexOffsets;
3113 for (
int i = 0, ie = savedVertexBuffers.batches.count(); i != ie; ++i) {
3114 const auto &bufferBatch(savedVertexBuffers.batches[i]);
3115 const auto &offsetBatch(savedVertexOffsets.batches[i]);
3116 [cbD->d->currentRenderPassEncoder setVertexBuffers:
3117 bufferBatch.resources.constData()
3118 offsets: offsetBatch.resources.constData()
3119 withRange: NSMakeRange(uint(savedFirstVertexBinding) + bufferBatch.startBinding,
3120 NSUInteger(bufferBatch.resources.count()))];
3125 cbD->currentIndexOffset = savedIndexOffset;
3126 cbD->currentIndexFormat = savedIndexFormat;
3130 [cbD->d->currentRenderPassEncoder useResource:indirectBufMtl
3131 usage:MTLResourceUsageRead
3132 stages:MTLRenderStageVertex | MTLRenderStageFragment];
3134 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
3135 usage:MTLResourceUsageRead
3136 stages:MTLRenderStageVertex | MTLRenderStageFragment];
3138 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:d->icb
3139 indirectBuffer:d->icbRangeBuffer
3140 indirectBufferOffset:0];
3153 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
3161 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3163 if (drawCount > ICB_DRAW_COUNT_THRESHOLD && !icbUnavailableReason(cbD)
3164 && icbDraw(cbD,
false, indirectBufD, indirectBufferOffset,
nullptr, 0, drawCount, stride))
3170 NSUInteger offset = indirectBufferOffset;
3171 for (quint32 i = 0; i < drawCount; ++i) {
3172 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
3173 indirectBuffer: indirectBufMtl
3174 indirectBufferOffset: offset];
3180 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
3189 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
3194 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3196 if (drawCount > ICB_DRAW_COUNT_THRESHOLD && !icbUnavailableReason(cbD)
3197 && icbDraw(cbD,
true, indirectBufD, indirectBufferOffset,
nullptr, 0, drawCount, stride))
3203 NSUInteger offset = indirectBufferOffset;
3204 for (quint32 i = 0; i < drawCount; ++i) {
3205 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
3206 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
3207 indexBuffer: indexBufMtl
3208 indexBufferOffset: cbD->currentIndexOffset
3209 indirectBuffer: indirectBufMtl
3210 indirectBufferOffset: offset];
3220 NSString *str = [NSString stringWithUTF8String: name.constData()];
3223 case QMetalCommandBuffer::RenderPass:
3224 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
3225 cbD
->d->openDebugGroups.append(name);
3227 case QMetalCommandBuffer::ComputePass:
3228 [cbD->d->currentComputePassEncoder pushDebugGroup: str];
3231 [cbD->d->cb pushDebugGroup: str];
3243 case QMetalCommandBuffer::RenderPass:
3244 [cbD->d->currentRenderPassEncoder popDebugGroup];
3245 if (!cbD
->d->openDebugGroups.isEmpty())
3246 cbD
->d->openDebugGroups.removeLast();
3248 case QMetalCommandBuffer::ComputePass:
3249 [cbD->d->currentComputePassEncoder popDebugGroup];
3252 [cbD->d->cb popDebugGroup];
3263 NSString *str = [NSString stringWithUTF8String: msg.constData()];
3265 case QMetalCommandBuffer::RenderPass:
3266 [cbD->d->currentRenderPassEncoder insertDebugSignpost: str];
3268 case QMetalCommandBuffer::ComputePass:
3269 [cbD->d->currentComputePassEncoder insertDebugSignpost: str];
3278 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
3304 currentFrameSlot = swapChainD->currentFrameSlot;
3309 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
3317 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
3318 if (sc != swapChainD)
3319 sc->waitUntilCompleted(currentFrameSlot);
3322 [d->captureScope beginScope];
3324 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
3328 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
3335 swapChainD
->rtWrapper.d->fb.dsTex = swapChainD
->ds ? swapChainD->ds->d->tex : nil;
3343 d->argBufPool[currentFrameSlot].offset = 0;
3345 d->globalFrameId += 1;
3348 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
3349 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
3352 return QRhi::FrameOpSuccess;
3361 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
3363 __block
int thisFrameSlot = currentFrameSlot;
3364 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
3365 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
3366 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
3373 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
3374 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
3375 [drawableTexture release];
3379 if (flags.testFlag(QRhi::SkipPresent)) {
3381 [commandBuffer commit];
3383 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
3385 if (swapChainD
->d->layer.presentsWithTransaction) {
3386 [commandBuffer commit];
3388 auto *metalLayer = swapChainD
->d->layer;
3389 auto presentWithTransaction = ^{
3390 [commandBuffer waitUntilScheduled];
3397 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
3398 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
3399 if (textureSize == surfaceSize) {
3402 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
3403 << textureSize <<
"not matching surface size" << surfaceSize;
3407 if (NSThread.currentThread == NSThread.mainThread) {
3408 presentWithTransaction();
3410 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
3411 Q_ASSERT(qtMetalLayer);
3413 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
3417 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
3418 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
3424 if (qtMetalLayer.displayLock.tryLockForRead()) {
3426 qtMetalLayer.displayLock.unlock();
3428 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
3429 <<
"due to" << qtMetalLayer <<
"needing display";
3435 [commandBuffer commit];
3439 [commandBuffer commit];
3446 [swapChainD->d->curDrawable release];
3447 swapChainD
->d->curDrawable = nil;
3449 [d->captureScope endScope];
3453 return QRhi::FrameOpSuccess;
3460 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
3462 for (QMetalSwapChain *sc : std::as_const(swapchains))
3463 sc->waitUntilCompleted(currentFrameSlot);
3465 d->ofr.active =
true;
3466 *cb = &
d->ofr.cbWrapper;
3467 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
3469 d->argBufPool[currentFrameSlot].offset = 0;
3471 d->globalFrameId += 1;
3474 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
3475 d->ofr.lastGpuTime = 0;
3478 return QRhi::FrameOpSuccess;
3484 Q_ASSERT(
d->ofr.active);
3485 d->ofr.active =
false;
3487 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
3491 [cb waitUntilCompleted];
3493 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
3497 return QRhi::FrameOpSuccess;
3502 id<MTLCommandBuffer> cb = nil;
3505 if (
d->ofr.active) {
3508 cb =
d->ofr.cbWrapper.d->cb;
3513 cb = swapChainD->cbWrapper.d->cb;
3517 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
3518 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3519 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
3524 sc->waitUntilCompleted(i);
3530 [cb waitUntilCompleted];
3534 if (
d->ofr.active) {
3535 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
3536 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
3538 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
3539 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
3547 return QRhi::FrameOpSuccess;
3551 const QColor &colorClearValue,
3552 const QRhiDepthStencilClearValue &depthStencilClearValue,
3554 QRhiShadingRateMap *shadingRateMap)
3556 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
3557 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
3558 colorClearValue.alphaF());
3560 for (uint i = 0; i < uint(colorAttCount); ++i) {
3561 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
3562 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
3563 rp.colorAttachments[i].clearColor = c;
3566 if (hasDepthStencil) {
3567 rp.depthAttachment.loadAction = MTLLoadActionClear;
3568 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
3569 rp.stencilAttachment.loadAction = MTLLoadActionClear;
3570 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
3571 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
3572 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
3576 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
3584 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3585 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3586 if (imageSizeBytes > 0)
3587 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3592 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
3595 const QPoint dp = subresDesc.destinationTopLeft();
3596 const QByteArray rawData = subresDesc.data();
3597 QImage img = subresDesc.image();
3598 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3599 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
3601 if (!img.isNull()) {
3602 const qsizetype fullImageSizeBytes = img.sizeInBytes();
3603 QSize size = img.size();
3604 int bpl = img.bytesPerLine();
3606 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3607 const int sx = subresDesc.sourceTopLeft().x();
3608 const int sy = subresDesc.sourceTopLeft().y();
3609 if (!subresDesc.sourceSize().isEmpty())
3610 size = subresDesc.sourceSize();
3611 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3612 if (size.width() == img.width()) {
3613 const int bpc = qMax(1, img.depth() / 8);
3614 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
3615 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
3616 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
3617 size.height() * img.bytesPerLine());
3619 img = img.copy(sx, sy, size.width(), size.height());
3620 bpl = img.bytesPerLine();
3621 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
3622 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
3625 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3626 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
3629 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3630 sourceOffset: NSUInteger(*curOfs)
3631 sourceBytesPerRow: NSUInteger(bpl)
3632 sourceBytesPerImage: 0
3633 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
3634 toTexture: texD->d->tex
3635 destinationSlice: NSUInteger(is3D ? 0 : layer)
3636 destinationLevel: NSUInteger(level)
3637 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3638 options: MTLBlitOptionNone];
3640 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3641 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3642 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3643 const int subresw = subresSize.width();
3644 const int subresh = subresSize.height();
3646 if (subresDesc.sourceSize().isEmpty()) {
3650 w = subresDesc.sourceSize().width();
3651 h = subresDesc.sourceSize().height();
3656 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
3658 const int dx = aligned(dp.x(), blockDim.width());
3659 const int dy = aligned(dp.y(), blockDim.height());
3660 if (dx + w != subresw)
3661 w = aligned(w, blockDim.width());
3662 if (dy + h != subresh)
3663 h = aligned(h, blockDim.height());
3665 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3667 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3668 sourceOffset: NSUInteger(*curOfs)
3669 sourceBytesPerRow: bpl
3670 sourceBytesPerImage: 0
3671 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3672 toTexture: texD->d->tex
3673 destinationSlice: NSUInteger(is3D ? 0 : layer)
3674 destinationLevel: NSUInteger(level)
3675 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
3676 options: MTLBlitOptionNone];
3678 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3679 }
else if (!rawData.isEmpty()) {
3680 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3681 const int subresw = subresSize.width();
3682 const int subresh = subresSize.height();
3684 if (subresDesc.sourceSize().isEmpty()) {
3688 w = subresDesc.sourceSize().width();
3689 h = subresDesc.sourceSize().height();
3692 QSize size = clampedSubResourceUploadSize(QSize(w, h), dp, level, texD->m_pixelSize);
3693 quint32 bytesPerPixel = 0;
3694 textureFormatInfo(texD->m_format, size,
nullptr,
nullptr, &bytesPerPixel);
3695 size = clampedSubResourceUploadSizeForSourceData(size, subresDesc.dataStride(),
3696 bytesPerPixel, rawData.size());
3701 if (subresDesc.dataStride())
3702 bpl = subresDesc.dataStride();
3704 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
3706 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3708 if (!size.isEmpty()) {
3709 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3710 sourceOffset: NSUInteger(*curOfs)
3711 sourceBytesPerRow: bpl
3712 sourceBytesPerImage: 0
3713 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3714 toTexture: texD->d->tex
3715 destinationSlice: NSUInteger(is3D ? 0 : layer)
3716 destinationLevel: NSUInteger(level)
3717 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3718 options: MTLBlitOptionNone];
3721 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3723 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3732 id<MTLBlitCommandEncoder> blitEnc = nil;
3733 auto ensureBlit = [&blitEnc, cbD,
this]() {
3735 blitEnc = [cbD->d->cb blitCommandEncoder];
3737 [blitEnc pushDebugGroup: @
"Resource updates"];
3745 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3746 for (
int i = 0, ie = bufD
->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i) {
3747 if (u.offset == 0 && u.data.size() == bufD->m_size)
3748 bufD
->d->pendingUpdates[i].clear();
3749 bufD
->d->pendingUpdates[i].append({ u.offset, u.data });
3753 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3754 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
3756 const quint32 size = quint32(u.data.size());
3758 quint32 stagingOffset = 0;
3759 id<MTLBuffer> stagingBuf =
d->allocBufferStaging(size, currentFrameSlot, &stagingOffset);
3761 char *p =
reinterpret_cast<
char *>([stagingBuf contents]);
3762 memcpy(p + stagingOffset, u.data.constData(), size);
3764 [blitEnc copyFromBuffer: stagingBuf
3765 sourceOffset: stagingOffset
3766 toBuffer: bufD->d->buf[0]
3767 destinationOffset: u.offset
3770 qWarning(
"Failed to allocate staging buffer of size %u for buffer upload", size);
3775 for (
int i = 0, ie = bufD
->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i)
3776 bufD
->d->pendingUpdates[i].append({ u.offset, u.data });
3781 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
3782 if (bufD->m_type == QRhiBuffer::Dynamic) {
3783 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
3785 u.result->data.resize(u.readSize);
3786 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
3788 if (u.result->completed)
3789 u.result->completed();
3800 readback.readSize = u.readSize;
3801 readback.result = u.result;
3802 readback.buf = [d->dev newBufferWithLength: u.readSize
3803 options: MTLResourceStorageModeShared];
3806 [blitEnc copyFromBuffer: bufD->d->buf[idx]
3807 sourceOffset: u.offset
3808 toBuffer: readback.buf
3809 destinationOffset: 0
3812 d->activeBufferReadbacks.append(readback);
3818 Q_ASSERT(dstD->m_type != QRhiBuffer::Dynamic && srcD->m_type != QRhiBuffer::Dynamic);
3821 const int srcIdx = srcD->d->slotted ? currentFrameSlot : 0;
3822 const int dstSlotCount = dstD
->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1;
3832 for (
int i = 0; i != dstSlotCount; ++i)
3836 for (
int i = 0; i != dstSlotCount; ++i) {
3837 [blitEnc copyFromBuffer: srcD->d->buf[srcIdx]
3838 sourceOffset: u.srcOffset
3839 toBuffer: dstD->d->buf[i]
3840 destinationOffset: u.offset
3847 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic && !bufD->d->slotted);
3850 [blitEnc fillBuffer: bufD->d->buf[0]
3851 range: NSMakeRange(u.offset, u.readSize)
3852 value: u.fillValue];
3861 qsizetype stagingSize = 0;
3862 for (
const auto &subres : u.subresDesc)
3863 stagingSize += subresUploadByteSize(subres.desc);
3866 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
3867 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
3868 options: MTLResourceStorageModeShared];
3870 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
3871 qsizetype curOfs = 0;
3872 for (
const auto &subres : u.subresDesc)
3873 enqueueSubresUpload(utexD, mp, blitEnc, subres.layer, subres.level, subres.desc, &curOfs);
3880 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
3881 utexD->d->stagingBuf[currentFrameSlot] = nil;
3882 d->releaseQueue.append(e);
3887 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3888 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3889 const QPoint dp = u.desc.destinationTopLeft();
3890 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3891 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3892 const QPoint sp = u.desc.sourceTopLeft();
3895 [blitEnc copyFromTexture: srcD->d->tex
3896 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
3897 sourceLevel: NSUInteger(u.desc.sourceLevel())
3898 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
3899 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
3900 toTexture: dstD->d->tex
3901 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
3902 destinationLevel: NSUInteger(u.desc.destinationLevel())
3903 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
3909 readback.desc = u.rb;
3910 readback.result = u.result;
3919 qWarning(
"Multisample texture cannot be read back");
3922 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3923 if (u.rb.rect().isValid())
3926 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3927 readback.format = texD->m_format;
3933 if (u.rb.rect().isValid())
3936 rect = QRect({0, 0}, swapChainD->pixelSize);
3937 readback.format = swapChainD
->d->rhiColorFormat;
3941 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
3943 readback.pixelSize = rect.size();
3946 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
3957 const MTLPixelFormat mtlFormat = texD ? texD->d->format : MTLPixelFormatInvalid;
3958 const bool isDepthFormat = texD && (readback.format == QRhiTexture::D16
3959 || readback.format == QRhiTexture::D24
3960 || readback.format == QRhiTexture::D24S8
3961 || readback.format == QRhiTexture::D32F
3962 || readback.format == QRhiTexture::D32FS8);
3963 const bool nativeLayout = (readback.format == QRhiTexture::D16 && mtlFormat == MTLPixelFormatDepth16Unorm)
3964 || (readback.format == QRhiTexture::D32F && mtlFormat == MTLPixelFormatDepth32Float);
3965 const bool usePlanes = isDepthFormat && !nativeLayout;
3966 bool combinedMtlFormat = mtlFormat == MTLPixelFormatDepth32Float_Stencil8;
3968 if (mtlFormat == MTLPixelFormatDepth24Unorm_Stencil8)
3969 combinedMtlFormat =
true;
3971 const bool hasStencil = isStencilSupportingFormat(readback.format);
3972 quint32 depthPlaneBytesPerRow = 0;
3976 depthPlaneBytesPerRow = quint32(rect.width()) * 4;
3977 readback.depthPlaneByteSize = depthPlaneBytesPerRow * quint32(rect.height());
3979 readback.stencilPlaneByteSize = quint32(rect.width()) * quint32(rect.height());
3981 || mtlFormat == MTLPixelFormatDepth32Float_Stencil8;
3983 const quint32 stagingBufSize = usePlanes
3984 ? readback.depthPlaneByteSize + readback.stencilPlaneByteSize
3986 readback.buf = [d->dev newBufferWithLength: stagingBufSize options: MTLResourceStorageModeShared];
3989 const auto blit = [&](quint32 destinationOffset, quint32 destinationBytesPerRow, MTLBlitOption options) {
3990 [blitEnc copyFromTexture: src
3991 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
3992 sourceLevel: NSUInteger(u.rb.level())
3993 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
3994 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
3995 toBuffer: readback.buf
3996 destinationOffset: destinationOffset
3997 destinationBytesPerRow: destinationBytesPerRow
3998 destinationBytesPerImage: 0
4002 blit(0, depthPlaneBytesPerRow,
4003 combinedMtlFormat ? MTLBlitOptionDepthFromDepthStencil : MTLBlitOptionNone);
4005 blit(readback.depthPlaneByteSize, quint32(rect.width()), MTLBlitOptionStencilFromDepthStencil);
4007 blit(0, bpl, MTLBlitOptionNone);
4010 d->activeTextureReadbacks.append(readback);
4014 [blitEnc generateMipmapsForTexture: utexD->d->tex];
4021 [blitEnc popDebugGroup];
4022 [blitEnc endEncoding];
4031 if (bufD
->d->pendingUpdates[slot].isEmpty())
4036 void *p = [bufD->d->buf[slot] contents];
4037 quint32 changeBegin = UINT32_MAX;
4038 quint32 changeEnd = 0;
4039 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
4040 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
4041 if (u.offset < changeBegin)
4042 changeBegin = u.offset;
4043 if (u.offset + u.data.size() > changeEnd)
4044 changeEnd = u.offset + u.data.size();
4047 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
4048 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
4051 bufD
->d->pendingUpdates[slot].clear();
4061 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
4067 QRhiRenderTarget *rt,
4068 const QColor &colorClearValue,
4069 const QRhiDepthStencilClearValue &depthStencilClearValue,
4070 QRhiResourceUpdateBatch *resourceUpdates,
4076 if (resourceUpdates)
4080 switch (rt->resourceType()) {
4081 case QRhiResource::SwapChainRenderTarget:
4085 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
4088 depthStencilClearValue,
4096 if (!swapChainD
->d->curDrawable) {
4097 QMacAutoReleasePool pool;
4098 swapChainD
->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
4100 if (!swapChainD
->d->curDrawable) {
4101 qWarning(
"No drawable");
4104 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
4109 color0.resolveTex = scTex;
4115 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
4118 case QRhiResource::TextureRenderTarget:
4122 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
4126 depthStencilClearValue,
4128 rtTex->m_desc.shadingRateMap());
4129 if (rtD->fb.preserveColor) {
4131 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
4134 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
4135 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
4137 int colorAttCount = 0;
4138 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
4142 if (it->texture()) {
4144 if (it->multiViewCount() >= 2)
4145 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
4146 }
else if (it->renderBuffer()) {
4149 if (it->resolveTexture())
4152 if (rtTex->m_desc.depthStencilBuffer())
4154 if (rtTex->m_desc.depthTexture()) {
4157 if (depthTexture->arraySize() >= 2) {
4158 const int depthLayer = rtTex->m_desc.depthLayer();
4159 if (depthLayer >= 0) {
4160 cbD
->d->currentPassRpDesc.depthAttachment.slice = NSUInteger(depthLayer);
4161 cbD
->d->currentPassRpDesc.stencilAttachment.slice = NSUInteger(depthLayer);
4162 if (colorAttCount == 0)
4163 cbD
->d->currentPassRpDesc.renderTargetArrayLength = 1;
4164 }
else if (colorAttCount == 0) {
4165 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
4169 if (rtTex->m_desc.depthResolveTexture())
4171 if (rtTex->m_desc.shadingRateMap())
4180 cbD
->d->deferredColorStoreActions.clear();
4181 cbD
->d->deferredDepthStoreAction = MTLStoreActionUnknown;
4182 cbD
->d->deferredStencilStoreAction = MTLStoreActionUnknown;
4184 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
4185 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
4186 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
4187 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
4188 if (rtD->fb.colorAtt[i].resolveTex) {
4189 const MTLStoreAction storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
4190 : MTLStoreActionMultisampleResolve;
4194 cbD
->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionUnknown;
4195 cbD
->d->deferredColorStoreActions.append({ i, storeAction });
4196 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
4197 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
4198 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
4203 Q_ASSERT(rtD->fb.dsTex);
4204 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
4205 cbD
->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
4206 if (rtD->fb.depthNeedsStore) {
4207 cbD
->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
4208 if (rtD->fb.hasStencil)
4209 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = MTLStoreActionStore;
4210 }
else if (canStoreAttachment(rtD->fb.dsTex)) {
4214 cbD
->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionUnknown;
4215 cbD
->d->deferredDepthStoreAction = MTLStoreActionDontCare;
4216 if (rtD->fb.hasStencil) {
4217 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = MTLStoreActionUnknown;
4218 cbD
->d->deferredStencilStoreAction = MTLStoreActionDontCare;
4221 if (rtD->fb.dsResolveTex) {
4222 const MTLStoreAction dsStoreAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
4223 : MTLStoreActionMultisampleResolve;
4229 const bool deferrable = canStoreAttachment(rtD->fb.dsTex);
4230 cbD
->d->currentPassRpDesc.depthAttachment.storeAction = deferrable ? MTLStoreActionUnknown
4233 cbD
->d->deferredDepthStoreAction = dsStoreAction;
4234 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
4235 if (rtD->fb.hasStencil) {
4236 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
4237 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = deferrable ? MTLStoreActionUnknown
4240 cbD
->d->deferredStencilStoreAction = dsStoreAction;
4245 cbD
->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
4250 cbD->currentTarget = rt;
4259 [cbD->d->currentRenderPassEncoder endEncoding];
4262 cbD->currentTarget =
nullptr;
4264 if (resourceUpdates)
4269 QRhiResourceUpdateBatch *resourceUpdates,
4275 if (resourceUpdates)
4278 cbD
->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
4288 [cbD->d->currentComputePassEncoder endEncoding];
4291 if (resourceUpdates)
4306 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
4318 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
4319 threadsPerThreadgroup: psD->d->localSize];
4323 quint32 indirectBufferOffset)
4332 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
4339 [cbD->d->currentComputePassEncoder
4340 dispatchThreadgroupsWithIndirectBuffer: indirectBufMtl
4341 indirectBufferOffset: indirectBufferOffset
4342 threadsPerThreadgroup: psD->d->localSize];
4346 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
4347 QRhiBuffer *countBuffer, quint32 countBufferOffset,
4348 quint32 maxDrawCount, quint32 stride)
4356 qWarning(
"drawIndirectCount is not available because %s; skipping", reason);
4360 icbDraw(cbD,
false,
QRHI_RES(QMetalBuffer, indirectBuffer), indirectBufferOffset,
4361 QRHI_RES(QMetalBuffer, countBuffer), countBufferOffset, maxDrawCount, stride);
4365 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
4366 QRhiBuffer *countBuffer, quint32 countBufferOffset,
4367 quint32 maxDrawCount, quint32 stride)
4375 qWarning(
"drawIndexedIndirectCount is not available because %s; skipping", reason);
4380 qWarning(
"drawIndexedIndirectCount called without an index buffer bound; skipping");
4384 icbDraw(cbD,
true,
QRHI_RES(QMetalBuffer, indirectBuffer), indirectBufferOffset,
4385 QRHI_RES(QMetalBuffer, countBuffer), countBufferOffset, maxDrawCount, stride);
4429 e.stagingIcbBuffer.icb = slot.icb;
4430 e.stagingIcbBuffer.argBuffer = slot.argBuffer;
4431 rhiD
->d->releaseQueue.append(e);
4433 if (slot.rangeBuffer) {
4437 e.stagingBuffer.buffer = slot.rangeBuffer;
4438 rhiD
->d->releaseQueue.append(e);
4454 return icbD
->d->frameSlots[0].icb != nil;
4462 if (!rhiD->caps.indirectCommandBuffers)
4469 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
4470 icbDesc.commandTypes = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws
4471 ? MTLIndirectCommandTypeDrawIndexed : MTLIndirectCommandTypeDraw;
4473 icbDesc.inheritPipelineState = YES;
4474 icbDesc.inheritBuffers = YES;
4475 icbDesc.maxVertexBufferBindCount = 0;
4476 icbDesc.maxFragmentBufferBindCount = 0;
4481 slot.icb = [rhiD->d->dev newIndirectCommandBufferWithDescriptor:icbDesc
4482 maxCommandCount:icbD->maxCommandCount()
4483 options:gpu ? MTLResourceStorageModePrivate
4484 : MTLResourceStorageModeShared];
4486 qWarning(
"Failed to create MTLIndirectCommandBuffer");
4491 slot.rangeBuffer = [rhiD->d->dev newBufferWithLength:
sizeof(MTLIndirectCommandBufferExecutionRange)
4492 options:MTLResourceStorageModePrivate];
4493 id<MTLArgumentEncoder> argEnc = [rhiD->d->icbEncodeFunction newArgumentEncoderWithBufferIndex:1];
4494 slot.argBuffer = [rhiD->d->dev newBufferWithLength:argEnc.encodedLength
4495 options:MTLResourceStorageModeShared];
4496 if (slot.rangeBuffer && slot.argBuffer) {
4497 [argEnc setArgumentBuffer:slot.argBuffer offset:0];
4498 [argEnc setIndirectCommandBuffer:slot.icb atIndex:0];
4500 qWarning(
"Failed to create MTLIndirectCommandBuffer helper buffers");
4512 [icbD->d->frameSlots[i].icb release];
4513 [icbD->d->frameSlots[i].rangeBuffer release];
4514 [icbD->d->frameSlots[i].argBuffer release];
4515 icbD
->d->frameSlots[i] = {};
4526 quint32 maxCommandCount)
4552 m_gpuBuiltCommandCount = 0;
4557 rhiD->unregisterResource(
this);
4568 if (!m_maxCommandCount) {
4569 qWarning(
"QRhiIndirectCommandBuffer: maxCommandCount is 0");
4576 rhiD->registerResource(
this);
4580QRhiIndirectCommandBuffer *
QRhiMetal::createIndirectCommandBuffer(QRhiIndirectCommandBuffer::Type type,
4581 quint32 maxCommandCount)
4583 return new QMetalIndirectCommandBuffer(
this, type, maxCommandCount);
4587 QRhiIndirectCommandBuffer *icb)
4599 MTLPrimitiveType primitiveType,
4600 id<MTLBuffer> indexBufMtl, quint32 indexOffset,
4601 QRhiCommandBuffer::IndexFormat indexFormat)
4603 const bool indexed = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4606 && slot.generation == icbD->contentsGeneration()
4607 && slot.primitiveType == primitiveType
4608 && (!indexed || (slot.indexBuf == indexBufMtl
4609 && slot.indexOffset == indexOffset
4610 && slot.indexFormat == indexFormat));
4616 MTLPrimitiveType primitiveType,
4617 id<MTLBuffer> indexBufMtl, quint32 indexOffset,
4618 QRhiCommandBuffer::IndexFormat indexFormat)
4620 const bool indexed = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4622 if (qrhimtl_icbSlotMatches(icbD, slot, primitiveType, indexBufMtl, indexOffset, indexFormat))
4625 const quint32 count = icbD->recordedCommandCount();
4627 const MTLIndexType indexType = indexFormat == QRhiCommandBuffer::IndexUInt16
4628 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32;
4629 const quint32 indexSize = indexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4;
4631 for (quint32 i = 0; i < count; ++i) {
4633 id<MTLIndirectRenderCommand> rc = [slot.icb indirectRenderCommandAtIndex:i];
4634 [rc drawIndexedPrimitives:primitiveType
4635 indexCount:c.indexCount
4637 indexBuffer:indexBufMtl
4638 indexBufferOffset:indexOffset + c.firstIndex * indexSize
4639 instanceCount:c.instanceCount
4640 baseVertex:c.vertexOffset
4641 baseInstance:c.firstInstance];
4644 const QRhiIndirectDrawCommand *cmds = icbD->drawCommands();
4645 for (quint32 i = 0; i < count; ++i) {
4646 const QRhiIndirectDrawCommand &c(cmds[i]);
4647 id<MTLIndirectRenderCommand> rc = [slot.icb indirectRenderCommandAtIndex:i];
4648 [rc drawPrimitives:primitiveType
4649 vertexStart:c.firstVertex
4650 vertexCount:c.vertexCount
4651 instanceCount:c.instanceCount
4652 baseInstance:c.firstInstance];
4657 if (count < icbD->maxCommandCount())
4658 [slot.icb resetWithRange:NSMakeRange(count, icbD->maxCommandCount() - count)];
4660 slot.generation = icbD->contentsGeneration();
4661 slot.primitiveType = primitiveType;
4662 slot.indexBuf = indexBufMtl;
4663 slot.indexOffset = indexOffset;
4664 slot.indexFormat = indexFormat;
4670 quint32 firstCommand, quint32 count,
4671 int currentFrameSlot)
4675 if (icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws) {
4679 id<MTLBuffer> indexBufMtl = indexBufD
->d->buf[indexBufD
->d->slotted ? currentFrameSlot : 0];
4680 const MTLIndexType indexType = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16
4681 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32;
4682 const quint32 indexSize = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4;
4684 for (quint32 i = 0; i < count; ++i) {
4686 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: primitiveType
4687 indexCount: c.indexCount
4688 indexType: indexType
4689 indexBuffer: indexBufMtl
4690 indexBufferOffset: cbD->currentIndexOffset + c.firstIndex * indexSize
4691 instanceCount: c.instanceCount
4692 baseVertex: c.vertexOffset
4693 baseInstance: c.firstInstance];
4696 const QRhiIndirectDrawCommand *cmds = icbD->drawCommands();
4697 for (quint32 i = 0; i < count; ++i) {
4698 const QRhiIndirectDrawCommand &c(cmds[firstCommand + i]);
4699 [cbD->d->currentRenderPassEncoder drawPrimitives: primitiveType
4700 vertexStart: c.firstVertex
4701 vertexCount: c.vertexCount
4702 instanceCount: c.instanceCount
4703 baseInstance: c.firstInstance];
4709 quint32 firstCommand, quint32 commandCount)
4717 const bool indexed = icb->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4721 qWarning(
"executeIndirect: the indirect command buffer was built for a different "
4722 "topology than the current graphics pipeline uses; skipping");
4727 const quint32 total = icbD->commandCount();
4728 const bool deviceCount = icbD
->d->buildInfo.countBuffer !=
nullptr;
4729 NSRange range = NSMakeRange(0, total);
4735 if (firstCommand != 0 || commandCount < total) {
4736 qWarning(
"executeIndirect: firstCommand and commandCount cannot be honoured "
4737 "together with a device-side count; executing all %u command(s)", total);
4740 if (firstCommand >= total)
4742 const quint32 count = qMin(commandCount, total - firstCommand);
4745 range = NSMakeRange(firstCommand, count);
4748 if (indexed && icbD
->d->buildInfo.indexBuffer) {
4750 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4751 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
4752 usage:MTLResourceUsageRead
4753 stages:MTLRenderStageVertex | MTLRenderStageFragment];
4756 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb
4757 indirectBuffer:slot.rangeBuffer
4758 indirectBufferOffset:0];
4760 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb withRange:range];
4766 const QRhiIndirectCommandBufferBuildInfo &info(icbD
->d->buildInfo);
4767 if (info.countBuffer) {
4768 qWarning(
"executeIndirect: a device-side count needs an indirect command buffer, "
4769 "which is not available here; skipping");
4772 const quint32 canonicalStride = indexed ?
sizeof(QRhiIndexedIndirectDrawCommand)
4773 :
sizeof(QRhiIndirectDrawCommand);
4774 const quint32 stride = info.stride ? info.stride : canonicalStride;
4775 const quint32 total = icbD->commandCount();
4776 if (firstCommand >= total)
4778 const quint32 count = qMin(commandCount, total - firstCommand);
4781 const quint32 offset = info.sourceBufferOffset + firstCommand * stride;
4783 drawIndexedIndirect(cb, info.sourceBuffer, offset, count, stride);
4785 drawIndirect(cb, info.sourceBuffer, offset, count, stride);
4789 const quint32 total = icbD->recordedCommandCount();
4790 if (firstCommand >= total)
4792 const quint32 count = qMin(commandCount, total - firstCommand);
4799 qrhimtl_replayIcbOnCpu(cbD, icbD, firstCommand, count, currentFrameSlot);
4804 id<MTLBuffer> indexBufMtl = nil;
4805 quint32 indexOffset = 0;
4811 indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4812 indexOffset = cbD->currentIndexOffset;
4816 if (slot.usedInFrameId == d->globalFrameId
4817 && !qrhimtl_icbSlotMatches(icbD, slot, primitiveType, indexBufMtl, indexOffset,
4818 cbD->currentIndexFormat))
4823 qWarning(
"executeIndirect: the same indirect command buffer is executed more than once "
4824 "in a frame, with different contents, topology or index buffer state; "
4825 "falling back to individual draw calls");
4826 qrhimtl_replayIcbOnCpu(cbD, icbD, firstCommand, count, currentFrameSlot);
4830 qrhimtl_encodeIcbFromCpu(icbD, slot, primitiveType,
4831 indexBufMtl, indexOffset, cbD->currentIndexFormat);
4836 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
4837 usage:MTLResourceUsageRead
4838 stages:MTLRenderStageVertex | MTLRenderStageFragment];
4840 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb
4841 withRange:NSMakeRange(firstCommand, count)];
4842 slot.usedInFrameId =
d->globalFrameId;
4846 const QRhiIndirectCommandBufferBuildInfo &info)
4854 const bool indexed = icb->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4856 if (indexed && !info.indexBuffer) {
4857 qWarning(
"buildIndirect: an IndexedDraws indirect command buffer needs an "
4858 "index buffer in QRhiIndirectCommandBufferBuildInfo; skipping");
4862 quint32 count = info.commandCount ? info.commandCount : icbD->m_maxCommandCount;
4863 if (count > icbD->m_maxCommandCount) {
4864 qWarning(
"QRhiIndirectCommandBuffer: buildIndirect() with commandCount %u exceeds "
4865 "maxCommandCount %u; clamping", count, icbD->m_maxCommandCount);
4866 count = icbD->m_maxCommandCount;
4868 icbD->m_gpuBuilt =
true;
4869 icbD->m_gpuBuiltCommandCount = count;
4874 icbD
->d->buildInfo = info;
4884 id<MTLBuffer> srcBufMtl = srcBufD->d->buf[srcBufD->d->slotted ? currentFrameSlot : 0];
4886 id<MTLBuffer> indexBufMtl = nil;
4890 indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4893 id<MTLBuffer> countBufMtl = nil;
4894 if (info.countBuffer) {
4898 countBufMtl = countBufD->d->buf[countBufD->d->slotted ? currentFrameSlot : 0];
4901 const quint32 stride = info.stride ? info.stride
4902 : (indexed ?
sizeof(QRhiIndexedIndirectDrawCommand)
4903 :
sizeof(QRhiIndirectDrawCommand));
4907 id<MTLComputeCommandEncoder> computeEncoder = [cbD->d->cb computeCommandEncoder];
4908 encodeIcbWithCompute(
d, computeEncoder, slot.icb, slot.argBuffer, slot.rangeBuffer,
4909 indexed, info.indexFormat,
4910 toMetalPrimitiveType(info.topology),
4911 srcBufMtl, info.sourceBufferOffset,
4912 indexBufMtl, info.indexBufferOffset,
4913 countBufMtl, info.countBufferOffset,
4914 icbD->commandCount(), stride);
4915 [computeEncoder endEncoding];
4919 icbD
->d->buildInfo = info;
4927 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4928 [e.buffer.buffers[i] release];
4933 [e.renderbuffer.texture release];
4938 [e.texture.texture release];
4939 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4940 [e.texture.stagingBuffers[i] release];
4941 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
4942 [e.texture.views[i] release];
4943 [e.texture.samplingView release];
4944 [e.texture.writeView release];
4949 [e.sampler.samplerState release];
4954 qsizetype keepBegin =
d->releaseQueue.size();
4955 for (qsizetype i = keepBegin - 1; i >= 0; --i) {
4957 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
4971 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
4972 [e.stagingBuffer.buffer release];
4974 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
4975 [e.graphicsPipeline.pipelineState release];
4976 [e.graphicsPipeline.depthStencilState release];
4977 [e.graphicsPipeline.tessVertexComputeState[0] release];
4978 [e.graphicsPipeline.tessVertexComputeState[1] release];
4979 [e.graphicsPipeline.tessVertexComputeState[2] release];
4980 [e.graphicsPipeline.tessTessControlComputeState release];
4982 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
4983 [e.computePipeline.pipelineState release];
4985 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
4986 [e.shadingRateMap.rateMap release];
4988 case QRhiMetalData::DeferredReleaseEntry::StagingIcbBuffer:
4989 [e.stagingIcbBuffer.icb release];
4990 [e.stagingIcbBuffer.argBuffer release];
4995 }
else if (--keepBegin != i) {
4996 d->releaseQueue[keepBegin] =
std::move(
d->releaseQueue[i]);
5000 d->releaseQueue.remove(0, keepBegin);
5005 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
5007 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
5009 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
5010 readback.result->format = readback.format;
5011 readback.result->pixelSize = readback.pixelSize;
5012 readback.result->data.resize(
int(readback.bufSize));
5013 const void *p = [readback.buf contents];
5014 if (readback.depthPlaneByteSize) {
5022 const quint32 pixelCount = quint32(readback.pixelSize.width()) * quint32(readback.pixelSize.height());
5023 const char *depthPlane =
static_cast<
const char *>(p);
5024 const quint8 *stencilPlane =
reinterpret_cast<
const quint8 *>(depthPlane + readback.depthPlaneByteSize);
5025 char *dst = readback.result->data.data();
5026 for (quint32 px = 0; px < pixelCount; ++px) {
5027 const quint32 depthWord = qFromUnaligned<quint32>(depthPlane + px * 4);
5028 const float depthF = readback
.depthPlaneIsFloat ? qFromUnaligned<
float>(depthPlane + px * 4) : 0.0f;
5029 const quint32 stencil = readback.stencilPlaneByteSize ? stencilPlane[px] : 0;
5030 switch (readback.format) {
5031 case QRhiTexture::D16:
5032 qToUnaligned(quint16(qRound(qBound(0.0f, depthF, 1.0f) * 65535.0f)), dst + px * 2);
5034 case QRhiTexture::D32FS8:
5035 qToUnaligned(depthWord, dst + px * 8);
5036 qToUnaligned(stencil, dst + px * 8 + 4);
5041 ? quint32(qBound(0.0f, depthF, 1.0f) * 16777215.0f + 0.5f)
5042 : depthWord & 0x00FFFFFFu;
5043 qToUnaligned((depth24 << 8) | stencil, dst + px * 4);
5049 memcpy(readback.result->data.data(), p, readback.bufSize);
5051 [readback.buf release];
5053 if (readback.result->completed)
5054 completedCallbacks.append(readback.result->completed);
5056 d->activeTextureReadbacks.remove(i);
5060 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
5062 if (forced || currentFrameSlot == readback.activeFrameSlot
5063 || readback.activeFrameSlot < 0) {
5064 readback.result->data.resize(readback.readSize);
5065 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
5067 memcpy(readback.result->data.data(), p, size_t(readback.readSize));
5068 [readback.buf release];
5070 if (readback.result->completed)
5071 completedCallbacks.append(readback.result->completed);
5073 d->activeBufferReadbacks.remove(i);
5077 for (
auto f : completedCallbacks)
5105 e.buffer.buffers[i] =
d->buf[i];
5107 d->pendingUpdates[i].clear();
5112 rhiD
->d->releaseQueue.append(e);
5113 rhiD->unregisterResource(
this);
5122 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
5123 qWarning(
"StorageBuffer cannot be combined with Dynamic");
5127 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
5128 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
5132 MTLResourceOptions opts = MTLResourceStorageModeShared;
5136 const bool internalHostWritable = (
int(m_usage) & (WorkBufPoolUsage | InternalHostWritable)) != 0;
5138 if (m_type != Dynamic && !internalHostWritable && rhiD->caps.usePrivateStaticBuffers) {
5139 opts = MTLResourceStorageModePrivate;
5144 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
5145 opts = MTLResourceStorageModeManaged;
5152 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer)
5153 && !internalHostWritable;
5158 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
5159 if (!m_objectName.isEmpty()) {
5161 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
5163 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
5164 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
5172 rhiD->registerResource(
this);
5180 Q_ASSERT(
sizeof(b.objects) /
sizeof(b.objects[0]) >= size_t(QMTL_FRAMES_IN_FLIGHT));
5184 b.objects[i] = &
d->buf[i];
5186 b.slotCount = QMTL_FRAMES_IN_FLIGHT;
5193 return { { &
d->buf[0] }, 1 };
5203 Q_ASSERT(m_type == Dynamic);
5206 Q_ASSERT(rhiD->inFrame);
5207 const int slot =
d->slotted ? rhiD->currentFrameSlot : 0;
5208 void *p = [d->buf[slot] contents];
5209 return static_cast<
char *>(p);
5224 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
5226 case QRhiTexture::RGBA8:
5227 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
5228 case QRhiTexture::BGRA8:
5229 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
5230 case QRhiTexture::R8:
5232 return MTLPixelFormatR8Unorm;
5234 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
5236 case QRhiTexture::R8SI:
5237 return MTLPixelFormatR8Sint;
5238 case QRhiTexture::R8UI:
5239 return MTLPixelFormatR8Uint;
5240 case QRhiTexture::RG8:
5242 return MTLPixelFormatRG8Unorm;
5244 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
5246 case QRhiTexture::R16:
5247 return MTLPixelFormatR16Unorm;
5248 case QRhiTexture::RG16:
5249 return MTLPixelFormatRG16Unorm;
5250 case QRhiTexture::RED_OR_ALPHA8:
5251 return MTLPixelFormatR8Unorm;
5253 case QRhiTexture::RGBA16F:
5254 return MTLPixelFormatRGBA16Float;
5255 case QRhiTexture::RGBA32F:
5256 return MTLPixelFormatRGBA32Float;
5257 case QRhiTexture::R16F:
5258 return MTLPixelFormatR16Float;
5259 case QRhiTexture::R32F:
5260 return MTLPixelFormatR32Float;
5262 case QRhiTexture::RGB10A2:
5263 return MTLPixelFormatRGB10A2Unorm;
5265 case QRhiTexture::R32SI:
5266 return MTLPixelFormatR32Sint;
5267 case QRhiTexture::R32UI:
5268 return MTLPixelFormatR32Uint;
5269 case QRhiTexture::RG32SI:
5270 return MTLPixelFormatRG32Sint;
5271 case QRhiTexture::RG32UI:
5272 return MTLPixelFormatRG32Uint;
5273 case QRhiTexture::RGBA32SI:
5274 return MTLPixelFormatRGBA32Sint;
5275 case QRhiTexture::RGBA32UI:
5276 return MTLPixelFormatRGBA32Uint;
5279 case QRhiTexture::D16:
5280 return MTLPixelFormatDepth16Unorm;
5281 case QRhiTexture::D24:
5282 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
5283 case QRhiTexture::D24S8:
5284 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
5286 case QRhiTexture::D16:
5287 return MTLPixelFormatDepth32Float;
5288 case QRhiTexture::D24:
5289 return MTLPixelFormatDepth32Float;
5290 case QRhiTexture::D24S8:
5291 return MTLPixelFormatDepth32Float_Stencil8;
5293 case QRhiTexture::D32F:
5294 return MTLPixelFormatDepth32Float;
5295 case QRhiTexture::D32FS8:
5296 return MTLPixelFormatDepth32Float_Stencil8;
5299 case QRhiTexture::BC1:
5300 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
5301 case QRhiTexture::BC2:
5302 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
5303 case QRhiTexture::BC3:
5304 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
5305 case QRhiTexture::BC4:
5306 return MTLPixelFormatBC4_RUnorm;
5307 case QRhiTexture::BC5:
5308 qWarning(
"QRhiMetal does not support BC5");
5309 return MTLPixelFormatInvalid;
5310 case QRhiTexture::BC6H:
5311 return MTLPixelFormatBC6H_RGBUfloat;
5312 case QRhiTexture::BC7:
5313 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
5315 case QRhiTexture::BC1:
5316 case QRhiTexture::BC2:
5317 case QRhiTexture::BC3:
5318 case QRhiTexture::BC4:
5319 case QRhiTexture::BC5:
5320 case QRhiTexture::BC6H:
5321 case QRhiTexture::BC7:
5322 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
5323 return MTLPixelFormatInvalid;
5327 case QRhiTexture::ETC2_RGB8:
5328 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
5329 case QRhiTexture::ETC2_RGB8A1:
5330 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
5331 case QRhiTexture::ETC2_RGBA8:
5332 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
5334 case QRhiTexture::ASTC_4x4:
5335 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
5336 case QRhiTexture::ASTC_5x4:
5337 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
5338 case QRhiTexture::ASTC_5x5:
5339 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
5340 case QRhiTexture::ASTC_6x5:
5341 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
5342 case QRhiTexture::ASTC_6x6:
5343 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
5344 case QRhiTexture::ASTC_8x5:
5345 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
5346 case QRhiTexture::ASTC_8x6:
5347 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
5348 case QRhiTexture::ASTC_8x8:
5349 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
5350 case QRhiTexture::ASTC_10x5:
5351 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
5352 case QRhiTexture::ASTC_10x6:
5353 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
5354 case QRhiTexture::ASTC_10x8:
5355 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
5356 case QRhiTexture::ASTC_10x10:
5357 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
5358 case QRhiTexture::ASTC_12x10:
5359 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
5360 case QRhiTexture::ASTC_12x12:
5361 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
5363 case QRhiTexture::ETC2_RGB8:
5364 if (d->caps.isAppleGPU)
5365 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
5366 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5367 return MTLPixelFormatInvalid;
5368 case QRhiTexture::ETC2_RGB8A1:
5369 if (d->caps.isAppleGPU)
5370 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
5371 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5372 return MTLPixelFormatInvalid;
5373 case QRhiTexture::ETC2_RGBA8:
5374 if (d->caps.isAppleGPU)
5375 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
5376 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5377 return MTLPixelFormatInvalid;
5378 case QRhiTexture::ASTC_4x4:
5379 if (d->caps.isAppleGPU)
5380 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
5381 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5382 return MTLPixelFormatInvalid;
5383 case QRhiTexture::ASTC_5x4:
5384 if (d->caps.isAppleGPU)
5385 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
5386 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5387 return MTLPixelFormatInvalid;
5388 case QRhiTexture::ASTC_5x5:
5389 if (d->caps.isAppleGPU)
5390 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
5391 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5392 return MTLPixelFormatInvalid;
5393 case QRhiTexture::ASTC_6x5:
5394 if (d->caps.isAppleGPU)
5395 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
5396 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5397 return MTLPixelFormatInvalid;
5398 case QRhiTexture::ASTC_6x6:
5399 if (d->caps.isAppleGPU)
5400 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
5401 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5402 return MTLPixelFormatInvalid;
5403 case QRhiTexture::ASTC_8x5:
5404 if (d->caps.isAppleGPU)
5405 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
5406 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5407 return MTLPixelFormatInvalid;
5408 case QRhiTexture::ASTC_8x6:
5409 if (d->caps.isAppleGPU)
5410 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
5411 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5412 return MTLPixelFormatInvalid;
5413 case QRhiTexture::ASTC_8x8:
5414 if (d->caps.isAppleGPU)
5415 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
5416 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5417 return MTLPixelFormatInvalid;
5418 case QRhiTexture::ASTC_10x5:
5419 if (d->caps.isAppleGPU)
5420 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
5421 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5422 return MTLPixelFormatInvalid;
5423 case QRhiTexture::ASTC_10x6:
5424 if (d->caps.isAppleGPU)
5425 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
5426 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5427 return MTLPixelFormatInvalid;
5428 case QRhiTexture::ASTC_10x8:
5429 if (d->caps.isAppleGPU)
5430 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
5431 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5432 return MTLPixelFormatInvalid;
5433 case QRhiTexture::ASTC_10x10:
5434 if (d->caps.isAppleGPU)
5435 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
5436 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5437 return MTLPixelFormatInvalid;
5438 case QRhiTexture::ASTC_12x10:
5439 if (d->caps.isAppleGPU)
5440 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
5441 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5442 return MTLPixelFormatInvalid;
5443 case QRhiTexture::ASTC_12x12:
5444 if (d->caps.isAppleGPU)
5445 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
5446 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5447 return MTLPixelFormatInvalid;
5452 return MTLPixelFormatInvalid;
5457 int sampleCount, QRhiRenderBuffer::Flags flags,
5458 QRhiTexture::Format backingFormatHint)
5479 e.renderbuffer.texture =
d->tex;
5484 rhiD
->d->releaseQueue.append(e);
5485 rhiD->unregisterResource(
this);
5494 if (m_pixelSize.isEmpty())
5498 samples = rhiD->effectiveSampleCount(m_sampleCount);
5500 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
5501 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
5502 desc.width = NSUInteger(m_pixelSize.width());
5503 desc.height = NSUInteger(m_pixelSize.height());
5505 desc.sampleCount = NSUInteger(
samples);
5506 desc.resourceOptions = MTLResourceStorageModePrivate;
5507 desc.usage = MTLTextureUsageRenderTarget;
5512 const bool canBeMemoryless = !m_flags.testFlag(QRhiRenderBuffer::NoTransientBacking);
5517 if (rhiD->caps.isAppleGPU && canBeMemoryless) {
5518 desc.storageMode = MTLStorageModeMemoryless;
5519 d->format = MTLPixelFormatDepth32Float_Stencil8;
5521 desc.storageMode = MTLStorageModePrivate;
5522 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
5523 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
5526 desc.storageMode = canBeMemoryless ? MTLStorageModeMemoryless : MTLStorageModePrivate;
5527 d->format = MTLPixelFormatDepth32Float_Stencil8;
5529 desc.pixelFormat =
d->format;
5532 desc.storageMode = MTLStorageModePrivate;
5533 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
5534 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
5536 d->format = MTLPixelFormatRGBA8Unorm;
5537 desc.pixelFormat =
d->format;
5544 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
5547 if (!m_objectName.isEmpty())
5548 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
5552 rhiD->registerResource(
this);
5558 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
5559 return m_backingFormatHint;
5561 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
5565 int arraySize,
int sampleCount, Flags flags)
5570 d->stagingBuf[i] = nil;
5572 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
5573 d->perLevelViews[i] = nil;
5591 e.texture.texture =
d->owns ? d->tex : nil;
5595 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
5596 d->stagingBuf[i] = nil;
5599 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
5600 e.texture.views[i] =
d->perLevelViews[i];
5601 d->perLevelViews[i] = nil;
5604 e.texture.samplingView =
d->samplingView;
5605 d->samplingView = nil;
5606 e.texture.writeView =
d->writeView;
5611 rhiD
->d->releaseQueue.append(e);
5612 rhiD->unregisterResource(
this);
5621 const bool isCube = m_flags.testFlag(CubeMap);
5622 const bool is3D = m_flags.testFlag(ThreeDimensional);
5623 const bool isArray = m_flags.testFlag(TextureArray);
5624 const bool hasMipMaps = m_flags.testFlag(MipMapped);
5625 const bool is1D = m_flags.testFlag(OneDimensional);
5627 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
5628 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
5631 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
5632 if (m_writeViewFormat.format != UnknownFormat) {
5633 d->viewFormat = toMetalTextureFormat(m_writeViewFormat.format,
5634 m_writeViewFormat.srgb ? sRGB : Flags(), rhiD);
5636 d->viewFormat =
d->format;
5638 if (m_readViewFormat.format != UnknownFormat) {
5639 d->viewFormatForSampling = toMetalTextureFormat(m_readViewFormat.format,
5640 m_readViewFormat.srgb ? sRGB : Flags(), rhiD);
5642 d->viewFormatForSampling =
d->format;
5644 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
5645 samples = rhiD->effectiveSampleCount(m_sampleCount);
5648 qWarning(
"Cubemap texture cannot be multisample");
5652 qWarning(
"3D texture cannot be multisample");
5656 qWarning(
"Multisample texture cannot have mipmaps");
5660 if (isCube && is3D) {
5661 qWarning(
"Texture cannot be both cube and 3D");
5664 if (isArray && is3D) {
5665 qWarning(
"Texture cannot be both array and 3D");
5669 qWarning(
"Texture cannot be both 1D and 3D");
5672 if (is1D && isCube) {
5673 qWarning(
"Texture cannot be both 1D and cube");
5676 if (m_depth > 1 && !is3D) {
5677 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
5680 if (m_arraySize > 0 && !isArray) {
5681 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
5684 if (m_arraySize < 1 && isArray) {
5685 qWarning(
"Texture is an array but array size is %d", m_arraySize);
5689 if (!rhiD->textureFormatInfo(m_format, size,
nullptr,
nullptr,
nullptr))
5693 *adjustedSize = size;
5701 if (!prepareCreate(&size))
5704 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
5706 const bool isCube = m_flags.testFlag(CubeMap);
5707 const bool is3D = m_flags.testFlag(ThreeDimensional);
5708 const bool isArray = m_flags.testFlag(TextureArray);
5709 const bool is1D = m_flags.testFlag(OneDimensional);
5711 desc.textureType = MTLTextureTypeCube;
5713 desc.textureType = MTLTextureType3D;
5715 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
5716 }
else if (isArray) {
5717 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
5719 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
5721 desc.pixelFormat =
d->format;
5722 desc.width = NSUInteger(size.width());
5723 desc.height = NSUInteger(size.height());
5724 desc.depth = is3D ? qMax(1, m_depth) : 1;
5727 desc.sampleCount = NSUInteger(
samples);
5729 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
5730 desc.resourceOptions = MTLResourceStorageModePrivate;
5731 desc.storageMode = MTLStorageModePrivate;
5732 desc.usage = MTLTextureUsageShaderRead;
5733 if (m_flags.testFlag(RenderTarget))
5734 desc.usage |= MTLTextureUsageRenderTarget;
5735 if (m_flags.testFlag(UsedWithLoadStore))
5736 desc.usage |= MTLTextureUsageShaderWrite;
5742 const bool writeViewChangesFormat = m_writeViewFormat.format != UnknownFormat
5743 && m_writeViewFormat.format != m_format;
5744 const bool readViewChangesFormat = m_readViewFormat.format != UnknownFormat
5745 && m_readViewFormat.format != m_format;
5746 if (writeViewChangesFormat || readViewChangesFormat)
5747 desc.usage |= MTLTextureUsagePixelFormatView;
5750 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
5753 if (!m_objectName.isEmpty())
5754 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
5763 rhiD->registerResource(
this);
5769 id<MTLTexture> tex = id<MTLTexture>(src.object);
5773 if (!prepareCreate())
5786 rhiD->registerResource(
this);
5792 return {quint64(
d->tex), 0};
5797 Q_ASSERT(!samplingView && !writeView);
5799 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
5800 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
5801 const bool hasArrayRange = isArray &&
q->m_arrayRangeStart >= 0 &&
q->m_arrayRangeLength >= 0;
5802 const NSUInteger sliceCount = isCube ? 6 : (isArray ? NSUInteger(qMax(0,
q->m_arraySize)) : 1);
5804 const MTLTextureType type = [tex textureType];
5806 id<MTLTexture> newSamplingView = nil;
5807 id<MTLTexture> newWriteView = nil;
5809 if (viewFormatForSampling != format || hasArrayRange) {
5810 const NSRange slices = hasArrayRange
5811 ? NSMakeRange(NSUInteger(
q->m_arrayRangeStart), NSUInteger(
q->m_arrayRangeLength))
5812 : NSMakeRange(0, sliceCount);
5813 newSamplingView = [tex newTextureViewWithPixelFormat: viewFormatForSampling
5814 textureType: type levels: levels slices: slices];
5815 if (!newSamplingView) {
5816 qWarning(
"QRhiMetal: Failed to create texture view used for sampling");
5821 if (viewFormat != format) {
5822 newWriteView = [tex newTextureViewWithPixelFormat: viewFormat
5823 textureType: type levels: levels
5824 slices: NSMakeRange(0, sliceCount)];
5825 if (!newWriteView) {
5826 qWarning(
"QRhiMetal: Failed to create texture view used for rendering");
5827 [newSamplingView release];
5832 samplingView = newSamplingView;
5833 writeView = newWriteView;
5840 if (perLevelViews[level])
5841 return perLevelViews[level];
5843 const MTLTextureType type = [tex textureType];
5844 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
5845 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
5846 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: viewFormat textureType: type
5847 levels: NSMakeRange(NSUInteger(level), 1)
5848 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
5850 perLevelViews[level] = view;
5855 AddressMode u, AddressMode v, AddressMode w)
5869 if (!
d->samplerState)
5876 e.sampler.samplerState =
d->samplerState;
5877 d->samplerState = nil;
5881 rhiD
->d->releaseQueue.append(e);
5882 rhiD->unregisterResource(
this);
5889 case QRhiSampler::Nearest:
5890 return MTLSamplerMinMagFilterNearest;
5891 case QRhiSampler::Linear:
5892 return MTLSamplerMinMagFilterLinear;
5895 return MTLSamplerMinMagFilterNearest;
5902 case QRhiSampler::None:
5903 return MTLSamplerMipFilterNotMipmapped;
5904 case QRhiSampler::Nearest:
5905 return MTLSamplerMipFilterNearest;
5906 case QRhiSampler::Linear:
5907 return MTLSamplerMipFilterLinear;
5910 return MTLSamplerMipFilterNotMipmapped;
5917 case QRhiSampler::Repeat:
5918 return MTLSamplerAddressModeRepeat;
5919 case QRhiSampler::ClampToEdge:
5920 return MTLSamplerAddressModeClampToEdge;
5921 case QRhiSampler::Mirror:
5922 return MTLSamplerAddressModeMirrorRepeat;
5925 return MTLSamplerAddressModeClampToEdge;
5932 case QRhiSampler::Never:
5933 return MTLCompareFunctionNever;
5934 case QRhiSampler::Less:
5935 return MTLCompareFunctionLess;
5936 case QRhiSampler::Equal:
5937 return MTLCompareFunctionEqual;
5938 case QRhiSampler::LessOrEqual:
5939 return MTLCompareFunctionLessEqual;
5940 case QRhiSampler::Greater:
5941 return MTLCompareFunctionGreater;
5942 case QRhiSampler::NotEqual:
5943 return MTLCompareFunctionNotEqual;
5944 case QRhiSampler::GreaterOrEqual:
5945 return MTLCompareFunctionGreaterEqual;
5946 case QRhiSampler::Always:
5947 return MTLCompareFunctionAlways;
5950 return MTLCompareFunctionNever;
5956 if (
d->samplerState)
5959 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
5960 desc.minFilter = toMetalFilter(m_minFilter);
5961 desc.magFilter = toMetalFilter(m_magFilter);
5962 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
5963 desc.sAddressMode = toMetalAddressMode(m_addressU);
5964 desc.tAddressMode = toMetalAddressMode(m_addressV);
5965 desc.rAddressMode = toMetalAddressMode(m_addressW);
5966 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
5972 desc.supportArgumentBuffers = rhiD->caps.indirectCommandBuffers ? YES : NO;
5973 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
5975 if (!
d->samplerState) {
5979 qWarning(
"Failed to create Metal sampler state. The number of unique sampler "
5980 "states with argument buffer support may have exceeded the limit of %u.",
5981 uint(rhiD
->d->dev.maxArgumentBufferSamplerCount));
5987 rhiD->registerResource(
this);
6012 e.shadingRateMap.rateMap =
d->rateMap;
6017 rhiD
->d->releaseQueue.append(e);
6018 rhiD->unregisterResource(
this);
6027 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
6031 [d->rateMap retain];
6036 rhiD->registerResource(
this);
6045 const MTLSize screenSize = [d->rateMap screenSize];
6046 return QSize(
int(screenSize.width),
int(screenSize.height));
6054 serializedFormatData.reserve(16);
6066 rhiD->unregisterResource(
this);
6100 serializedFormatData.clear();
6101 auto p =
std::back_inserter(serializedFormatData);
6123 rhiD->registerResource(rpD,
false);
6129 return serializedFormatData;
6151 return d->pixelSize;
6165 const QRhiTextureRenderTargetDescription &desc,
6182 rhiD->unregisterResource(
this);
6187 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
6192 for (
int i = 0; i < colorAttachmentCount; ++i) {
6193 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
6199 if (m_desc.depthTexture())
6201 else if (m_desc.depthStencilBuffer())
6202 rpD
->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
6209 rhiD->registerResource(rpD,
false);
6216 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
6217 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
6218 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
6222 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
6226 Q_ASSERT(texD || rbD);
6227 id<MTLTexture> dst = nil;
6230 dst = texD
->d->textureForWrite();
6231 if (attIndex == 0) {
6232 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
6235 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
6238 if (attIndex == 0) {
6239 d->pixelSize = rbD->pixelSize();
6246 colorAtt
.slice = is3D ? it->layer() : 0;
6247 colorAtt
.level = it->level();
6249 colorAtt.resolveTex = resTexD ? resTexD->d->textureForWrite() : nil;
6252 d->fb.colorAtt[attIndex] = colorAtt;
6256 if (hasDepthStencil) {
6257 if (m_desc.depthTexture()) {
6259 d->fb.dsTex = depthTexD
->d->textureForWrite();
6260 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
6261 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
6262 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
6264 d->pixelSize = depthTexD->pixelSize();
6269 d->fb.dsTex = depthRbD
->d->tex;
6270 d->fb.hasStencil =
true;
6271 d->fb.depthNeedsStore =
false;
6272 d->fb.preserveDs =
false;
6274 d->pixelSize = depthRbD->pixelSize();
6278 if (m_desc.depthResolveTexture()) {
6280 d->fb.dsResolveTex = depthResolveTexD
->d->textureForWrite();
6288 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
6290 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
6292 rhiD->registerResource(
this,
false);
6298 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
6301 return d->pixelSize;
6326 sortedBindings.clear();
6331 rhiD->unregisterResource(
this);
6336 if (!sortedBindings.isEmpty())
6340 if (!rhiD->sanityCheckShaderResourceBindings(
this))
6343 rhiD->updateLayoutDesc(
this);
6345 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
6346 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
6347 if (!sortedBindings.isEmpty())
6348 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
6352 boundResourceData.resize(sortedBindings.count());
6354 for (BoundResourceData &bd : boundResourceData)
6358 rhiD->registerResource(
this,
false);
6364 sortedBindings.clear();
6365 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
6366 if (!flags.testFlag(BindingsAreSorted))
6367 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
6369 for (BoundResourceData &bd : boundResourceData)
6396 d->tess.compVs[0].destroy();
6397 d->tess.compVs[1].destroy();
6398 d->tess.compVs[2].destroy();
6400 d->tess.compTesc.destroy();
6401 d->tess.vertTese.destroy();
6403 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
6404 d->extraBufMgr.deviceLocalWorkBuffers.clear();
6405 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
6406 d->extraBufMgr.hostVisibleWorkBuffers.clear();
6411 if (!
d->ps && !
d->ds
6412 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
6413 && !
d->tess.tessControlComputeState)
6421 e.graphicsPipeline.pipelineState =
d->ps;
6422 e.graphicsPipeline.depthStencilState =
d->ds;
6423 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
6424 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
6427 d->tess.vertexComputeState = {};
6428 d->tess.tessControlComputeState = nil;
6432 rhiD
->d->releaseQueue.append(e);
6433 rhiD->unregisterResource(
this);
6440 case QRhiVertexInputAttribute::Float4:
6441 return MTLVertexFormatFloat4;
6442 case QRhiVertexInputAttribute::Float3:
6443 return MTLVertexFormatFloat3;
6444 case QRhiVertexInputAttribute::Float2:
6445 return MTLVertexFormatFloat2;
6446 case QRhiVertexInputAttribute::Float:
6447 return MTLVertexFormatFloat;
6448 case QRhiVertexInputAttribute::UNormByte4:
6449 return MTLVertexFormatUChar4Normalized;
6450 case QRhiVertexInputAttribute::UNormByte2:
6451 return MTLVertexFormatUChar2Normalized;
6452 case QRhiVertexInputAttribute::UNormByte:
6453 return MTLVertexFormatUCharNormalized;
6454 case QRhiVertexInputAttribute::UInt4:
6455 return MTLVertexFormatUInt4;
6456 case QRhiVertexInputAttribute::UInt3:
6457 return MTLVertexFormatUInt3;
6458 case QRhiVertexInputAttribute::UInt2:
6459 return MTLVertexFormatUInt2;
6460 case QRhiVertexInputAttribute::UInt:
6461 return MTLVertexFormatUInt;
6462 case QRhiVertexInputAttribute::SInt4:
6463 return MTLVertexFormatInt4;
6464 case QRhiVertexInputAttribute::SInt3:
6465 return MTLVertexFormatInt3;
6466 case QRhiVertexInputAttribute::SInt2:
6467 return MTLVertexFormatInt2;
6468 case QRhiVertexInputAttribute::SInt:
6469 return MTLVertexFormatInt;
6470 case QRhiVertexInputAttribute::Half4:
6471 return MTLVertexFormatHalf4;
6472 case QRhiVertexInputAttribute::Half3:
6473 return MTLVertexFormatHalf3;
6474 case QRhiVertexInputAttribute::Half2:
6475 return MTLVertexFormatHalf2;
6476 case QRhiVertexInputAttribute::Half:
6477 return MTLVertexFormatHalf;
6478 case QRhiVertexInputAttribute::UShort4:
6479 return MTLVertexFormatUShort4;
6480 case QRhiVertexInputAttribute::UShort3:
6481 return MTLVertexFormatUShort3;
6482 case QRhiVertexInputAttribute::UShort2:
6483 return MTLVertexFormatUShort2;
6484 case QRhiVertexInputAttribute::UShort:
6485 return MTLVertexFormatUShort;
6486 case QRhiVertexInputAttribute::SShort4:
6487 return MTLVertexFormatShort4;
6488 case QRhiVertexInputAttribute::SShort3:
6489 return MTLVertexFormatShort3;
6490 case QRhiVertexInputAttribute::SShort2:
6491 return MTLVertexFormatShort2;
6492 case QRhiVertexInputAttribute::SShort:
6493 return MTLVertexFormatShort;
6496 return MTLVertexFormatFloat4;
6503 case QRhiGraphicsPipeline::Zero:
6504 return MTLBlendFactorZero;
6505 case QRhiGraphicsPipeline::One:
6506 return MTLBlendFactorOne;
6507 case QRhiGraphicsPipeline::SrcColor:
6508 return MTLBlendFactorSourceColor;
6509 case QRhiGraphicsPipeline::OneMinusSrcColor:
6510 return MTLBlendFactorOneMinusSourceColor;
6511 case QRhiGraphicsPipeline::DstColor:
6512 return MTLBlendFactorDestinationColor;
6513 case QRhiGraphicsPipeline::OneMinusDstColor:
6514 return MTLBlendFactorOneMinusDestinationColor;
6515 case QRhiGraphicsPipeline::SrcAlpha:
6516 return MTLBlendFactorSourceAlpha;
6517 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
6518 return MTLBlendFactorOneMinusSourceAlpha;
6519 case QRhiGraphicsPipeline::DstAlpha:
6520 return MTLBlendFactorDestinationAlpha;
6521 case QRhiGraphicsPipeline::OneMinusDstAlpha:
6522 return MTLBlendFactorOneMinusDestinationAlpha;
6523 case QRhiGraphicsPipeline::ConstantColor:
6524 return MTLBlendFactorBlendColor;
6525 case QRhiGraphicsPipeline::ConstantAlpha:
6526 return MTLBlendFactorBlendAlpha;
6527 case QRhiGraphicsPipeline::OneMinusConstantColor:
6528 return MTLBlendFactorOneMinusBlendColor;
6529 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
6530 return MTLBlendFactorOneMinusBlendAlpha;
6531 case QRhiGraphicsPipeline::SrcAlphaSaturate:
6532 return MTLBlendFactorSourceAlphaSaturated;
6533 case QRhiGraphicsPipeline::Src1Color:
6534 return MTLBlendFactorSource1Color;
6535 case QRhiGraphicsPipeline::OneMinusSrc1Color:
6536 return MTLBlendFactorOneMinusSource1Color;
6537 case QRhiGraphicsPipeline::Src1Alpha:
6538 return MTLBlendFactorSource1Alpha;
6539 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
6540 return MTLBlendFactorOneMinusSource1Alpha;
6543 return MTLBlendFactorZero;
6550 case QRhiGraphicsPipeline::Add:
6551 return MTLBlendOperationAdd;
6552 case QRhiGraphicsPipeline::Subtract:
6553 return MTLBlendOperationSubtract;
6554 case QRhiGraphicsPipeline::ReverseSubtract:
6555 return MTLBlendOperationReverseSubtract;
6556 case QRhiGraphicsPipeline::Min:
6557 return MTLBlendOperationMin;
6558 case QRhiGraphicsPipeline::Max:
6559 return MTLBlendOperationMax;
6562 return MTLBlendOperationAdd;
6569 if (c.testFlag(QRhiGraphicsPipeline::R))
6570 f |= MTLColorWriteMaskRed;
6571 if (c.testFlag(QRhiGraphicsPipeline::G))
6572 f |= MTLColorWriteMaskGreen;
6573 if (c.testFlag(QRhiGraphicsPipeline::B))
6574 f |= MTLColorWriteMaskBlue;
6575 if (c.testFlag(QRhiGraphicsPipeline::A))
6576 f |= MTLColorWriteMaskAlpha;
6583 case QRhiGraphicsPipeline::Never:
6584 return MTLCompareFunctionNever;
6585 case QRhiGraphicsPipeline::Less:
6586 return MTLCompareFunctionLess;
6587 case QRhiGraphicsPipeline::Equal:
6588 return MTLCompareFunctionEqual;
6589 case QRhiGraphicsPipeline::LessOrEqual:
6590 return MTLCompareFunctionLessEqual;
6591 case QRhiGraphicsPipeline::Greater:
6592 return MTLCompareFunctionGreater;
6593 case QRhiGraphicsPipeline::NotEqual:
6594 return MTLCompareFunctionNotEqual;
6595 case QRhiGraphicsPipeline::GreaterOrEqual:
6596 return MTLCompareFunctionGreaterEqual;
6597 case QRhiGraphicsPipeline::Always:
6598 return MTLCompareFunctionAlways;
6601 return MTLCompareFunctionAlways;
6608 case QRhiGraphicsPipeline::StencilZero:
6609 return MTLStencilOperationZero;
6610 case QRhiGraphicsPipeline::Keep:
6611 return MTLStencilOperationKeep;
6612 case QRhiGraphicsPipeline::Replace:
6613 return MTLStencilOperationReplace;
6614 case QRhiGraphicsPipeline::IncrementAndClamp:
6615 return MTLStencilOperationIncrementClamp;
6616 case QRhiGraphicsPipeline::DecrementAndClamp:
6617 return MTLStencilOperationDecrementClamp;
6618 case QRhiGraphicsPipeline::Invert:
6619 return MTLStencilOperationInvert;
6620 case QRhiGraphicsPipeline::IncrementAndWrap:
6621 return MTLStencilOperationIncrementWrap;
6622 case QRhiGraphicsPipeline::DecrementAndWrap:
6623 return MTLStencilOperationDecrementWrap;
6626 return MTLStencilOperationKeep;
6633 case QRhiGraphicsPipeline::Triangles:
6634 return MTLPrimitiveTypeTriangle;
6635 case QRhiGraphicsPipeline::TriangleStrip:
6636 return MTLPrimitiveTypeTriangleStrip;
6637 case QRhiGraphicsPipeline::Lines:
6638 return MTLPrimitiveTypeLine;
6639 case QRhiGraphicsPipeline::LineStrip:
6640 return MTLPrimitiveTypeLineStrip;
6641 case QRhiGraphicsPipeline::Points:
6642 return MTLPrimitiveTypePoint;
6645 return MTLPrimitiveTypeTriangle;
6652 case QRhiGraphicsPipeline::Triangles:
6653 case QRhiGraphicsPipeline::TriangleStrip:
6654 case QRhiGraphicsPipeline::TriangleFan:
6655 return MTLPrimitiveTopologyClassTriangle;
6656 case QRhiGraphicsPipeline::Lines:
6657 case QRhiGraphicsPipeline::LineStrip:
6658 return MTLPrimitiveTopologyClassLine;
6659 case QRhiGraphicsPipeline::Points:
6660 return MTLPrimitiveTopologyClassPoint;
6663 return MTLPrimitiveTopologyClassTriangle;
6670 case QRhiGraphicsPipeline::None:
6671 return MTLCullModeNone;
6672 case QRhiGraphicsPipeline::Front:
6673 return MTLCullModeFront;
6674 case QRhiGraphicsPipeline::Back:
6675 return MTLCullModeBack;
6678 return MTLCullModeNone;
6685 case QRhiGraphicsPipeline::Fill:
6686 return MTLTriangleFillModeFill;
6687 case QRhiGraphicsPipeline::Line:
6688 return MTLTriangleFillModeLines;
6691 return MTLTriangleFillModeFill;
6698 case QShaderDescription::CwTessellationWindingOrder:
6699 return MTLWindingClockwise;
6700 case QShaderDescription::CcwTessellationWindingOrder:
6701 return MTLWindingCounterClockwise;
6704 return MTLWindingCounterClockwise;
6711 case QShaderDescription::EqualTessellationPartitioning:
6712 return MTLTessellationPartitionModePow2;
6713 case QShaderDescription::FractionalEvenTessellationPartitioning:
6714 return MTLTessellationPartitionModeFractionalEven;
6715 case QShaderDescription::FractionalOddTessellationPartitioning:
6716 return MTLTessellationPartitionModeFractionalOdd;
6719 return MTLTessellationPartitionModePow2;
6725 int v = version.version();
6726 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
6730 int frameSlot, quint32 minBlockSize, quint32 *offset)
6733 const quint32 alignedSize = aligned<quint32>(size, alignment);
6734 if (pool.offset + alignedSize > pool.capacity) {
6744 e.stagingBuffer.buffer = pool.buf;
6745 releaseQueue.append(e);
6747 pool.capacity = qMax(pool.capacity * 2, qMax(alignedSize, minBlockSize));
6748 pool.buf = [dev newBufferWithLength: pool.capacity options: MTLResourceStorageModeShared];
6755 *offset = pool.offset;
6756 pool.offset += alignedSize;
6762 return allocFromStagingArea(&argBufPool[frameSlot], size, alignment, frameSlot, 16384, offset);
6767 id<MTLBuffer> buf = [dev newBufferWithLength: size options: MTLResourceStorageModeShared];
6773 e.stagingBuffer.buffer = buf;
6774 releaseQueue.append(e);
6780 if (size > LARGE_STAGING_ALLOC) {
6782 return newOneShotStagingBuffer(size, frameSlot);
6787 const quint32 alignedSize = aligned<quint32>(size, 4u);
6788 area.bytesNeeded += alignedSize;
6790 if (area.offset + alignedSize > area.capacity) {
6792 return newOneShotStagingBuffer(size, frameSlot);
6795 *offset = area.offset;
6796 area.offset += alignedSize;
6803 const quint32 needed = area.bytesNeeded;
6804 area.bytesNeeded = 0;
6807 bool resize =
false;
6808 quint32 newCapacity = 0;
6809 if (needed > area.capacity) {
6816 newCapacity = qMin(qNextPowerOfTwo(needed), STAGING_AREA_MAX);
6819 }
else if (needed <= area.capacity / 4 && area.capacity > 0) {
6825 newCapacity = needed ? qMax(area.capacity / 2, STAGING_AREA_MIN) : 0;
6833 if (resize && newCapacity != area.capacity) {
6838 e.stagingBuffer.buffer = area.buf;
6839 releaseQueue.append(e);
6844 area.buf = [dev newBufferWithLength: newCapacity options: MTLResourceStorageModeShared];
6846 area.capacity = newCapacity;
6852 bool preferArgumentBuffers,
6853 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
6855 QVarLengthArray<
int, 8> versions;
6856 versions << 30 << 24 << 23 << 22 << 21 << 20 << 12;
6866 QVarLengthArray<QShader::Variant, 2> variants;
6867 if (preferArgumentBuffers)
6868 variants << QShader::ArgumentBufferShader;
6869 variants << shaderVariant;
6871 const QList<QShaderKey> shaders = shader.availableShaders();
6873 auto findKey = [&shaders, &versions, &variants](QShader::Source source, QShaderKey *result) {
6874 for (
const QShader::Variant &variant : variants) {
6875 for (
const int &version : versions) {
6876 const QShaderKey key = { source, version, variant };
6877 if (shaders.contains(key)) {
6888 if (findKey(QShader::Source::MetalLibShader, &key)) {
6889 QShaderCode mtllib = shader.shader(key);
6890 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
6891 size_t(mtllib.shader().size()),
6892 dispatch_get_global_queue(0, 0),
6893 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
6895 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
6896 dispatch_release(data);
6898 *entryPoint = mtllib.entryPoint();
6902 const QString msg = QString::fromNSString(err.localizedDescription);
6903 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
6907 if (!findKey(QShader::Source::MslShader, &key)) {
6908 qWarning() <<
"No MSL code found in baked shader" << shader;
6912 QShaderCode mslSource = shader.shader(key);
6914 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
6915 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
6916 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
6918 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
6926 const QString msg = QString::fromNSString(err.localizedDescription);
6931 *entryPoint = mslSource.entryPoint();
6938 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
6943 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
6947 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
6948 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
6949 rpDesc.colorAttachments[0].blendingEnabled =
false;
6951 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
6952 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
6954 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
6955 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
6956 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
6957 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
6958 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
6959 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
6960 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
6961 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
6962 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
6963 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
6964 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
6971 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
6972 rpDesc.depthAttachmentPixelFormat = fmt;
6973#if defined(Q_OS_MACOS)
6974 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
6976 if (fmt != MTLPixelFormatDepth32Float)
6978 rpDesc.stencilAttachmentPixelFormat = fmt;
6982 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
6987 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
6989 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
6990 dsDesc.depthWriteEnabled = m_depthWrite;
6991 if (m_stencilTest) {
6992 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
6993 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
6994 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
6995 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
6996 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
6997 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
6998 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
7000 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
7001 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
7002 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
7003 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
7004 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
7005 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
7006 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
7012 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
7013 d->cullMode = toMetalCullMode(m_cullMode);
7014 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
7015 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
7027 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
7030 const uint loc = uint(it->location());
7031 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
7032 desc.attributes[loc].offset = NSUInteger(it->offset());
7033 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
7035 int bindingIndex = 0;
7036 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
7037 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
7038 it != itEnd; ++it, ++bindingIndex)
7040 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
7041 desc.layouts[layoutIdx].stepFunction =
7042 it->classification() == QRhiVertexInputBinding::PerInstance
7043 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
7044 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
7045 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
7046 desc.layouts[layoutIdx].stepRate *= viewCount;
7047 desc.layouts[layoutIdx].stride = it->stride();
7058 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
7061 const uint loc = uint(it->location());
7062 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
7063 desc.attributes[loc].offset = NSUInteger(it->offset());
7064 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
7066 int bindingIndex = 0;
7067 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
7068 it != itEnd; ++it, ++bindingIndex)
7070 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
7071 if (desc.indexBufferIndex) {
7072 desc.layouts[layoutIdx].stepFunction =
7073 it->classification() == QRhiVertexInputBinding::PerInstance
7074 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
7076 desc.layouts[layoutIdx].stepFunction =
7077 it->classification() == QRhiVertexInputBinding::PerInstance
7078 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
7080 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
7081 desc.layouts[layoutIdx].stride = it->stride();
7088 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
7089 rpDesc.binaryArchives = binArchArray;
7097 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
7098 const QString msg = QString::fromNSString(err.localizedDescription);
7099 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
7106 return !desc.combinedImageSamplers().isEmpty()
7107 || !desc.separateImages().isEmpty()
7108 || !desc.storageImages().isEmpty();
7113 for (
const QShaderKey &k : shader.availableShaders()) {
7114 if (k.sourceVariant() == QShader::ArgumentBufferShader)
7122 const int index = shader->nativeShaderInfo.extraBufferBindings.value(QShaderPrivate::MslArgumentBufferBinding, -1);
7125 shader->argumentEncoder = [shader->func newArgumentEncoderWithBufferIndex: NSUInteger(index)];
7136 const bool wantArgumentBuffers = m_flags.testFlag(UsesIndirectDraws) && rhiD->caps.indirectCommandBuffers;
7138 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
7139 d->setupVertexInputDescriptor(vertexDesc);
7141 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
7142 rpDesc.vertexDescriptor = vertexDesc;
7150 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7151 const QShader shader = shaderStage.shader();
7152 const bool argumentBufferBuild = wantArgumentBuffers && hasArgumentBufferVariant(shader);
7153 auto cacheIt = rhiD->d->shaderCache.constFind({ shaderStage, argumentBufferBuild });
7154 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
7155 switch (shaderStage.type()) {
7156 case QRhiShaderStage::Vertex:
7159 [d->vs.func retain];
7160 [d->vs.argumentEncoder retain];
7161 rpDesc.vertexFunction = d->vs.func;
7163 case QRhiShaderStage::Fragment:
7166 [d->fs.func retain];
7167 [d->fs.argumentEncoder retain];
7168 rpDesc.fragmentFunction = d->fs.func;
7175 QByteArray entryPoint;
7176 QShaderKey activeKey;
7177 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
7178 argumentBufferBuild,
7179 &error, &entryPoint, &activeKey);
7181 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
7184 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
7186 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7190 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
7192 for (QMetalShader &s : rhiD->d->shaderCache)
7194 rhiD->d->shaderCache.clear();
7196 switch (shaderStage.type()) {
7197 case QRhiShaderStage::Vertex:
7200 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
7201 d->vs.desc = shader.description();
7202 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
7203 setupArgumentBufferEncoder(&d->vs);
7204 rhiD->d->shaderCache.insert({ shaderStage, argumentBufferBuild }, d->vs);
7206 [d->vs.func retain];
7207 [d->vs.argumentEncoder retain];
7208 rpDesc.vertexFunction = func;
7210 case QRhiShaderStage::Fragment:
7213 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
7214 d->fs.desc = shader.description();
7215 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
7216 setupArgumentBufferEncoder(&d->fs);
7217 rhiD->d->shaderCache.insert({ shaderStage, argumentBufferBuild }, d->fs);
7219 [d->fs.func retain];
7220 [d->fs.argumentEncoder retain];
7221 rpDesc.fragmentFunction = func;
7237 return !usesTextures(s.desc) || s.argumentBufferIndex >= 0;
7239 d->icbCapable = wantArgumentBuffers && icbSafe(
d->vs) && icbSafe(
d->fs);
7241 rpDesc.supportIndirectCommandBuffers = YES;
7243 if (m_multiViewCount >= 2)
7244 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
7246 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
7248 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7249 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
7252 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
7255 const QString msg = QString::fromNSString(err.localizedDescription);
7256 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
7260 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
7262 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
7265 d->primitiveType = toMetalPrimitiveType(m_topology);
7273 switch (vertexCompVariant) {
7274 case QShader::NonIndexedVertexAsComputeShader:
7276 case QShader::UInt32IndexedVertexAsComputeShader:
7278 case QShader::UInt16IndexedVertexAsComputeShader:
7288 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
7289 if (varIndex >= 0 && vertexComputeState[varIndex])
7290 return vertexComputeState[varIndex];
7292 id<MTLFunction> func = nil;
7294 func = compVs[varIndex].func;
7297 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
7301 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
7302 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
7304 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
7305 cpDesc.computeFunction = func;
7306 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
7307 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
7308 if (indexBufferBinding >= 0) {
7309 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
7310 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
7311 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
7312 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
7313 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
7314 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
7317 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
7319 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
7321 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7322 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
7325 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
7326 options: MTLPipelineOptionNone
7331 const QString msg = QString::fromNSString(err.localizedDescription);
7332 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
7334 vertexComputeState[varIndex] = ps;
7342 if (tessControlComputeState)
7343 return tessControlComputeState;
7345 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
7346 cpDesc.computeFunction = compTesc.func;
7348 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
7350 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7351 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
7354 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
7355 options: MTLPipelineOptionNone
7360 const QString msg = QString::fromNSString(err.localizedDescription);
7361 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
7363 tessControlComputeState = ps;
7371 return (indices >> index) & 0x1;
7374static inline void takeIndex(quint32 index, quint64 &indices)
7376 indices |= 1 << index;
7385 static const int maxVertexAttributes = 31;
7387 for (
int index = 0; index < maxVertexAttributes; ++index) {
7388 if (!indexTaken(index, indices))
7392 Q_UNREACHABLE_RETURN(-1);
7395static inline int aligned(quint32 offset, quint32 alignment)
7397 return ((offset + alignment - 1) / alignment) * alignment;
7405 for (
const int dim : variable.arrayDims)
7408 if (variable.type == QShaderDescription::VariableType::Struct) {
7409 for (
int element = 0; element < elements; ++element) {
7410 for (
const auto &member : variable.structMembers) {
7411 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
7415 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
7416 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
7419 const quint32 alignment = size;
7420 vertexAlignment =
std::max(vertexAlignment, alignment);
7422 for (
int element = 0; element < elements; ++element) {
7424 offset = aligned(offset, alignment);
7431static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
7435 for (
const int dim : variable.arrayDims)
7438 if (variable.type == QShaderDescription::VariableType::Struct) {
7439 for (
int element = 0; element < elements; ++element) {
7440 for (
const auto &member : variable.structMembers) {
7441 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
7445 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
7446 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
7449 const quint32 alignment = size;
7450 vertexAlignment =
std::max(vertexAlignment, alignment);
7452 for (
int element = 0; element < elements; ++element) {
7453 Q_ASSERT(!indexTaken(index, indices));
7456 offset = aligned(offset, alignment);
7458 attributes[index].bufferIndex = binding;
7459 attributes[index].format = toMetalAttributeFormat(format);
7460 attributes[index].offset = offset;
7462 takeIndex(index, indices);
7464 if (indexTaken(index, indices))
7465 index = nextAttributeIndex(indices);
7472static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
7474 if (a.size() == b.size()) {
7476 for (
int i = 0; i < a.size() && match; ++i) {
7477 match &= a[i].type == b[i].type
7478 && a[i].arrayDims == b[i].arrayDims
7479 && matches(a[i].structMembers, b[i].structMembers);
7487static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
7489 return a.location == b.location
7491 && a.perPatch == b.perPatch
7492 && matches(a.structMembers, b.structMembers);
7541 if (pipeline
->d->ps)
7542 return pipeline
->d->ps;
7544 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
7545 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
7548 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
7549 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
7550 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
7551 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
7552 quint32 offsetInTescOutput = 0;
7553 quint32 offsetInTescPatchOutput = 0;
7554 quint32 offsetInTessFactorBuffer = 0;
7555 quint32 tescOutputAlignment = 0;
7556 quint32 tescPatchOutputAlignment = 0;
7557 quint32 tessFactorAlignment = 0;
7558 QSet<
int> usedBuffers;
7561 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
7562 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
7563 tescOutVars[tescOutVar.location] = tescOutVar;
7566 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
7567 for (
const auto &teseInVar : vertTese.desc.inputVariables())
7568 teseInVars[teseInVar.location] = teseInVar;
7571 quint64 indices = 0;
7573 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
7575 int index = tescOutVar.location;
7577 quint32 *offset =
nullptr;
7578 quint32 *alignment =
nullptr;
7580 if (tescOutVar.perPatch) {
7581 binding = tescPatchOutputBufferBinding;
7582 offset = &offsetInTescPatchOutput;
7583 alignment = &tescPatchOutputAlignment;
7585 tescOutVar.arrayDims.removeLast();
7586 binding = tescOutputBufferBinding;
7587 offset = &offsetInTescOutput;
7588 alignment = &tescOutputAlignment;
7591 if (teseInVars.contains(index)) {
7593 if (!matches(teseInVars[index], tescOutVar)) {
7594 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
7595 qWarning() <<
" tesc out:" << tescOutVar;
7596 qWarning() <<
" tese in:" << teseInVars[index];
7599 if (binding != -1) {
7600 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
7601 usedBuffers << binding;
7603 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
7604 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
7608 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
7609 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
7612 teseInVars.remove(tescOutVar.location);
7615 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
7616 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
7619 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
7620 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
7621 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
7624 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
7625 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
7626 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
7628 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
7629 bool tessLevelAdded =
false;
7631 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
7633 QShaderDescription::InOutVariable variable;
7635 quint32 *offset =
nullptr;
7636 quint32 *alignment =
nullptr;
7638 switch (builtin.type) {
7639 case QShaderDescription::BuiltinType::PositionBuiltin:
7640 variable.type = QShaderDescription::VariableType::Vec4;
7641 binding = tescOutputBufferBinding;
7642 offset = &offsetInTescOutput;
7643 alignment = &tescOutputAlignment;
7645 case QShaderDescription::BuiltinType::PointSizeBuiltin:
7646 variable.type = QShaderDescription::VariableType::Float;
7647 binding = tescOutputBufferBinding;
7648 offset = &offsetInTescOutput;
7649 alignment = &tescOutputAlignment;
7651 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
7652 variable.type = QShaderDescription::VariableType::Float;
7653 variable.arrayDims = builtin.arrayDims;
7654 binding = tescOutputBufferBinding;
7655 offset = &offsetInTescOutput;
7656 alignment = &tescOutputAlignment;
7658 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
7659 variable.type = QShaderDescription::VariableType::Half4;
7660 binding = tessFactorBufferBinding;
7661 offset = &offsetInTessFactorBuffer;
7662 tessLevelAdded = trianglesMode;
7663 alignment = &tessFactorAlignment;
7665 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
7666 if (trianglesMode) {
7667 if (!tessLevelAdded) {
7668 variable.type = QShaderDescription::VariableType::Half4;
7669 binding = tessFactorBufferBinding;
7670 offsetInTessFactorBuffer = 0;
7671 offset = &offsetInTessFactorBuffer;
7672 alignment = &tessFactorAlignment;
7673 tessLevelAdded =
true;
7675 teseInBuiltins.remove(builtin.type);
7679 variable.type = QShaderDescription::VariableType::Half2;
7680 binding = tessFactorBufferBinding;
7681 offsetInTessFactorBuffer = 8;
7682 offset = &offsetInTessFactorBuffer;
7683 alignment = &tessFactorAlignment;
7691 if (teseInBuiltins.contains(builtin.type)) {
7692 if (binding != -1) {
7693 int index = nextAttributeIndex(indices);
7694 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
7695 usedBuffers << binding;
7697 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
7698 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
7701 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
7704 teseInBuiltins.remove(builtin.type);
7707 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
7708 switch (builtin.type) {
7709 case QShaderDescription::BuiltinType::PositionBuiltin:
7710 case QShaderDescription::BuiltinType::PointSizeBuiltin:
7711 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
7712 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
7719 if (usedBuffers.contains(tescOutputBufferBinding)) {
7720 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
7721 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
7724 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
7725 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
7726 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
7729 if (usedBuffers.contains(tessFactorBufferBinding)) {
7730 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
7731 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
7734 rpDesc.vertexDescriptor = vertexDesc;
7735 rpDesc.vertexFunction = vertTese.func;
7736 rpDesc.fragmentFunction = pipeline
->d->fs.func;
7742 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
7744 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
7749 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
7751 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7752 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
7755 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
7758 const QString msg = QString::fromNSString(err.localizedDescription);
7759 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
7763 pipeline
->d->ps = ps;
7770 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
7773 for (QMetalBuffer *workBuf : *workBuffers) {
7774 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
7775 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
7782 if (workBuffers->count() > QMTL_FRAMES_IN_FLIGHT * 8) {
7783 for (QMetalBuffer *workBuf : *workBuffers) {
7784 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
7785 workBuf->setSize(size);
7786 if (workBuf->create()) {
7787 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
7798 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
7801 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
7805 workBuffers->append(buf);
7809 qWarning(
"Failed to acquire work buffer of size %u", size);
7817 QByteArray entryPoint;
7818 QShaderKey activeKey;
7820 const QShaderDescription tescDesc = tesc.description();
7821 const QShaderDescription teseDesc = tese.description();
7822 d->tess.inControlPointCount = uint(m_patchControlPointCount);
7823 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
7824 if (!
d->tess.outControlPointCount)
7825 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
7827 if (!
d->tess.outControlPointCount) {
7828 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
7829 d->tess.enabled =
false;
7830 d->tess.failed =
true;
7834 if (m_multiViewCount >= 2)
7835 qWarning(
"Multiview is not supported with tessellation");
7843 bool variantsPresent[3] = {};
7844 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
7845 for (
const QShaderKey &k : tessVertKeys) {
7846 switch (k.sourceVariant()) {
7847 case QShader::NonIndexedVertexAsComputeShader:
7848 variantsPresent[0] =
true;
7850 case QShader::UInt32IndexedVertexAsComputeShader:
7851 variantsPresent[1] =
true;
7853 case QShader::UInt16IndexedVertexAsComputeShader:
7854 variantsPresent[2] =
true;
7860 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
7861 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
7862 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
7863 "Try passing --msltess to qsb.");
7864 d->tess.enabled =
false;
7865 d->tess.failed =
true;
7870 for (QShader::Variant variant : {
7871 QShader::NonIndexedVertexAsComputeShader,
7872 QShader::UInt32IndexedVertexAsComputeShader,
7873 QShader::UInt16IndexedVertexAsComputeShader })
7875 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant,
false, &error, &entryPoint, &activeKey);
7877 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
7878 d->tess.enabled =
false;
7879 d->tess.failed =
true;
7882 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
7884 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7886 d->tess.enabled =
false;
7887 d->tess.failed =
true;
7890 QMetalShader &compVs(d->tess.compVs[varIndex]);
7893 compVs.desc = tessVert.description();
7894 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
7895 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
7898 if (!d->tess.vsCompPipeline(rhiD, variant)) {
7899 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
7900 d->tess.enabled =
false;
7901 d->tess.failed =
true;
7909 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7910 if (!tessControlLib) {
7911 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
7912 d->tess.enabled =
false;
7913 d->tess.failed =
true;
7916 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
7917 if (!tessControlFunc) {
7918 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7919 [tessControlLib release];
7920 d->tess.enabled =
false;
7921 d->tess.failed =
true;
7924 d->tess.compTesc.lib = tessControlLib;
7925 d->tess.compTesc.func = tessControlFunc;
7926 d->tess.compTesc.desc = tesc.description();
7927 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
7928 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
7929 if (!
d->tess.tescCompPipeline(rhiD)) {
7930 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
7931 d->tess.enabled =
false;
7932 d->tess.failed =
true;
7937 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7939 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
7940 d->tess.enabled =
false;
7941 d->tess.failed =
true;
7944 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
7945 if (!tessEvalFunc) {
7946 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7947 [tessEvalLib release];
7948 d->tess.enabled =
false;
7949 d->tess.failed =
true;
7952 d->tess.vertTese.lib = tessEvalLib;
7953 d->tess.vertTese.func = tessEvalFunc;
7954 d->tess.vertTese.desc = tese.description();
7955 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
7956 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
7958 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7960 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
7961 d->tess.enabled =
false;
7962 d->tess.failed =
true;
7965 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
7967 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7969 d->tess.enabled =
false;
7970 d->tess.failed =
true;
7973 d->fs.lib = fragLib;
7974 d->fs.func = fragFunc;
7975 d->fs.desc = tessFrag.description();
7976 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
7977 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
7979 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
7980 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
7981 d->tess.enabled =
false;
7982 d->tess.failed =
true;
7986 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
7988 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
8002 rhiD->pipelineCreationStart();
8003 if (!rhiD->sanityCheckGraphicsPipeline(
this))
8011 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
8012 switch (shaderStage.type()) {
8013 case QRhiShaderStage::Vertex:
8014 tessVert = shaderStage.shader();
8016 case QRhiShaderStage::TessellationControl:
8017 tesc = shaderStage.shader();
8019 case QRhiShaderStage::TessellationEvaluation:
8020 tese = shaderStage.shader();
8022 case QRhiShaderStage::Fragment:
8023 tessFrag = shaderStage.shader();
8029 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
8030 d->tess.failed =
false;
8032 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
8038 QVarLengthArray<QMetalShader *, 6> shaders;
8039 if (
d->tess.enabled) {
8040 shaders.append(&
d->tess.compVs[0]);
8041 shaders.append(&
d->tess.compVs[1]);
8042 shaders.append(&
d->tess.compVs[2]);
8043 shaders.append(&
d->tess.compTesc);
8044 shaders.append(&
d->tess.vertTese);
8046 shaders.append(&
d->vs);
8048 shaders.append(&
d->fs);
8050 for (QMetalShader *shader : shaders) {
8051 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
8052 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
8053 shader->nativeResourceBindingMap[binding] = {binding, -1};
8054 int maxNativeBinding = 0;
8055 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
8056 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
8060 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
8067 QRhiBuffer::UsageFlags(
int(QRhiBuffer::StorageBuffer)
8068 | QMetalBuffer::InternalHostWritable),
8069 buffers *
sizeof(
int));
8075 rhiD->pipelineCreationEnd();
8078 rhiD->registerResource(
this);
8107 e.computePipeline.pipelineState =
d->ps;
8112 rhiD
->d->releaseQueue.append(e);
8113 rhiD->unregisterResource(
this);
8120 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
8121 cpDesc.binaryArchives = binArchArray;
8129 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
8130 const QString msg = QString::fromNSString(err.localizedDescription);
8131 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
8142 rhiD->pipelineCreationStart();
8144 auto cacheIt = rhiD
->d->shaderCache.constFind({ m_shaderStage,
false });
8145 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
8148 const QShader shader = m_shaderStage.shader();
8150 QByteArray entryPoint;
8151 QShaderKey activeKey;
8152 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
false,
8153 &error, &entryPoint, &activeKey);
8155 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
8158 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
8160 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
8166 d->cs.localSize = shader.description().computeShaderLocalSize();
8167 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
8168 d->cs.desc = shader.description();
8169 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
8176 setupArgumentBufferEncoder(&
d->cs);
8179 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
8180 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
8181 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
8184 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
8185 for (QMetalShader &s : rhiD->d->shaderCache)
8187 rhiD
->d->shaderCache.clear();
8189 rhiD
->d->shaderCache.insert({ m_shaderStage,
false },
d->cs);
8193 [d->cs.func retain];
8194 [d->cs.argumentEncoder retain];
8196 if (
d->cs.argumentBufferIndex >= 0 && !rhiD->caps.indirectCommandBuffers) {
8200 qWarning(
"The ArgumentBufferShader variant of a compute shader cannot be used on this device");
8204 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
8206 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
8207 cpDesc.computeFunction =
d->cs.func;
8209 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
8211 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
8212 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
8215 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
8216 options: MTLPipelineOptionNone
8221 const QString msg = QString::fromNSString(err.localizedDescription);
8222 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
8227 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
8229 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
8230 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
8234 if (!d->bufferSizeBuffer)
8235 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static,
8236 QRhiBuffer::UsageFlags(
int(QRhiBuffer::StorageBuffer)
8237 | QMetalBuffer::InternalHostWritable),
8238 buffers *
sizeof(
int));
8244 rhiD->pipelineCreationEnd();
8247 rhiD->registerResource(
this);
8271 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
8272 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
8273 return &nativeHandlesStruct;
8279 d->currentRenderPassEncoder = nil;
8280 d->currentComputePassEncoder = nil;
8281 d->tessellationComputeEncoder = nil;
8282 d->currentPassRpDesc = nil;
8289 currentTarget =
nullptr;
8290 d->openDebugGroups.clear();
8299 pushConstantData.clear();
8306 currentIndexOffset = 0;
8307 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
8312 currentDepthBiasValues = { 0.0f, 0.0f };
8314 currentScissor = {};
8315 currentViewport = {};
8317 currentBlendConstants = {};
8318 currentStencilRef = 0;
8319 appliedStencilRef.reset();
8321 d->currentShaderResourceBindingState = {};
8322 d->currentDepthStencilState = nil;
8324 d->currentVertexInputsBuffers.clear();
8325 d->currentVertexInputOffsets.clear();
8335 d->sem[i] =
nullptr;
8336 d->msaaTex[i] = nil;
8356 dispatch_release(
d->sem[i]);
8357 d->sem[i] =
nullptr;
8362 [d->msaaTex[i] release];
8363 d->msaaTex[i] = nil;
8369 [d->curDrawable release];
8370 d->curDrawable = nil;
8374 rhiD->swapchains.remove(
this);
8375 rhiD->unregisterResource(
this);
8395 CALayer *layer =
nullptr;
8397 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
8398 layer = cocoaWindow->contentLayer();
8400 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
8403 return static_cast<CAMetalLayer *>(layer);
8419 CAMetalLayer *layer =
d->layer;
8421 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
8424 int height = (
int)layer.bounds.size.height;
8425 int width = (
int)layer.bounds.size.width;
8426 width *= layer.contentsScale;
8427 height *= layer.contentsScale;
8428 return QSize(width, height);
8433 if (f == HDRExtendedSrgbLinear) {
8434 return hdrInfo().limits.colorComponentValue.maxPotentialColorComponentValue > 1.0f;
8435 }
else if (f == HDR10) {
8436 return hdrInfo().limits.colorComponentValue.maxPotentialColorComponentValue > 1.0f;
8437 }
else if (f == HDRExtendedDisplayP3Linear) {
8438 return hdrInfo().limits.colorComponentValue.maxPotentialColorComponentValue > 1.0f;
8457 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
8458 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
8460 rpD
->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
8467 rhiD->registerResource(rpD,
false);
8474 samples = rhiD->effectiveSampleCount(m_sampleCount);
8476 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
8477 d->colorFormat = MTLPixelFormatRGBA16Float;
8478 d->rhiColorFormat = QRhiTexture::RGBA16F;
8481 if (m_format == HDR10) {
8482 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
8483 d->rhiColorFormat = QRhiTexture::RGB10A2;
8486 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
8487 d->rhiColorFormat = QRhiTexture::BGRA8;
8496 dispatch_semaphore_t sem =
d->sem[slot];
8497 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
8498 dispatch_semaphore_signal(sem);
8505 const bool needsRegistration = !
window ||
window != m_window;
8507 if (window && window != m_window)
8512 if (needsRegistration || !rhiD->swapchains.contains(
this))
8513 rhiD->swapchains.insert(
this);
8517 if (window->surfaceType() != QSurface::MetalSurface) {
8518 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
8522 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
8526 if (
d->colorFormat !=
d->layer.pixelFormat)
8527 d->layer.pixelFormat =
d->colorFormat;
8529 if (m_format == HDRExtendedSrgbLinear) {
8530 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
8531 d->layer.wantsExtendedDynamicRangeContent = YES;
8532 }
else if (m_format == HDR10) {
8533 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
8534 d->layer.wantsExtendedDynamicRangeContent = YES;
8535 }
else if (m_format == HDRExtendedDisplayP3Linear) {
8536 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
8537 d->layer.wantsExtendedDynamicRangeContent = YES;
8540 if (m_flags.testFlag(UsedAsTransferSource))
8541 d->layer.framebufferOnly = NO;
8544 if (m_flags.testFlag(NoVSync))
8545 d->layer.displaySyncEnabled = NO;
8548 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
8549 d->layer.opaque = NO;
8550 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
8555 d->layer.opaque = NO;
8557 d->layer.opaque = YES;
8563 int width = (
int)
d->layer.bounds.size.width;
8564 int height = (
int)
d->layer.bounds.size.height;
8565 CGSize layerSize = CGSizeMake(width, height);
8566 const float scaleFactor =
d->layer.contentsScale;
8567 layerSize.width *= scaleFactor;
8568 layerSize.height *= scaleFactor;
8569 d->layer.drawableSize = layerSize;
8571 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
8572 pixelSize = m_currentPixelSize;
8574 [d->layer setDevice: rhiD->d->dev];
8576 [d->curDrawable release];
8577 d->curDrawable = nil;
8580 d->lastGpuTime[i] = 0;
8582 d->sem[i] = dispatch_semaphore_create(QMTL_FRAMES_IN_FLIGHT - 1);
8588 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
8589 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
8590 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
8591 m_depthStencil->sampleCount(), m_sampleCount);
8593 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
8594 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
8595 m_depthStencil->setPixelSize(pixelSize);
8596 if (!m_depthStencil->create())
8597 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
8598 pixelSize.width(), pixelSize.height());
8600 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
8601 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
8602 pixelSize.width(), pixelSize.height());
8606 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
8613 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
8614 pixelSize.width(), pixelSize.height(), scaleFactor);
8617 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
8618 desc.textureType = MTLTextureType2DMultisample;
8619 desc.pixelFormat =
d->colorFormat;
8620 desc.width = NSUInteger(pixelSize.width());
8621 desc.height = NSUInteger(pixelSize.height());
8622 desc.sampleCount = NSUInteger(
samples);
8623 desc.resourceOptions = MTLResourceStorageModePrivate;
8624 desc.storageMode = MTLStorageModePrivate;
8625 desc.usage = MTLTextureUsageRenderTarget;
8627 if (
d->msaaTex[i]) {
8631 e.renderbuffer.texture =
d->msaaTex[i];
8632 rhiD
->d->releaseQueue.append(e);
8634 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
8639 rhiD->registerResource(
this);
8648 info.limits.colorComponentValue.maxColorComponentValue = 1;
8649 info.limits.colorComponentValue.maxPotentialColorComponentValue = 1;
8655#if defined(Q_OS_MACOS)
8656 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
8657 NSScreen *screen = view.window.screen;
8658 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
8659 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
8660#elif defined(Q_OS_IOS)
8661 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
8662 UIScreen *screen = view.window.windowScene.screen;
8663 info.limits.colorComponentValue.maxColorComponentValue =
8664 view.window.windowScene.screen.currentEDRHeadroom;
8665 info.limits.colorComponentValue.maxPotentialColorComponentValue =
8666 screen.potentialEDRHeadroom;
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Combined button and popup list for selecting options.
Int aligned(Int v, Int byteAlign)
\variable QRhiVulkanQueueSubmitParams::waitSemaphoreCount
MTLPixelFormat viewFormat
MTLPixelFormat viewFormatForSampling
id< MTLTexture > viewForLevel(int level)
id< MTLTexture > perLevelViews[QRhi::MAX_MIP_LEVELS]
id< MTLBuffer > stagingBuf[QMTL_FRAMES_IN_FLIGHT]
id< MTLTexture > writeView
QMetalTextureData(QMetalTexture *t)
id< MTLTexture > samplingView
id< MTLTexture > textureForWrite() const
id< MTLTexture > textureForSampling() const
~QMetalTextureRenderTarget()
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
bool create() override
Creates the corresponding native graphics resources.
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.
\variable QRhiIndirectDrawCommand::vertexCount
QRhiReadbackDescription desc
quint32 depthPlaneByteSize
QRhiReadbackResult * result
QRhiTexture::Format format
quint32 stencilPlaneByteSize
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
LuminanceBehavior luminanceBehavior
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h