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);
227 id<MTLTexture> texture;
332 int frameSlot, quint32 minBlockSize, quint32 *offset);
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();
698 if (importedCmdQueue)
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 (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
767 d->setupBinaryArchive();
769 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
770 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
780 for (QMetalShader &s : d->shaderCache)
782 d->shaderCache.clear();
784 [d->captureScope release];
785 d->captureScope = nil;
787 for (
auto *pools : { d->argBufPool, d->bufStagingPool }) {
788 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
789 [pools[i].buf release];
791 pools[i].capacity = 0;
796 [d->icbArgumentBuffer release];
797 d->icbArgumentBuffer = nil;
799 [d->icbRangeBuffer release];
800 d->icbRangeBuffer = nil;
802 [d->icbNoCountBuffer release];
803 d->icbNoCountBuffer = nil;
805 [d->icbEncodeFunction release];
806 d->icbEncodeFunction = nil;
808 [d->icbEncodeFunctionU32 release];
809 d->icbEncodeFunctionU32 = nil;
811 [d->icbEncodeFunctionU16 release];
812 d->icbEncodeFunctionU16 = nil;
814 [d->icbEncodePipeline release];
815 d->icbEncodePipeline = nil;
817 [d->icbEncodePipelineU32 release];
818 d->icbEncodePipelineU32 = nil;
820 [d->icbEncodePipelineU16 release];
821 d->icbEncodePipelineU16 = nil;
829 [d->binArch release];
832 [d->cmdQueue release];
833 if (!importedCmdQueue)
843 return caps.supportedSampleCounts;
848 Q_UNUSED(sampleCount);
849 return { QSize(1, 1) };
854 return new QMetalSwapChain(
this);
857QRhiBuffer *
QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
859 return new QMetalBuffer(
this, type, usage, size);
886 static constexpr QMatrix4x4 m(1.0f, 0.0f, 0.0f, 0.0f,
887 0.0f, 1.0f, 0.0f, 0.0f,
888 0.0f, 0.0f, 0.5f, 0.5f,
889 0.0f, 0.0f, 0.0f, 1.0f);
897 bool supportsFamilyMac2 =
false;
898 bool supportsFamilyApple3 =
false;
901 supportsFamilyMac2 =
true;
903 supportsFamilyApple3 =
true;
905 supportsFamilyApple3 =
true;
909 if (format == QRhiTexture::BC5)
912 if (!supportsFamilyApple3) {
913 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
915 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
919 if (!supportsFamilyMac2)
920 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
929 case QRhi::MultisampleTexture:
931 case QRhi::MultisampleRenderBuffer:
933 case QRhi::DebugMarkers:
935 case QRhi::Timestamps:
937 case QRhi::Instancing:
939 case QRhi::CustomInstanceStepRate:
941 case QRhi::PrimitiveRestart:
943 case QRhi::NonDynamicUniformBuffers:
945 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
947 case QRhi::NPOTTextureRepeat:
949 case QRhi::RedOrAlpha8IsRed:
951 case QRhi::ElementIndexUint:
955 case QRhi::WideLines:
957 case QRhi::VertexShaderPointSize:
959 case QRhi::BaseVertex:
960 return caps.baseVertexAndInstance;
961 case QRhi::BaseInstance:
962 return caps.baseVertexAndInstance;
963 case QRhi::TriangleFanTopology:
965 case QRhi::ReadBackNonUniformBuffer:
967 case QRhi::ReadBackNonBaseMipLevel:
969 case QRhi::TexelFetch:
971 case QRhi::RenderToNonBaseMipLevel:
973 case QRhi::IntAttributes:
975 case QRhi::ScreenSpaceDerivatives:
977 case QRhi::ReadBackAnyTextureFormat:
979 case QRhi::PipelineCacheDataLoadSave:
985 case QRhi::ImageDataStride:
987 case QRhi::RenderBufferImport:
989 case QRhi::ThreeDimensionalTextures:
991 case QRhi::RenderTo3DTextureSlice:
993 case QRhi::TextureArrays:
995 case QRhi::Tessellation:
997 case QRhi::GeometryShader:
999 case QRhi::TextureArrayRange:
1001 case QRhi::NonFillPolygonMode:
1003 case QRhi::OneDimensionalTextures:
1005 case QRhi::OneDimensionalTextureMipmaps:
1007 case QRhi::HalfAttributes:
1009 case QRhi::RenderToOneDimensionalTexture:
1011 case QRhi::ThreeDimensionalTextureMipmaps:
1013 case QRhi::MultiView:
1014 return caps.multiView;
1015 case QRhi::TextureViewFormat:
1017 case QRhi::ResolveDepthStencil:
1019 case QRhi::VariableRateShading:
1021 case QRhi::VariableRateShadingMap:
1022 return caps.shadingRateMap;
1023 case QRhi::VariableRateShadingMapWithTexture:
1025 case QRhi::PerRenderTargetBlending:
1026 case QRhi::SampleVariables:
1028 case QRhi::InstanceIndexIncludesBaseInstance:
1030 case QRhi::DepthClamp:
1031 return caps.depthClamp;
1032 case QRhi::DrawIndirect:
1034 case QRhi::DrawIndirectMulti:
1035 return caps.indirectCommandBuffers;
1036 case QRhi::ShaderDrawParameters:
1038 case QRhi::PushConstants:
1040 case QRhi::DrawIndirectCount:
1041 return caps.indirectCommandBuffers;
1042 case QRhi::DispatchIndirect:
1044 case QRhi::BufferToBufferCopy:
1046 case QRhi::StaticBuffersOnGpuTimeline:
1047 return caps.usePrivateStaticBuffers;
1057 case QRhi::TextureSizeMin:
1059 case QRhi::TextureSizeMax:
1060 return caps.maxTextureSize;
1061 case QRhi::MaxColorAttachments:
1063 case QRhi::FramesInFlight:
1065 case QRhi::MaxAsyncReadbackFrames:
1067 case QRhi::MaxThreadGroupsPerDimension:
1069 case QRhi::MaxThreadsPerThreadGroup:
1071 case QRhi::MaxThreadGroupX:
1073 case QRhi::MaxThreadGroupY:
1075 case QRhi::MaxThreadGroupZ:
1076 return caps.maxThreadGroupSize;
1077 case QRhi::TextureArraySizeMax:
1079 case QRhi::MaxUniformBufferRange:
1081 case QRhi::MaxVertexInputs:
1083 case QRhi::MaxVertexOutputs:
1085 case QRhi::MaxPushConstantsSize:
1087 case QRhi::MaxVertexStorageBuffers:
1088 case QRhi::MaxFragmentStorageBuffers:
1090 case QRhi::ShadingRateImageTileSize:
1100 return &nativeHandlesStruct;
1105 return driverInfoStruct;
1111 result.totalPipelineCreationTime = totalPipelineCreationTime();
1128 for (QMetalShader &s : d->shaderCache)
1131 d->shaderCache.clear();
1153 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
1158 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1163 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1164 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1166 if (![d->binArch serializeToURL: url error: &err]) {
1167 const QString msg = QString::fromNSString(err.localizedDescription);
1169 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
1174 if (!f.open(QIODevice::ReadOnly)) {
1175 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1178 const QByteArray blob = f.readAll();
1182 const quint32 dataSize = quint32(blob.size());
1184 data.resize(headerSize + dataSize);
1187 header.rhiId = pipelineCacheRhiId();
1188 header.arch = quint32(
sizeof(
void*));
1189 header.dataSize = quint32(dataSize);
1190 header.osMajor = osMajor;
1191 header.osMinor = osMinor;
1192 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1194 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1195 header.driver[driverStrLen] =
'\0';
1197 memcpy(data.data(), &header, headerSize);
1198 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1208 if (data.size() < qsizetype(headerSize)) {
1209 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1213 const size_t dataOffset = headerSize;
1215 memcpy(&header, data.constData(), headerSize);
1217 const quint32 rhiId = pipelineCacheRhiId();
1218 if (header.rhiId != rhiId) {
1219 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1220 rhiId, header.rhiId);
1224 const quint32 arch = quint32(
sizeof(
void*));
1225 if (header.arch != arch) {
1226 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1231 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1232 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1233 osMajor, osMinor, header.osMajor, header.osMinor);
1237 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1238 if (strncmp(header
.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1239 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1243 if (quint64(data.size()) < quint64(dataOffset) + header.dataSize) {
1244 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1248 const char *p = data.constData() + dataOffset;
1252 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1255 tmp.write(p, header.dataSize);
1258 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1259 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1260 if (
d->setupBinaryArchive(url))
1261 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1264QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1265 int sampleCount, QRhiRenderBuffer::Flags flags,
1266 QRhiTexture::Format backingFormatHint)
1268 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1272 const QSize &pixelSize,
int depth,
int arraySize,
1273 int sampleCount, QRhiTexture::Flags flags)
1275 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1279 QRhiSampler::Filter mipmapMode,
1280 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1282 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1287 return new QMetalShadingRateMap(
this);
1291 QRhiTextureRenderTarget::Flags flags)
1298 return new QMetalGraphicsPipeline(
this);
1303 return new QMetalComputePipeline(
this);
1308 return new QMetalShaderResourceBindings(
this);
1319 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1322 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1323 if (!map || map->isEmpty())
1326 auto it = map->constFind(binding);
1327 if (it != map->cend())
1339 case QRhiShaderResourceBinding::ImageLoad:
1340 return MTLResourceUsageRead;
1341 case QRhiShaderResourceBinding::ImageStore:
1342 return MTLResourceUsageWrite;
1344 return MTLResourceUsageRead | MTLResourceUsageWrite;
1352 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : res.textures) {
1353 switch (encoderStage) {
1354 case QMetalShaderResourceBindingsData::VERTEX:
1355 [cbD->d->currentRenderPassEncoder useResource: t.mtltex usage: t.usage stages: MTLRenderStageVertex];
1357 case QMetalShaderResourceBindingsData::FRAGMENT:
1358 [cbD->d->currentRenderPassEncoder useResource: t.mtltex usage: t.usage stages: MTLRenderStageFragment];
1360 case QMetalShaderResourceBindingsData::COMPUTE:
1361 [cbD->d->currentComputePassEncoder useResource: t.mtltex usage: t.usage];
1371 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1372 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1375 case QMetalShaderResourceBindingsData::VERTEX:
1376 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1377 offsets: offsetBatch.resources.constData()
1378 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1380 case QMetalShaderResourceBindingsData::FRAGMENT:
1381 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1382 offsets: offsetBatch.resources.constData()
1383 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1385 case QMetalShaderResourceBindingsData::COMPUTE:
1386 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1387 offsets: offsetBatch.resources.constData()
1388 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1402 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1405 case QMetalShaderResourceBindingsData::VERTEX:
1406 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1407 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1409 case QMetalShaderResourceBindingsData::FRAGMENT:
1410 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1411 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1413 case QMetalShaderResourceBindingsData::COMPUTE:
1414 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1415 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1429 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1431 switch (encoderStage) {
1432 case QMetalShaderResourceBindingsData::VERTEX:
1433 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1434 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1436 case QMetalShaderResourceBindingsData::FRAGMENT:
1437 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1438 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1440 case QMetalShaderResourceBindingsData::COMPUTE:
1441 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1442 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1464 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1465 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1466 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1467 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1470 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1471 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1472 bindStageTextures(cbD, encoderStage, batch);
1475 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1476 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1477 bindStageSamplers(cbD, encoderStage, batch);
1480 if (bindingData->res[resourceStage].usesArgumentBuffer)
1481 declareStageArgumentBufferResources(cbD, encoderStage, bindingData->res[resourceStage]);
1487 case QMetalShaderResourceBindingsData::VERTEX:
1488 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1489 case QMetalShaderResourceBindingsData::TESSCTRL:
1490 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1491 case QMetalShaderResourceBindingsData::TESSEVAL:
1492 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1493 case QMetalShaderResourceBindingsData::FRAGMENT:
1494 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1495 case QMetalShaderResourceBindingsData::COMPUTE:
1496 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1499 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1504 int dynamicOffsetCount,
1505 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1506 bool offsetOnlyChange,
1507 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES],
1512 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1513 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1515 case QRhiShaderResourceBinding::UniformBuffer:
1517 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1518 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1519 quint32 offset = b->u.ubuf.offset;
1520 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1521 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1522 if (dynOfs.first == b->binding) {
1523 offset = dynOfs.second;
1528 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1529 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1530 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1531 if (nativeBinding >= 0)
1532 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1537 case QRhiShaderResourceBinding::SampledTexture:
1538 case QRhiShaderResourceBinding::Texture:
1539 case QRhiShaderResourceBinding::Sampler:
1541 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->stex;
1542 for (
int elem = 0; elem < data->count(); ++elem) {
1543 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->stex.texSamplers[elem].tex);
1544 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->stex.texSamplers[elem].sampler);
1546 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1547 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1552 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1553 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1554 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1555 if (textureBinding >= 0 && texD)
1556 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->textureForSampling(), MTLResourceUsageRead });
1557 if (samplerBinding >= 0)
1558 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1564 case QRhiShaderResourceBinding::ImageLoad:
1565 case QRhiShaderResourceBinding::ImageStore:
1566 case QRhiShaderResourceBinding::ImageLoadStore:
1568 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1569 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1571 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1572 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1573 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1574 if (nativeBinding >= 0)
1575 bindingData.res[stage].textures.append({ nativeBinding, t, storageImageUsage(b->type) });
1580 case QRhiShaderResourceBinding::BufferLoad:
1581 case QRhiShaderResourceBinding::BufferStore:
1582 case QRhiShaderResourceBinding::BufferLoadStore:
1584 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1585 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1586 quint32 offset = b->u.sbuf.offset;
1587 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1588 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1589 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1590 if (nativeBinding >= 0)
1591 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1607 if (!shader || !shader->argumentEncoder)
1614 if (offsetOnlyChange) {
1616 cbD
->d->currentShaderResourceBindingState.res[stage]);
1618 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &b : prev.buffers) {
1619 if (b.nativeBinding == shader->argumentBufferIndex) {
1620 res.samplers.clear();
1621 res.buffers.append(b);
1622 res.usesArgumentBuffer =
true;
1635 const quint32 argBufAlignment = qMax(quint32(shader->argumentEncoder.alignment),
1637 quint32 argBufOffset = 0;
1638 id<MTLBuffer> argBuf =
d->allocArgumentBuffer(quint32(shader->argumentEncoder.encodedLength),
1639 argBufAlignment, currentFrameSlot, &argBufOffset);
1645 qWarning(
"Failed to allocate Metal argument buffer");
1646 res.textures.clear();
1647 res.samplers.clear();
1650 [shader->argumentEncoder setArgumentBuffer: argBuf offset: argBufOffset];
1651 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(res.textures))
1652 [shader->argumentEncoder setTexture: t.mtltex atIndex: NSUInteger(t.nativeBinding)];
1653 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &sm : std::as_const(res.samplers))
1654 [shader->argumentEncoder setSamplerState: sm.mtlsampler atIndex: NSUInteger(sm.nativeBinding)];
1655 res.samplers.clear();
1676 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1677 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1678 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1681 bindingData.res[stage].bufferBatches.finish();
1682 bindingData.res[stage].bufferOffsetBatches.finish();
1684 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1685 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1686 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1688 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1689 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1690 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1691 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1695 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1698 if (offsetOnlyChange)
1701 if (bindingData.res[stage].usesArgumentBuffer) {
1702 declareStageArgumentBufferResources(cbD, stage, bindingData.res[stage]);
1714 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1715 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1717 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1718 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1720 bindingData.res[stage].textureBatches.finish();
1721 bindingData.res[stage].samplerBatches.finish();
1723 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1724 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1726 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1727 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1731 bindStageTextures(cbD, stage, batch);
1734 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1735 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1737 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1738 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1742 bindStageSamplers(cbD, stage, batch);
1746 cbD
->d->currentShaderResourceBindingState = bindingData;
1751 const QList<QShaderDescription::PushConstantBlock> blocks = s.desc.pushConstantBlocks();
1752 return blocks.isEmpty() ? 0 : quint32(blocks.first().size);
1757 return s.nativeShaderInfo.extraBufferBindings.value(QShaderPrivate::MslPushConstantBufferBinding, -1);
1770 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1772 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1773 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1774 cbD
->d->currentDepthStencilState =
d->ds;
1777 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1781 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1784 if (rhiD->caps.depthClamp) {
1786 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1791 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1794 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1797 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1798 slopeScale: d->slopeScaledDepthBias
1804 const int vsIdx = mtlPushConstantBufferIndex(
d->vs);
1805 const int fsIdx = mtlPushConstantBufferIndex(
d->fs);
1806 if (vsIdx >= 0 || fsIdx >= 0) {
1807 const quint32 blockSize = qMax(mtlPushConstantBlockSize(d->vs), mtlPushConstantBlockSize(d->fs));
1808 if (quint32(cbD->pushConstantData.size()) < blockSize)
1809 cbD->pushConstantData.resize(
int(blockSize), 0);
1810 const NSUInteger total = NSUInteger(cbD->pushConstantData.size());
1812 [cbD->d->currentRenderPassEncoder setVertexBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(vsIdx)];
1814 [cbD->d->currentRenderPassEncoder setFragmentBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(fsIdx)];
1836 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1841 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1842 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1843 workBuf->lastActiveFrameSlot = -1;
1845 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1846 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1847 workBuf->lastActiveFrameSlot = -1;
1850 psD->lastActiveFrameSlot = currentFrameSlot;
1854 int dynamicOffsetCount,
1855 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1864 srb = gfxPsD->m_shaderResourceBindings;
1866 srb = compPsD->m_shaderResourceBindings;
1870 bool hasSlottedResourceInSrb =
false;
1871 bool hasDynamicOffsetInSrb =
false;
1872 bool resNeedsRebind =
false;
1874 bool pipelineChanged =
false;
1887 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1890 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1891 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1894 case QRhiShaderResourceBinding::UniformBuffer:
1897 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1898 sanityCheckResourceOwnership(bufD);
1901 hasSlottedResourceInSrb =
true;
1902 if (b->u.ubuf.hasDynamicOffset)
1903 hasDynamicOffsetInSrb =
true;
1904 if (bufD
->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1905 resNeedsRebind =
true;
1906 bd.ubuf.id = bufD->m_id;
1909 bufD->lastActiveFrameSlot = currentFrameSlot;
1912 case QRhiShaderResourceBinding::SampledTexture:
1913 case QRhiShaderResourceBinding::Texture:
1914 case QRhiShaderResourceBinding::Sampler:
1916 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->stex;
1917 if (bd.stex.d.size() != data->count()) {
1918 bd.stex.d.resize(data->count());
1919 resNeedsRebind =
true;
1921 for (
int elem = 0; elem < data->count(); ++elem) {
1924 Q_ASSERT(texD || samplerD);
1925 sanityCheckResourceOwnership(texD);
1926 sanityCheckResourceOwnership(samplerD);
1927 const quint64 texId = texD ? texD->m_id : 0;
1929 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1930 const uint samplerGen = samplerD ? samplerD
->generation : 0;
1931 if (texGen != bd.stex.d[elem].texGeneration
1932 || texId != bd.stex.d[elem].texId
1933 || samplerGen != bd.stex.d[elem].samplerGeneration
1934 || samplerId != bd.stex.d[elem].samplerId)
1936 resNeedsRebind =
true;
1937 bd.stex.d[elem].texId = texId;
1938 bd.stex.d[elem].texGeneration = texGen;
1939 bd.stex.d[elem].samplerId = samplerId;
1940 bd.stex.d[elem].samplerGeneration = samplerGen;
1943 texD->lastActiveFrameSlot = currentFrameSlot;
1945 samplerD->lastActiveFrameSlot = currentFrameSlot;
1949 case QRhiShaderResourceBinding::ImageLoad:
1950 case QRhiShaderResourceBinding::ImageStore:
1951 case QRhiShaderResourceBinding::ImageLoadStore:
1954 sanityCheckResourceOwnership(texD);
1955 if (texD
->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1956 resNeedsRebind =
true;
1957 bd.simage.id = texD->m_id;
1960 texD->lastActiveFrameSlot = currentFrameSlot;
1963 case QRhiShaderResourceBinding::BufferLoad:
1964 case QRhiShaderResourceBinding::BufferStore:
1965 case QRhiShaderResourceBinding::BufferLoadStore:
1968 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1969 sanityCheckResourceOwnership(bufD);
1971 if (needsBufferSizeBuffer) {
1972 for (
int i = 0; i < 6; ++i) {
1973 const QRhiShaderResourceBinding::StageFlag stage =
1974 QRhiShaderResourceBinding::StageFlag(1 << i);
1975 if (b->stage.testFlag(stage)) {
1976 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1982 if (bufD
->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1983 resNeedsRebind =
true;
1984 bd.sbuf.id = bufD->m_id;
1987 bufD->lastActiveFrameSlot = currentFrameSlot;
1996 if (needsBufferSizeBuffer) {
1998 QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
2002 Q_ASSERT(compPsD
->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
2003 shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage});
2006 if (gfxPsD
->d->tess.enabled) {
2016 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
2017 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
2018 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
2019 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
2020 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
2021 == gfxPsD
->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
2022 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
2023 == gfxPsD
->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
2024 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
2025 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
2026 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
2027 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
2029 if (gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2030 shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage});
2032 if (gfxPsD
->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2033 shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage});
2035 if (gfxPsD
->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2036 shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage});
2039 if (gfxPsD
->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2040 shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage});
2042 if (gfxPsD
->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2043 shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage});
2047 for (
const auto &shader : shaders) {
2049 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
2052 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
2054 int maxNativeBinding = 0;
2055 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
2056 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
2058 const int size = (maxNativeBinding + 1) *
sizeof(
int);
2060 Q_ASSERT(offset + size <= bufD->size());
2061 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
2063 QMetalShaderResourceBindings::BoundResourceData bd;
2064 bd.sbuf.id = bufD->m_id;
2065 bd.sbuf.generation = bufD->generation;
2066 srbD->boundResourceData.append(bd);
2070 QVarLengthArray<
int, 8> bufferSizeBufferData;
2071 Q_ASSERT(storageBufferSizes.contains(shader.second));
2072 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
2073 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
2074 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
2080 if (bufferSizeBufferData.size() <= index)
2081 bufferSizeBufferData.resize(index + 1);
2083 Q_ASSERT(sizes.contains(block.binding));
2084 bufferSizeBufferData[index] = sizes[block.binding];
2087 QRhiBufferData data;
2088 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
2089 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
2090 Q_ASSERT(offset + size <= bufD->size());
2091 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
2094 offset += ((size + 31) / 32) * 32;
2098 bufD->lastActiveFrameSlot = currentFrameSlot;
2102 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
2104 resNeedsRebind =
true;
2110 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
2111 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
2116 if (gfxPsD
->d->tess.enabled) {
2119 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
2120 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
2139 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind
2140 && !srbChanged && !srbRebuilt && !pipelineChanged;
2141 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange,
2142 resBindMaps, shaders);
2147 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
2148 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
2153 QRhiBatchedBindings<id<MTLBuffer> > buffers;
2154 QRhiBatchedBindings<NSUInteger> offsets;
2155 for (
int i = 0; i < bindingCount; ++i) {
2158 bufD->lastActiveFrameSlot = currentFrameSlot;
2159 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
2160 buffers.feed(startBinding + i, mtlbuf);
2161 offsets.feed(startBinding + i, bindings[i].second);
2176 || buffers != cbD
->d->currentVertexInputsBuffers
2177 || offsets != cbD
->d->currentVertexInputOffsets)
2180 cbD
->d->currentVertexInputsBuffers = buffers;
2181 cbD
->d->currentVertexInputOffsets = offsets;
2183 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
2184 const auto &bufferBatch(buffers.batches[i]);
2185 const auto &offsetBatch(offsets.batches[i]);
2186 [cbD->d->currentRenderPassEncoder setVertexBuffers:
2187 bufferBatch.resources.constData()
2188 offsets: offsetBatch.resources.constData()
2189 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2196 ibufD->lastActiveFrameSlot = currentFrameSlot;
2198 cbD->currentIndexOffset = indexOffset;
2199 cbD->currentIndexFormat = indexFormat;
2210QSize
QRhiMetal::outputSizeForTarget(QRhiRenderTarget *target)
2212 QRhiShadingRateMap *srm =
nullptr;
2213 switch (target->resourceType()) {
2214 case QRhiResource::TextureRenderTarget:
2215 srm =
QRHI_RES(QMetalTextureRenderTarget, target)->m_desc.shadingRateMap();
2217 case QRhiResource::SwapChainRenderTarget:
2218 srm =
QRHI_RES(QMetalSwapChainRenderTarget, target)->swapChain()->shadingRateMap();
2225 const QSize logicalSize = srm->logicalSize();
2226 if (logicalSize.isValid())
2230 return target->pixelSize();
2237 const QSize outputSize = outputSizeForTarget(cbD->currentTarget);
2238 std::array<
float, 4> vp = cbD->currentViewport.viewport();
2239 float x = 0, y = 0, w = 0, h = 0;
2241 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
2244 w = outputSize.width();
2245 h = outputSize.height();
2248 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, vp, &x, &y, &w, &h);
2252 s.x = NSUInteger(x);
2253 s.y = NSUInteger(y);
2254 s.width = NSUInteger(w);
2255 s.height = NSUInteger(h);
2256 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2263 const QSize outputSize = outputSizeForTarget(cbD->currentTarget);
2267 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
2271 vp.originX =
double(x);
2272 vp.originY =
double(y);
2273 vp.width =
double(w);
2274 vp.height =
double(h);
2275 vp.znear =
double(viewport.minDepth());
2276 vp.zfar =
double(viewport.maxDepth());
2278 [cbD->d->currentRenderPassEncoder setViewport: vp];
2280 cbD->currentViewport = viewport;
2294 const QSize outputSize = outputSizeForTarget(cbD->currentTarget);
2298 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
2302 s.x = NSUInteger(x);
2303 s.y = NSUInteger(y);
2304 s.width = NSUInteger(w);
2305 s.height = NSUInteger(h);
2307 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2310 cbD->currentScissor = scissor;
2318 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
2319 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
2322 cbD->currentBlendConstants = c;
2330 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
2333 cbD->currentStencilRef = refValue;
2343 auto patch = [cbD, offset, size, data](quint32 blockSize) {
2344 const quint32 total = qMax(blockSize, offset + size);
2345 if (quint32(cbD->pushConstantData.size()) < total)
2346 cbD->pushConstantData.resize(
int(total), 0);
2347 memcpy(cbD->pushConstantData.data() + offset, data, size);
2355 const int idx = mtlPushConstantBufferIndex(psD
->d->cs);
2357 qWarning(
"No pipeline with a push constant block is active; setPushConstants ignored");
2360 const quint32 total = patch(mtlPushConstantBlockSize(psD->d->cs));
2361 [cbD->d->currentComputePassEncoder setBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(idx)];
2366 if (psD
->d->tess.enabled) {
2367 qWarning(
"Push constants are not supported with tessellation on Metal");
2370 const int vsIdx = mtlPushConstantBufferIndex(psD
->d->vs);
2371 const int fsIdx = mtlPushConstantBufferIndex(psD
->d->fs);
2372 if (vsIdx < 0 && fsIdx < 0) {
2373 qWarning(
"No pipeline with a push constant block is active; setPushConstants ignored");
2376 const quint32 total = patch(qMax(mtlPushConstantBlockSize(psD->d->vs), mtlPushConstantBlockSize(psD->d->fs)));
2378 [cbD->d->currentRenderPassEncoder setVertexBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(vsIdx)];
2380 [cbD->d->currentRenderPassEncoder setFragmentBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(fsIdx)];
2387 Q_UNUSED(coarsePixelSize);
2392 switch (cbD->currentTarget->resourceType()) {
2393 case QRhiResource::SwapChainRenderTarget:
2395 case QRhiResource::TextureRenderTarget:
2406 return tex && tex.storageMode != MTLStorageModeMemoryless;
2416 return finalAction == MTLStoreActionDontCare ? MTLStoreActionStore
2417 : MTLStoreActionStoreAndMultisampleResolve;
2427 for (
const auto &[index, finalAction] : std::as_const(cbD->d->deferredColorStoreActions))
2428 [cbD->d->currentRenderPassEncoder setColorStoreAction:
2429 interruptionStoreAction(finalAction, passIsEnding) atIndex: index];
2431 if (cbD->d->deferredDepthStoreAction != MTLStoreActionUnknown) {
2432 [cbD->d->currentRenderPassEncoder setDepthStoreAction:
2433 interruptionStoreAction(cbD->d->deferredDepthStoreAction, passIsEnding)];
2435 if (cbD->d->deferredStencilStoreAction != MTLStoreActionUnknown) {
2436 [cbD->d->currentRenderPassEncoder setStencilStoreAction:
2437 interruptionStoreAction(cbD->d->deferredStencilStoreAction, passIsEnding)];
2446 for (qsizetype i = 0; i < cbD->d->openDebugGroups.size(); ++i)
2447 [cbD->d->currentRenderPassEncoder popDebugGroup];
2448 [cbD->d->currentRenderPassEncoder endEncoding];
2449 cbD->d->currentRenderPassEncoder = nil;
2454 id<MTLComputeCommandEncoder> maybeComputeEncoder)
2456 if (cbD
->d->currentRenderPassEncoder)
2459 if (!maybeComputeEncoder)
2460 maybeComputeEncoder = [cbD->d->cb computeCommandEncoder];
2462 return maybeComputeEncoder;
2466 id<MTLComputeCommandEncoder> computeEncoder)
2468 if (computeEncoder) {
2469 [computeEncoder endEncoding];
2470 computeEncoder = nil;
2479 const auto canLoad = [](MTLRenderPassAttachmentDescriptor *att) {
2480 return att.storeAction != MTLStoreActionDontCare && canStoreAttachment(att.texture);
2483 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2485 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2486 if (canLoad(cbD->d->currentPassRpDesc.colorAttachments[i]))
2487 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2490 MTLLoadAction oldDepthLoad;
2491 MTLLoadAction oldStencilLoad;
2493 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2494 if (canLoad(cbD->d->currentPassRpDesc.depthAttachment))
2495 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2497 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2498 if (canLoad(cbD->d->currentPassRpDesc.stencilAttachment))
2499 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2506 const QRhiViewport prevViewport = cbD->currentViewport;
2508 const QRhiScissor prevScissor = cbD->currentScissor;
2510 const QColor prevBlendConstants = cbD->currentBlendConstants;
2512 const quint32 prevStencilRef = cbD->currentStencilRef;
2520 const QVarLengthArray<
char, 128> prevPushConstantData = cbD->pushConstantData;
2522 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2525 for (
const QByteArray &name : std::as_const(cbD->d->openDebugGroups))
2526 [cbD->d->currentRenderPassEncoder pushDebugGroup: [NSString stringWithUTF8String: name.constData()]];
2528 cbD->pushConstantData = prevPushConstantData;
2534 if (!qFuzzyIsNull(prevViewport.viewport()[2]) || !qFuzzyIsNull(prevViewport.viewport()[3]))
2535 rhiD->setViewport(cbD, prevViewport);
2537 rhiD->setScissor(cbD, prevScissor);
2538 else if (prevHasDefaultScissor)
2540 if (prevHasBlendConstants)
2541 rhiD->setBlendConstants(cbD, prevBlendConstants);
2542 if (prevHasStencilRef)
2543 rhiD->setStencilRef(cbD, prevStencilRef);
2546 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2550 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2551 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2560 if (graphicsPipeline
->d->tess.failed)
2564 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2565 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2569 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2575 id<MTLComputeCommandEncoder> vertTescComputeEncoder
2576 = tempComputeEncoder(
this, cbD, cbD->d->tessellationComputeEncoder);
2577 cbD
->d->tessellationComputeEncoder = vertTescComputeEncoder;
2581 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2582 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2583 if (args.type == TessDrawArgs::U16Indexed)
2584 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2585 else if (args.type == TessDrawArgs::U32Indexed)
2586 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2587 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2588 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2589 [computeEncoder setComputePipelineState: computePipelineState];
2594 cbD
->d->currentComputePassEncoder = computeEncoder;
2596 cbD->d->currentComputePassEncoder = nil;
2598 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2599 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2600 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2602 if (outputBufferBinding >= 0) {
2603 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2604 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2607 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2610 if (indexBufferBinding >= 0)
2611 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2613 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2614 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2615 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2616 [computeEncoder setBuffers: bufferBatch.resources.constData()
2617 offsets: offsetBatch.resources.constData()
2618 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2622 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2623 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2625 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2626 args.draw.vertexCount, args.draw.instanceCount)];
2629 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2630 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2635 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2636 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2637 [computeEncoder setComputePipelineState: computePipelineState];
2639 cbD
->d->currentComputePassEncoder = computeEncoder;
2641 cbD->d->currentComputePassEncoder = nil;
2643 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2644 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2645 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2646 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2647 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2648 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2650 if (outputBufferBinding >= 0) {
2651 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2652 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2655 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2658 if (patchOutputBufferBinding >= 0) {
2659 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2660 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2661 if (!tescPatchOutBuf)
2663 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2666 if (tessFactorBufferBinding >= 0) {
2667 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2668 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2671 if (paramsBufferBinding >= 0) {
2673 quint32 inControlPointCount;
2680 params.patchCount = patchCount;
2681 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2682 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2683 memcpy(p, ¶ms,
sizeof(params));
2684 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2687 if (vertOutBuf && inputBufferBinding >= 0)
2688 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2690 int sgSize =
int(computePipelineState.threadExecutionWidth);
2691 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2692 while (wgSize > caps.maxThreadGroupSize) {
2694 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2696 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2697 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2705 endTempComputeEncoding(
this, cbD, cbD
->d->tessellationComputeEncoder);
2706 cbD->d->tessellationComputeEncoder = nil;
2715 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2720 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2721 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2722 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2723 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2725 if (outputBufferBinding >= 0 && tescOutBuf)
2726 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2728 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2729 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2731 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2732 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2733 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2736 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2738 patchCount: patchCount
2739 patchIndexBuffer: nil
2740 patchIndexBufferOffset: 0
2750 if (multiViewCount <= 1)
2754 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2755 if (viewMaskBufBinding == -1) {
2756 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2763 multiViewInfo.viewOffset = 0;
2764 multiViewInfo.viewCount = quint32(multiViewCount);
2768 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2769 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2770 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2771 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2775 *instanceCount *= multiViewCount;
2780 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2789 a.draw.vertexCount = vertexCount;
2790 a.draw.instanceCount = instanceCount;
2791 a.draw.firstVertex = firstVertex;
2792 a.draw.firstInstance = firstInstance;
2797 adjustForMultiViewDraw(&instanceCount, cb);
2799 if (caps.baseVertexAndInstance) {
2800 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2801 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2803 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2804 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2809 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2817 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2818 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2821 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2826 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2827 a.drawIndexed.indexCount = indexCount;
2828 a.drawIndexed.instanceCount = instanceCount;
2829 a.drawIndexed.firstIndex = firstIndex;
2830 a.drawIndexed.vertexOffset = vertexOffset;
2831 a.drawIndexed.firstInstance = firstInstance;
2832 a.drawIndexed.indexBuffer = mtlibuf;
2837 adjustForMultiViewDraw(&instanceCount, cb);
2839 if (caps.baseVertexAndInstance) {
2840 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2841 indexCount: indexCount
2842 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2843 indexBuffer: mtlibuf
2844 indexBufferOffset: indexOffset
2845 instanceCount: instanceCount
2846 baseVertex: vertexOffset
2847 baseInstance: firstInstance];
2849 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2850 indexCount: indexCount
2851 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2852 indexBuffer: mtlibuf
2853 indexBufferOffset: indexOffset
2854 instanceCount: instanceCount];
2861 if (!caps.indirectCommandBuffers)
2862 return "indirect command buffers are not supported on this device";
2866 return "the current graphics pipeline was not created with UsesIndirectDraws";
2869 return "the current graphics pipeline uses tessellation";
2871 return "the shaders of the current graphics pipeline sample textures but have no "
2872 "argument buffer variant, which Metal requires for a pipeline that supports "
2873 "indirect command buffers; rebuild them with qsb --msl-argument-buffers "
2874 "(or use MSLARGUMENTBUFFERS with qt_add_shaders)";
2884 if (!
d->icbEncodePipeline) {
2886 NSString *src = [NSString stringWithUTF8String:s_icbEncodeMsl];
2887 MTLCompileOptions *opts = [MTLCompileOptions
new];
2888 opts.languageVersion = MTLLanguageVersion2_1;
2889 id<MTLLibrary> lib = [d->dev newLibraryWithSource:src options:opts error:&err];
2892 qWarning(
"Failed to compile ICB encode kernel: %s",
2893 qPrintable(QString::fromNSString(err.localizedDescription)));
2897 d->icbEncodeFunction = [lib newFunctionWithName:@
"encode_icb"];
2898 d->icbEncodeFunctionU32 = [lib newFunctionWithName:@
"encode_icb_indexed_u32"];
2899 d->icbEncodeFunctionU16 = [lib newFunctionWithName:@
"encode_icb_indexed_u16"];
2901 if (!
d->icbEncodeFunction || !
d->icbEncodeFunctionU32 || !
d->icbEncodeFunctionU16) {
2902 qWarning(
"ICB encode kernel functions not found");
2906 NSError *errU32 = nil;
2907 NSError *errU16 = nil;
2908 d->icbEncodePipeline = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunction error:&err];
2909 d->icbEncodePipelineU32 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU32 error:&errU32];
2910 d->icbEncodePipelineU16 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU16 error:&errU16];
2911 if (!
d->icbEncodePipeline || !
d->icbEncodePipelineU32 || !
d->icbEncodePipelineU16) {
2912 NSError *firstErr = !
d->icbEncodePipeline ? err
2913 : (!
d->icbEncodePipelineU32 ? errU32 : errU16);
2914 qWarning(
"Failed to create ICB encode compute pipeline: %s",
2915 qPrintable(QString::fromNSString(firstErr.localizedDescription)));
2921 if (!
d->icbRangeBuffer) {
2922 d->icbRangeBuffer = [d->dev newBufferWithLength:
sizeof(MTLIndirectCommandBufferExecutionRange)
2923 options:MTLResourceStorageModePrivate];
2924 static constexpr quint32 noCount = 0xFFFFFFFFu;
2925 d->icbNoCountBuffer = [d->dev newBufferWithBytes:&noCount
2926 length:
sizeof(noCount)
2927 options:MTLResourceStorageModeShared];
2928 if (!
d->icbRangeBuffer || !
d->icbNoCountBuffer) {
2929 qWarning(
"Failed to create ICB helper buffers");
2943 if (!
d->icb ||
d->icbCapacity < maxDrawCount) {
2947 e.lastActiveFrameSlot = currentFrameSlot;
2948 e.stagingIcbBuffer.icb =
d->icb;
2949 e.stagingIcbBuffer.argBuffer =
d->icbArgumentBuffer;
2950 d->releaseQueue.append(e);
2953 d->icbArgumentBuffer = nil;
2955 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
2956 icbDesc.commandTypes = MTLIndirectCommandTypeDraw | MTLIndirectCommandTypeDrawIndexed;
2957 icbDesc.inheritPipelineState = YES;
2958 icbDesc.inheritBuffers = YES;
2959 icbDesc.maxVertexBufferBindCount = 0;
2960 icbDesc.maxFragmentBufferBindCount = 0;
2961 d->icb = [d->dev newIndirectCommandBufferWithDescriptor:icbDesc
2962 maxCommandCount:maxDrawCount
2963 options:MTLResourceStorageModePrivate];
2966 qWarning(
"Failed to create MTLIndirectCommandBuffer");
2970 d->icbCapacity = maxDrawCount;
2972 id<MTLArgumentEncoder> argEnc = [d->icbEncodeFunction newArgumentEncoderWithBufferIndex:1];
2973 d->icbArgumentBuffer = [d->dev newBufferWithLength:argEnc.encodedLength
2974 options:MTLResourceStorageModeShared];
2975 [argEnc setArgumentBuffer:d->icbArgumentBuffer offset:0];
2976 [argEnc setIndirectCommandBuffer:d->icb atIndex:0];
2987 id<MTLComputeCommandEncoder> computeEncoder,
2988 id<MTLIndirectCommandBuffer> targetIcb,
2989 id<MTLBuffer> targetArgBuffer,
2990 id<MTLBuffer> targetRangeBuffer,
2992 QRhiCommandBuffer::IndexFormat indexFormat,
2993 MTLPrimitiveType primitiveType,
2994 id<MTLBuffer> indirectBufMtl, quint32 indirectBufferOffset,
2995 id<MTLBuffer> indexBufMtl, quint32 indexBufferOffset,
2996 id<MTLBuffer> countBufMtl, quint32 countBufferOffset,
2997 quint32 maxDrawCount, quint32 stride)
2999 id<MTLComputePipelineState> computePipeline = d->icbEncodePipeline;
3001 computePipeline = indexFormat == QRhiCommandBuffer::IndexUInt16
3002 ? d->icbEncodePipelineU16 : d->icbEncodePipelineU32;
3004 uint32_t maxDrawCountVal = maxDrawCount;
3005 uint32_t metalPrimType = uint32_t(primitiveType);
3006 uint32_t strideVal = stride;
3008 [computeEncoder setComputePipelineState:computePipeline];
3009 [computeEncoder setBuffer:indirectBufMtl offset:indirectBufferOffset atIndex:0];
3010 [computeEncoder setBuffer:targetArgBuffer offset:0 atIndex:1];
3011 [computeEncoder setBytes:&maxDrawCountVal length:
sizeof(uint32_t) atIndex:2];
3013 [computeEncoder setBuffer:indexBufMtl offset:indexBufferOffset atIndex:3];
3014 [computeEncoder setBytes:&metalPrimType length:
sizeof(uint32_t) atIndex:4];
3015 [computeEncoder setBytes:&strideVal length:
sizeof(uint32_t) atIndex:5];
3016 [computeEncoder setBuffer:countBufMtl ? countBufMtl : d->icbNoCountBuffer
3017 offset:countBufMtl ? countBufferOffset : 0
3019 [computeEncoder setBuffer:targetRangeBuffer offset:0 atIndex:7];
3020 [computeEncoder useResource:targetIcb usage:MTLResourceUsageWrite];
3021 [computeEncoder useResource:indirectBufMtl usage:MTLResourceUsageRead];
3023 [computeEncoder useResource:indexBufMtl usage:MTLResourceUsageRead];
3025 NSUInteger tw = computePipeline.threadExecutionWidth;
3026 [computeEncoder dispatchThreads:MTLSizeMake(maxDrawCount, 1, 1)
3027 threadsPerThreadgroup:MTLSizeMake(tw, 1, 1)];
3037 QMetalBuffer *indirectBufD, quint32 indirectBufferOffset,
3039 quint32 maxDrawCount, quint32 stride)
3045 if (indexed && !indexBufD)
3048 if (!prepareIcb(maxDrawCount))
3052 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
3053 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3055 id<MTLBuffer> countBufMtl = nil;
3058 countBufD->lastActiveFrameSlot = currentFrameSlot;
3059 countBufMtl = countBufD->d->buf[countBufD->d->slotted ? currentFrameSlot : 0];
3066 const auto savedVertexBuffers = cbD
->d->currentVertexInputsBuffers;
3067 const auto savedVertexOffsets = cbD
->d->currentVertexInputOffsets;
3068 const quint32 savedIndexOffset = cbD->currentIndexOffset;
3069 const QRhiCommandBuffer::IndexFormat savedIndexFormat = cbD->currentIndexFormat;
3070 id<MTLBuffer> indexBufMtl = indexed
3071 ? indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0] : nil;
3076 id<MTLComputeCommandEncoder> computeEncoder = [cbD->d->cb computeCommandEncoder];
3077 encodeIcbWithCompute(
d, computeEncoder,
d->icb,
d->icbArgumentBuffer,
d->icbRangeBuffer,
3078 indexed, savedIndexFormat, savedPipeline
->d->primitiveType,
3079 indirectBufMtl, indirectBufferOffset,
3080 indexBufMtl, savedIndexOffset,
3081 countBufMtl, countBufferOffset,
3082 maxDrawCount, stride);
3085 endTempComputeEncoding(
this, cbD, computeEncoder);
3094 if (savedFirstVertexBinding >= 0) {
3096 cbD
->d->currentVertexInputsBuffers = savedVertexBuffers;
3097 cbD
->d->currentVertexInputOffsets = savedVertexOffsets;
3098 for (
int i = 0, ie = savedVertexBuffers.batches.count(); i != ie; ++i) {
3099 const auto &bufferBatch(savedVertexBuffers.batches[i]);
3100 const auto &offsetBatch(savedVertexOffsets.batches[i]);
3101 [cbD->d->currentRenderPassEncoder setVertexBuffers:
3102 bufferBatch.resources.constData()
3103 offsets: offsetBatch.resources.constData()
3104 withRange: NSMakeRange(uint(savedFirstVertexBinding) + bufferBatch.startBinding,
3105 NSUInteger(bufferBatch.resources.count()))];
3110 cbD->currentIndexOffset = savedIndexOffset;
3111 cbD->currentIndexFormat = savedIndexFormat;
3115 [cbD->d->currentRenderPassEncoder useResource:indirectBufMtl
3116 usage:MTLResourceUsageRead
3117 stages:MTLRenderStageVertex | MTLRenderStageFragment];
3119 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
3120 usage:MTLResourceUsageRead
3121 stages:MTLRenderStageVertex | MTLRenderStageFragment];
3123 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:d->icb
3124 indirectBuffer:d->icbRangeBuffer
3125 indirectBufferOffset:0];
3138 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
3145 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
3146 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3148 if (drawCount > ICB_DRAW_COUNT_THRESHOLD && !icbUnavailableReason(cbD)
3149 && icbDraw(cbD,
false, indirectBufD, indirectBufferOffset,
nullptr, 0, drawCount, stride))
3155 NSUInteger offset = indirectBufferOffset;
3156 for (quint32 i = 0; i < drawCount; ++i) {
3157 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
3158 indirectBuffer: indirectBufMtl
3159 indirectBufferOffset: offset];
3165 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
3174 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
3178 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
3179 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3181 if (drawCount > ICB_DRAW_COUNT_THRESHOLD && !icbUnavailableReason(cbD)
3182 && icbDraw(cbD,
true, indirectBufD, indirectBufferOffset,
nullptr, 0, drawCount, stride))
3188 NSUInteger offset = indirectBufferOffset;
3189 for (quint32 i = 0; i < drawCount; ++i) {
3190 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
3191 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
3192 indexBuffer: indexBufMtl
3193 indexBufferOffset: cbD->currentIndexOffset
3194 indirectBuffer: indirectBufMtl
3195 indirectBufferOffset: offset];
3205 NSString *str = [NSString stringWithUTF8String: name.constData()];
3208 case QMetalCommandBuffer::RenderPass:
3209 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
3210 cbD
->d->openDebugGroups.append(name);
3212 case QMetalCommandBuffer::ComputePass:
3213 [cbD->d->currentComputePassEncoder pushDebugGroup: str];
3216 [cbD->d->cb pushDebugGroup: str];
3228 case QMetalCommandBuffer::RenderPass:
3229 [cbD->d->currentRenderPassEncoder popDebugGroup];
3230 if (!cbD
->d->openDebugGroups.isEmpty())
3231 cbD
->d->openDebugGroups.removeLast();
3233 case QMetalCommandBuffer::ComputePass:
3234 [cbD->d->currentComputePassEncoder popDebugGroup];
3237 [cbD->d->cb popDebugGroup];
3248 NSString *str = [NSString stringWithUTF8String: msg.constData()];
3250 case QMetalCommandBuffer::RenderPass:
3251 [cbD->d->currentRenderPassEncoder insertDebugSignpost: str];
3253 case QMetalCommandBuffer::ComputePass:
3254 [cbD->d->currentComputePassEncoder insertDebugSignpost: str];
3263 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
3289 currentFrameSlot = swapChainD->currentFrameSlot;
3294 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
3302 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
3303 if (sc != swapChainD)
3304 sc->waitUntilCompleted(currentFrameSlot);
3307 [d->captureScope beginScope];
3309 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
3313 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
3320 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
3321 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
3326 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
3328 d->argBufPool[currentFrameSlot].offset = 0;
3330 d->globalFrameId += 1;
3333 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
3334 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
3337 return QRhi::FrameOpSuccess;
3346 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
3348 __block
int thisFrameSlot = currentFrameSlot;
3349 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
3350 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
3351 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
3358 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
3359 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
3360 [drawableTexture release];
3364 if (flags.testFlag(QRhi::SkipPresent)) {
3366 [commandBuffer commit];
3368 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
3370 if (swapChainD
->d->layer.presentsWithTransaction) {
3371 [commandBuffer commit];
3373 auto *metalLayer = swapChainD
->d->layer;
3374 auto presentWithTransaction = ^{
3375 [commandBuffer waitUntilScheduled];
3382 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
3383 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
3384 if (textureSize == surfaceSize) {
3387 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
3388 << textureSize <<
"not matching surface size" << surfaceSize;
3392 if (NSThread.currentThread == NSThread.mainThread) {
3393 presentWithTransaction();
3395 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
3396 Q_ASSERT(qtMetalLayer);
3398 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
3402 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
3403 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
3409 if (qtMetalLayer.displayLock.tryLockForRead()) {
3411 qtMetalLayer.displayLock.unlock();
3413 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
3414 <<
"due to" << qtMetalLayer <<
"needing display";
3420 [commandBuffer commit];
3424 [commandBuffer commit];
3431 [swapChainD->d->curDrawable release];
3432 swapChainD->d->curDrawable = nil;
3434 [d->captureScope endScope];
3438 return QRhi::FrameOpSuccess;
3445 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
3447 for (QMetalSwapChain *sc : std::as_const(swapchains))
3448 sc->waitUntilCompleted(currentFrameSlot);
3450 d->ofr.active =
true;
3451 *cb = &
d->ofr.cbWrapper;
3452 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
3454 d->argBufPool[currentFrameSlot].offset = 0;
3456 d->globalFrameId += 1;
3459 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
3460 d->ofr.lastGpuTime = 0;
3463 return QRhi::FrameOpSuccess;
3469 Q_ASSERT(
d->ofr.active);
3470 d->ofr.active =
false;
3472 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
3476 [cb waitUntilCompleted];
3478 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
3482 return QRhi::FrameOpSuccess;
3487 id<MTLCommandBuffer> cb = nil;
3490 if (
d->ofr.active) {
3493 cb =
d->ofr.cbWrapper.d->cb;
3498 cb = swapChainD->cbWrapper.d->cb;
3502 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
3503 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3504 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
3509 sc->waitUntilCompleted(i);
3515 [cb waitUntilCompleted];
3519 if (
d->ofr.active) {
3520 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
3521 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
3523 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
3524 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
3532 return QRhi::FrameOpSuccess;
3536 const QColor &colorClearValue,
3537 const QRhiDepthStencilClearValue &depthStencilClearValue,
3539 QRhiShadingRateMap *shadingRateMap)
3541 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
3542 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
3543 colorClearValue.alphaF());
3545 for (uint i = 0; i < uint(colorAttCount); ++i) {
3546 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
3547 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
3548 rp.colorAttachments[i].clearColor = c;
3551 if (hasDepthStencil) {
3552 rp.depthAttachment.loadAction = MTLLoadActionClear;
3553 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
3554 rp.stencilAttachment.loadAction = MTLLoadActionClear;
3555 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
3556 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
3557 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
3561 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
3569 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3570 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3571 if (imageSizeBytes > 0)
3572 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3577 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
3580 const QPoint dp = subresDesc.destinationTopLeft();
3581 const QByteArray rawData = subresDesc.data();
3582 QImage img = subresDesc.image();
3583 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3584 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
3586 if (!img.isNull()) {
3587 const qsizetype fullImageSizeBytes = img.sizeInBytes();
3588 QSize size = img.size();
3589 int bpl = img.bytesPerLine();
3591 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3592 const int sx = subresDesc.sourceTopLeft().x();
3593 const int sy = subresDesc.sourceTopLeft().y();
3594 if (!subresDesc.sourceSize().isEmpty())
3595 size = subresDesc.sourceSize();
3596 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3597 if (size.width() == img.width()) {
3598 const int bpc = qMax(1, img.depth() / 8);
3599 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
3600 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
3601 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
3602 size.height() * img.bytesPerLine());
3604 img = img.copy(sx, sy, size.width(), size.height());
3605 bpl = img.bytesPerLine();
3606 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
3607 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
3610 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3611 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
3614 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3615 sourceOffset: NSUInteger(*curOfs)
3616 sourceBytesPerRow: NSUInteger(bpl)
3617 sourceBytesPerImage: 0
3618 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
3619 toTexture: texD->d->tex
3620 destinationSlice: NSUInteger(is3D ? 0 : layer)
3621 destinationLevel: NSUInteger(level)
3622 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3623 options: MTLBlitOptionNone];
3625 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3626 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3627 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3628 const int subresw = subresSize.width();
3629 const int subresh = subresSize.height();
3631 if (subresDesc.sourceSize().isEmpty()) {
3635 w = subresDesc.sourceSize().width();
3636 h = subresDesc.sourceSize().height();
3641 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
3643 const int dx = aligned(dp.x(), blockDim.width());
3644 const int dy = aligned(dp.y(), blockDim.height());
3645 if (dx + w != subresw)
3646 w = aligned(w, blockDim.width());
3647 if (dy + h != subresh)
3648 h = aligned(h, blockDim.height());
3650 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3652 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3653 sourceOffset: NSUInteger(*curOfs)
3654 sourceBytesPerRow: bpl
3655 sourceBytesPerImage: 0
3656 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3657 toTexture: texD->d->tex
3658 destinationSlice: NSUInteger(is3D ? 0 : layer)
3659 destinationLevel: NSUInteger(level)
3660 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
3661 options: MTLBlitOptionNone];
3663 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3664 }
else if (!rawData.isEmpty()) {
3665 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3666 const int subresw = subresSize.width();
3667 const int subresh = subresSize.height();
3669 if (subresDesc.sourceSize().isEmpty()) {
3673 w = subresDesc.sourceSize().width();
3674 h = subresDesc.sourceSize().height();
3677 QSize size = clampedSubResourceUploadSize(QSize(w, h), dp, level, texD->m_pixelSize);
3678 quint32 bytesPerPixel = 0;
3679 textureFormatInfo(texD->m_format, size,
nullptr,
nullptr, &bytesPerPixel);
3680 size = clampedSubResourceUploadSizeForSourceData(size, subresDesc.dataStride(),
3681 bytesPerPixel, rawData.size());
3686 if (subresDesc.dataStride())
3687 bpl = subresDesc.dataStride();
3689 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
3691 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3693 if (!size.isEmpty()) {
3694 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3695 sourceOffset: NSUInteger(*curOfs)
3696 sourceBytesPerRow: bpl
3697 sourceBytesPerImage: 0
3698 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3699 toTexture: texD->d->tex
3700 destinationSlice: NSUInteger(is3D ? 0 : layer)
3701 destinationLevel: NSUInteger(level)
3702 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3703 options: MTLBlitOptionNone];
3706 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3708 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3717 id<MTLBlitCommandEncoder> blitEnc = nil;
3718 auto ensureBlit = [&blitEnc, cbD,
this]() {
3720 blitEnc = [cbD->d->cb blitCommandEncoder];
3722 [blitEnc pushDebugGroup: @
"Resource updates"];
3730 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3732 if (u.offset == 0 && u
.data.size() == bufD->m_size)
3733 bufD
->d->pendingUpdates[i].clear();
3734 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3738 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3739 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
3741 const quint32 size = quint32(u
.data.size());
3743 quint32 stagingOffset = 0;
3744 id<MTLBuffer> stagingBuf =
d->allocBufferStaging(size, currentFrameSlot, &stagingOffset);
3746 char *p =
reinterpret_cast<
char *>([stagingBuf contents]);
3749 [blitEnc copyFromBuffer: stagingBuf
3750 sourceOffset: stagingOffset
3751 toBuffer: bufD->d->buf[0]
3752 destinationOffset: u.offset
3755 qWarning(
"Failed to allocate staging buffer of size %u for buffer upload", size);
3758 bufD->lastActiveFrameSlot = currentFrameSlot;
3761 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3766 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
3767 if (bufD->m_type == QRhiBuffer::Dynamic) {
3768 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
3770 u.result->data.resize(u.readSize);
3771 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
3773 if (u.result->completed)
3774 u.result->completed();
3784 readback.activeFrameSlot = currentFrameSlot;
3785 readback.readSize = u.readSize;
3786 readback.result = u.result;
3787 readback.buf = [d->dev newBufferWithLength: u.readSize
3788 options: MTLResourceStorageModeShared];
3791 [blitEnc copyFromBuffer: bufD->d->buf[idx]
3792 sourceOffset: u.offset
3793 toBuffer: readback.buf
3794 destinationOffset: 0
3797 d->activeBufferReadbacks.append(readback);
3798 bufD->lastActiveFrameSlot = currentFrameSlot;
3803 Q_ASSERT(dstD->m_type != QRhiBuffer::Dynamic && srcD->m_type != QRhiBuffer::Dynamic);
3806 const int srcIdx = srcD->d->slotted ? currentFrameSlot : 0;
3817 for (
int i = 0; i != dstSlotCount; ++i)
3821 for (
int i = 0; i != dstSlotCount; ++i) {
3822 [blitEnc copyFromBuffer: srcD->d->buf[srcIdx]
3823 sourceOffset: u.srcOffset
3824 toBuffer: dstD->d->buf[i]
3825 destinationOffset: u.offset
3829 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3832 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic && !bufD->d->slotted);
3835 [blitEnc fillBuffer: bufD->d->buf[0]
3836 range: NSMakeRange(u.offset, u.readSize)
3837 value: u.fillValue];
3838 bufD->lastActiveFrameSlot = currentFrameSlot;
3846 qsizetype stagingSize = 0;
3847 for (
const auto &subres : u.subresDesc)
3848 stagingSize += subresUploadByteSize(subres.desc);
3851 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
3852 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
3853 options: MTLResourceStorageModeShared];
3855 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
3856 qsizetype curOfs = 0;
3857 for (
const auto &subres : u.subresDesc)
3858 enqueueSubresUpload(utexD, mp, blitEnc, subres.layer, subres.level, subres.desc, &curOfs);
3860 utexD->lastActiveFrameSlot = currentFrameSlot;
3864 e.lastActiveFrameSlot = currentFrameSlot;
3865 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
3866 utexD->d->stagingBuf[currentFrameSlot] = nil;
3867 d->releaseQueue.append(e);
3872 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3873 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3874 const QPoint dp = u.desc.destinationTopLeft();
3875 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3876 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3877 const QPoint sp = u.desc.sourceTopLeft();
3880 [blitEnc copyFromTexture: srcD->d->tex
3881 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
3882 sourceLevel: NSUInteger(u.desc.sourceLevel())
3883 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
3884 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
3885 toTexture: dstD->d->tex
3886 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
3887 destinationLevel: NSUInteger(u.desc.destinationLevel())
3888 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
3890 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3893 readback.activeFrameSlot = currentFrameSlot;
3894 readback.desc = u.rb;
3895 readback.result = u.result;
3904 qWarning(
"Multisample texture cannot be read back");
3907 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3908 if (u.rb.rect().isValid())
3911 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3912 readback.format = texD->m_format;
3914 texD->lastActiveFrameSlot = currentFrameSlot;
3918 if (u.rb.rect().isValid())
3921 rect = QRect({0, 0}, swapChainD->pixelSize);
3922 readback.format = swapChainD
->d->rhiColorFormat;
3926 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
3928 readback.pixelSize = rect.size();
3931 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
3942 const MTLPixelFormat mtlFormat = texD ? texD->d->format : MTLPixelFormatInvalid;
3943 const bool isDepthFormat = texD && (readback.format == QRhiTexture::D16
3944 || readback.format == QRhiTexture::D24
3945 || readback.format == QRhiTexture::D24S8
3946 || readback.format == QRhiTexture::D32F
3947 || readback.format == QRhiTexture::D32FS8);
3948 const bool nativeLayout = (readback.format == QRhiTexture::D16 && mtlFormat == MTLPixelFormatDepth16Unorm)
3949 || (readback.format == QRhiTexture::D32F && mtlFormat == MTLPixelFormatDepth32Float);
3950 const bool usePlanes = isDepthFormat && !nativeLayout;
3951 bool combinedMtlFormat = mtlFormat == MTLPixelFormatDepth32Float_Stencil8;
3953 if (mtlFormat == MTLPixelFormatDepth24Unorm_Stencil8)
3954 combinedMtlFormat =
true;
3956 const bool hasStencil = isStencilSupportingFormat(readback.format);
3957 quint32 depthPlaneBytesPerRow = 0;
3961 depthPlaneBytesPerRow = quint32(rect.width()) * 4;
3962 readback.depthPlaneByteSize = depthPlaneBytesPerRow * quint32(rect.height());
3964 readback.stencilPlaneByteSize = quint32(rect.width()) * quint32(rect.height());
3965 readback.depthPlaneIsFloat = mtlFormat == MTLPixelFormatDepth32Float
3966 || mtlFormat == MTLPixelFormatDepth32Float_Stencil8;
3968 const quint32 stagingBufSize = usePlanes
3969 ? readback.depthPlaneByteSize + readback.stencilPlaneByteSize
3971 readback.buf = [d->dev newBufferWithLength: stagingBufSize options: MTLResourceStorageModeShared];
3974 const auto blit = [&](quint32 destinationOffset, quint32 destinationBytesPerRow, MTLBlitOption options) {
3975 [blitEnc copyFromTexture: src
3976 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
3977 sourceLevel: NSUInteger(u.rb.level())
3978 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
3979 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
3980 toBuffer: readback.buf
3981 destinationOffset: destinationOffset
3982 destinationBytesPerRow: destinationBytesPerRow
3983 destinationBytesPerImage: 0
3987 blit(0, depthPlaneBytesPerRow,
3988 combinedMtlFormat ? MTLBlitOptionDepthFromDepthStencil : MTLBlitOptionNone);
3990 blit(readback.depthPlaneByteSize, quint32(rect.width()), MTLBlitOptionStencilFromDepthStencil);
3992 blit(0, bpl, MTLBlitOptionNone);
3995 d->activeTextureReadbacks.append(readback);
3999 [blitEnc generateMipmapsForTexture: utexD->d->tex];
4000 utexD->lastActiveFrameSlot = currentFrameSlot;
4006 [blitEnc popDebugGroup];
4007 [blitEnc endEncoding];
4016 if (bufD
->d->pendingUpdates[slot].isEmpty())
4021 void *p = [bufD->d->buf[slot] contents];
4022 quint32 changeBegin = UINT32_MAX;
4023 quint32 changeEnd = 0;
4024 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
4025 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
4026 if (u.offset < changeBegin)
4027 changeBegin = u.offset;
4028 if (u.offset + u.data.size() > changeEnd)
4029 changeEnd = u.offset + u.data.size();
4032 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
4033 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
4036 bufD
->d->pendingUpdates[slot].clear();
4046 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
4052 QRhiRenderTarget *rt,
4053 const QColor &colorClearValue,
4054 const QRhiDepthStencilClearValue &depthStencilClearValue,
4055 QRhiResourceUpdateBatch *resourceUpdates,
4061 if (resourceUpdates)
4065 switch (rt->resourceType()) {
4066 case QRhiResource::SwapChainRenderTarget:
4070 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
4073 depthStencilClearValue,
4081 if (!swapChainD
->d->curDrawable) {
4082 QMacAutoReleasePool pool;
4083 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
4085 if (!swapChainD
->d->curDrawable) {
4086 qWarning(
"No drawable");
4089 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
4094 color0.resolveTex = scTex;
4100 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
4103 case QRhiResource::TextureRenderTarget:
4107 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
4111 depthStencilClearValue,
4113 rtTex->m_desc.shadingRateMap());
4114 if (rtD->fb.preserveColor) {
4115 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
4116 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
4119 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
4120 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
4122 int colorAttCount = 0;
4123 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
4127 if (it->texture()) {
4128 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
4129 if (it->multiViewCount() >= 2)
4130 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
4131 }
else if (it->renderBuffer()) {
4132 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
4134 if (it->resolveTexture())
4135 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
4137 if (rtTex->m_desc.depthStencilBuffer())
4138 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
4139 if (rtTex->m_desc.depthTexture()) {
4141 depthTexture->lastActiveFrameSlot = currentFrameSlot;
4142 if (depthTexture->arraySize() >= 2) {
4143 const int depthLayer = rtTex->m_desc.depthLayer();
4144 if (depthLayer >= 0) {
4145 cbD
->d->currentPassRpDesc.depthAttachment.slice = NSUInteger(depthLayer);
4146 cbD
->d->currentPassRpDesc.stencilAttachment.slice = NSUInteger(depthLayer);
4147 if (colorAttCount == 0)
4148 cbD
->d->currentPassRpDesc.renderTargetArrayLength = 1;
4149 }
else if (colorAttCount == 0) {
4150 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
4154 if (rtTex->m_desc.depthResolveTexture())
4155 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
4156 if (rtTex->m_desc.shadingRateMap())
4157 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
4165 cbD
->d->deferredColorStoreActions.clear();
4166 cbD->d->deferredDepthStoreAction = MTLStoreActionUnknown;
4167 cbD->d->deferredStencilStoreAction = MTLStoreActionUnknown;
4169 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
4170 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
4171 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
4172 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
4173 if (rtD->fb.colorAtt[i].resolveTex) {
4174 const MTLStoreAction storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
4175 : MTLStoreActionMultisampleResolve;
4179 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionUnknown;
4180 cbD
->d->deferredColorStoreActions.append({ i, storeAction });
4181 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
4182 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
4183 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
4188 Q_ASSERT(rtD->fb.dsTex);
4189 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
4190 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
4191 if (rtD->fb.depthNeedsStore) {
4192 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
4193 if (rtD->fb.hasStencil)
4194 cbD->d->currentPassRpDesc.stencilAttachment.storeAction = MTLStoreActionStore;
4195 }
else if (canStoreAttachment(rtD->fb.dsTex)) {
4199 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionUnknown;
4200 cbD->d->deferredDepthStoreAction = MTLStoreActionDontCare;
4201 if (rtD->fb.hasStencil) {
4202 cbD->d->currentPassRpDesc.stencilAttachment.storeAction = MTLStoreActionUnknown;
4203 cbD->d->deferredStencilStoreAction = MTLStoreActionDontCare;
4206 if (rtD->fb.dsResolveTex) {
4207 const MTLStoreAction dsStoreAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
4208 : MTLStoreActionMultisampleResolve;
4214 const bool deferrable = canStoreAttachment(rtD->fb.dsTex);
4215 cbD->d->currentPassRpDesc.depthAttachment.storeAction = deferrable ? MTLStoreActionUnknown
4218 cbD
->d->deferredDepthStoreAction = dsStoreAction;
4219 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
4220 if (rtD->fb.hasStencil) {
4221 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
4222 cbD->d->currentPassRpDesc.stencilAttachment.storeAction = deferrable ? MTLStoreActionUnknown
4225 cbD
->d->deferredStencilStoreAction = dsStoreAction;
4230 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
4235 cbD->currentTarget = rt;
4244 [cbD->d->currentRenderPassEncoder endEncoding];
4247 cbD->currentTarget =
nullptr;
4249 if (resourceUpdates)
4254 QRhiResourceUpdateBatch *resourceUpdates,
4260 if (resourceUpdates)
4263 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
4273 [cbD->d->currentComputePassEncoder endEncoding];
4276 if (resourceUpdates)
4291 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
4294 psD->lastActiveFrameSlot = currentFrameSlot;
4303 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
4304 threadsPerThreadgroup: psD->d->localSize];
4308 quint32 indirectBufferOffset)
4316 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
4317 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
4324 [cbD->d->currentComputePassEncoder
4325 dispatchThreadgroupsWithIndirectBuffer: indirectBufMtl
4326 indirectBufferOffset: indirectBufferOffset
4327 threadsPerThreadgroup: psD->d->localSize];
4331 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
4332 QRhiBuffer *countBuffer, quint32 countBufferOffset,
4333 quint32 maxDrawCount, quint32 stride)
4341 qWarning(
"drawIndirectCount is not available because %s; skipping", reason);
4345 icbDraw(cbD,
false,
QRHI_RES(QMetalBuffer, indirectBuffer), indirectBufferOffset,
4346 QRHI_RES(QMetalBuffer, countBuffer), countBufferOffset, maxDrawCount, stride);
4350 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
4351 QRhiBuffer *countBuffer, quint32 countBufferOffset,
4352 quint32 maxDrawCount, quint32 stride)
4360 qWarning(
"drawIndexedIndirectCount is not available because %s; skipping", reason);
4365 qWarning(
"drawIndexedIndirectCount called without an index buffer bound; skipping");
4369 icbDraw(cbD,
true,
QRHI_RES(QMetalBuffer, indirectBuffer), indirectBufferOffset,
4370 QRHI_RES(QMetalBuffer, countBuffer), countBufferOffset, maxDrawCount, stride);
4414 e.stagingIcbBuffer.icb = slot.icb;
4415 e.stagingIcbBuffer.argBuffer = slot.argBuffer;
4416 rhiD
->d->releaseQueue.append(e);
4418 if (slot.rangeBuffer) {
4422 e.stagingBuffer.buffer = slot.rangeBuffer;
4423 rhiD
->d->releaseQueue.append(e);
4438 if (icbD->d->fill == fill)
4439 return icbD->d->frameSlots[0].icb != nil;
4447 if (!rhiD->caps.indirectCommandBuffers)
4454 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
4455 icbDesc.commandTypes = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws
4456 ? MTLIndirectCommandTypeDrawIndexed : MTLIndirectCommandTypeDraw;
4458 icbDesc.inheritPipelineState = YES;
4459 icbDesc.inheritBuffers = YES;
4460 icbDesc.maxVertexBufferBindCount = 0;
4461 icbDesc.maxFragmentBufferBindCount = 0;
4466 slot.icb = [rhiD->d->dev newIndirectCommandBufferWithDescriptor:icbDesc
4467 maxCommandCount:icbD->maxCommandCount()
4468 options:gpu ? MTLResourceStorageModePrivate
4469 : MTLResourceStorageModeShared];
4471 qWarning(
"Failed to create MTLIndirectCommandBuffer");
4476 slot.rangeBuffer = [rhiD->d->dev newBufferWithLength:
sizeof(MTLIndirectCommandBufferExecutionRange)
4477 options:MTLResourceStorageModePrivate];
4478 id<MTLArgumentEncoder> argEnc = [rhiD->d->icbEncodeFunction newArgumentEncoderWithBufferIndex:1];
4479 slot.argBuffer = [rhiD->d->dev newBufferWithLength:argEnc.encodedLength
4480 options:MTLResourceStorageModeShared];
4481 if (slot.rangeBuffer && slot.argBuffer) {
4482 [argEnc setArgumentBuffer:slot.argBuffer offset:0];
4483 [argEnc setIndirectCommandBuffer:slot.icb atIndex:0];
4485 qWarning(
"Failed to create MTLIndirectCommandBuffer helper buffers");
4497 [icbD->d->frameSlots[i].icb release];
4498 [icbD->d->frameSlots[i].rangeBuffer release];
4499 [icbD->d->frameSlots[i].argBuffer release];
4500 icbD
->d->frameSlots[i] = {};
4511 quint32 maxCommandCount)
4537 m_gpuBuiltCommandCount = 0;
4542 rhiD->unregisterResource(
this);
4553 if (!m_maxCommandCount) {
4554 qWarning(
"QRhiIndirectCommandBuffer: maxCommandCount is 0");
4561 rhiD->registerResource(
this);
4565QRhiIndirectCommandBuffer *
QRhiMetal::createIndirectCommandBuffer(QRhiIndirectCommandBuffer::Type type,
4566 quint32 maxCommandCount)
4568 return new QMetalIndirectCommandBuffer(
this, type, maxCommandCount);
4572 QRhiIndirectCommandBuffer *icb)
4584 MTLPrimitiveType primitiveType,
4585 id<MTLBuffer> indexBufMtl, quint32 indexOffset,
4586 QRhiCommandBuffer::IndexFormat indexFormat)
4588 const bool indexed = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4591 && slot.generation == icbD->contentsGeneration()
4592 && slot.primitiveType == primitiveType
4593 && (!indexed || (slot.indexBuf == indexBufMtl
4594 && slot.indexOffset == indexOffset
4595 && slot.indexFormat == indexFormat));
4601 MTLPrimitiveType primitiveType,
4602 id<MTLBuffer> indexBufMtl, quint32 indexOffset,
4603 QRhiCommandBuffer::IndexFormat indexFormat)
4605 const bool indexed = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4607 if (qrhimtl_icbSlotMatches(icbD, slot, primitiveType, indexBufMtl, indexOffset, indexFormat))
4610 const quint32 count = icbD->recordedCommandCount();
4612 const MTLIndexType indexType = indexFormat == QRhiCommandBuffer::IndexUInt16
4613 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32;
4614 const quint32 indexSize = indexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4;
4616 for (quint32 i = 0; i < count; ++i) {
4618 id<MTLIndirectRenderCommand> rc = [slot.icb indirectRenderCommandAtIndex:i];
4619 [rc drawIndexedPrimitives:primitiveType
4620 indexCount:c.indexCount
4622 indexBuffer:indexBufMtl
4623 indexBufferOffset:indexOffset + c.firstIndex * indexSize
4624 instanceCount:c.instanceCount
4625 baseVertex:c.vertexOffset
4626 baseInstance:c.firstInstance];
4629 const QRhiIndirectDrawCommand *cmds = icbD->drawCommands();
4630 for (quint32 i = 0; i < count; ++i) {
4631 const QRhiIndirectDrawCommand &c(cmds[i]);
4632 id<MTLIndirectRenderCommand> rc = [slot.icb indirectRenderCommandAtIndex:i];
4633 [rc drawPrimitives:primitiveType
4634 vertexStart:c.firstVertex
4635 vertexCount:c.vertexCount
4636 instanceCount:c.instanceCount
4637 baseInstance:c.firstInstance];
4642 if (count < icbD->maxCommandCount())
4643 [slot.icb resetWithRange:NSMakeRange(count, icbD->maxCommandCount() - count)];
4645 slot.generation = icbD->contentsGeneration();
4646 slot.primitiveType = primitiveType;
4647 slot.indexBuf = indexBufMtl;
4648 slot.indexOffset = indexOffset;
4649 slot.indexFormat = indexFormat;
4655 quint32 firstCommand, quint32 count,
4656 int currentFrameSlot)
4660 if (icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws) {
4664 id<MTLBuffer> indexBufMtl = indexBufD
->d->buf[indexBufD
->d->slotted ? currentFrameSlot : 0];
4665 const MTLIndexType indexType = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16
4666 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32;
4667 const quint32 indexSize = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4;
4669 for (quint32 i = 0; i < count; ++i) {
4671 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: primitiveType
4672 indexCount: c.indexCount
4673 indexType: indexType
4674 indexBuffer: indexBufMtl
4675 indexBufferOffset: cbD->currentIndexOffset + c.firstIndex * indexSize
4676 instanceCount: c.instanceCount
4677 baseVertex: c.vertexOffset
4678 baseInstance: c.firstInstance];
4681 const QRhiIndirectDrawCommand *cmds = icbD->drawCommands();
4682 for (quint32 i = 0; i < count; ++i) {
4683 const QRhiIndirectDrawCommand &c(cmds[firstCommand + i]);
4684 [cbD->d->currentRenderPassEncoder drawPrimitives: primitiveType
4685 vertexStart: c.firstVertex
4686 vertexCount: c.vertexCount
4687 instanceCount: c.instanceCount
4688 baseInstance: c.firstInstance];
4694 quint32 firstCommand, quint32 commandCount)
4700 icbD->lastActiveFrameSlot = currentFrameSlot;
4702 const bool indexed = icb->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4706 qWarning(
"executeIndirect: the indirect command buffer was built for a different "
4707 "topology than the current graphics pipeline uses; skipping");
4712 const quint32 total = icbD->commandCount();
4713 const bool deviceCount = icbD
->d->buildInfo.countBuffer !=
nullptr;
4714 NSRange range = NSMakeRange(0, total);
4720 if (firstCommand != 0 || commandCount < total) {
4721 qWarning(
"executeIndirect: firstCommand and commandCount cannot be honoured "
4722 "together with a device-side count; executing all %u command(s)", total);
4725 if (firstCommand >= total)
4727 const quint32 count = qMin(commandCount, total - firstCommand);
4730 range = NSMakeRange(firstCommand, count);
4733 if (indexed && icbD
->d->buildInfo.indexBuffer) {
4735 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4736 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
4737 usage:MTLResourceUsageRead
4738 stages:MTLRenderStageVertex | MTLRenderStageFragment];
4741 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb
4742 indirectBuffer:slot.rangeBuffer
4743 indirectBufferOffset:0];
4745 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb withRange:range];
4751 const QRhiIndirectCommandBufferBuildInfo &info(icbD
->d->buildInfo);
4752 if (info.countBuffer) {
4753 qWarning(
"executeIndirect: a device-side count needs an indirect command buffer, "
4754 "which is not available here; skipping");
4757 const quint32 canonicalStride = indexed ?
sizeof(QRhiIndexedIndirectDrawCommand)
4758 :
sizeof(QRhiIndirectDrawCommand);
4759 const quint32 stride = info.stride ? info.stride : canonicalStride;
4760 const quint32 total = icbD->commandCount();
4761 if (firstCommand >= total)
4763 const quint32 count = qMin(commandCount, total - firstCommand);
4766 const quint32 offset = info.sourceBufferOffset + firstCommand * stride;
4768 drawIndexedIndirect(cb, info.sourceBuffer, offset, count, stride);
4770 drawIndirect(cb, info.sourceBuffer, offset, count, stride);
4774 const quint32 total = icbD->recordedCommandCount();
4775 if (firstCommand >= total)
4777 const quint32 count = qMin(commandCount, total - firstCommand);
4784 qrhimtl_replayIcbOnCpu(cbD, icbD, firstCommand, count, currentFrameSlot);
4789 id<MTLBuffer> indexBufMtl = nil;
4790 quint32 indexOffset = 0;
4795 indexBufD->lastActiveFrameSlot = currentFrameSlot;
4796 indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4797 indexOffset = cbD->currentIndexOffset;
4801 if (slot.usedInFrameId == d->globalFrameId
4802 && !qrhimtl_icbSlotMatches(icbD, slot, primitiveType, indexBufMtl, indexOffset,
4803 cbD->currentIndexFormat))
4808 qWarning(
"executeIndirect: the same indirect command buffer is executed more than once "
4809 "in a frame, with different contents, topology or index buffer state; "
4810 "falling back to individual draw calls");
4811 qrhimtl_replayIcbOnCpu(cbD, icbD, firstCommand, count, currentFrameSlot);
4815 qrhimtl_encodeIcbFromCpu(icbD, slot, primitiveType,
4816 indexBufMtl, indexOffset, cbD->currentIndexFormat);
4821 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
4822 usage:MTLResourceUsageRead
4823 stages:MTLRenderStageVertex | MTLRenderStageFragment];
4825 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb
4826 withRange:NSMakeRange(firstCommand, count)];
4827 slot.usedInFrameId =
d->globalFrameId;
4831 const QRhiIndirectCommandBufferBuildInfo &info)
4837 icbD->lastActiveFrameSlot = currentFrameSlot;
4839 const bool indexed = icb->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4841 if (indexed && !info.indexBuffer) {
4842 qWarning(
"buildIndirect: an IndexedDraws indirect command buffer needs an "
4843 "index buffer in QRhiIndirectCommandBufferBuildInfo; skipping");
4847 quint32 count = info.commandCount ? info.commandCount : icbD->m_maxCommandCount;
4848 if (count > icbD->m_maxCommandCount) {
4849 qWarning(
"QRhiIndirectCommandBuffer: buildIndirect() with commandCount %u exceeds "
4850 "maxCommandCount %u; clamping", count, icbD->m_maxCommandCount);
4851 count = icbD->m_maxCommandCount;
4853 icbD->m_gpuBuilt =
true;
4854 icbD->m_gpuBuiltCommandCount = count;
4859 icbD
->d->buildInfo = info;
4868 srcBufD->lastActiveFrameSlot = currentFrameSlot;
4869 id<MTLBuffer> srcBufMtl = srcBufD->d->buf[srcBufD->d->slotted ? currentFrameSlot : 0];
4871 id<MTLBuffer> indexBufMtl = nil;
4874 indexBufD->lastActiveFrameSlot = currentFrameSlot;
4875 indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4878 id<MTLBuffer> countBufMtl = nil;
4879 if (info.countBuffer) {
4882 countBufD->lastActiveFrameSlot = currentFrameSlot;
4883 countBufMtl = countBufD->d->buf[countBufD->d->slotted ? currentFrameSlot : 0];
4886 const quint32 stride = info.stride ? info.stride
4887 : (indexed ?
sizeof(QRhiIndexedIndirectDrawCommand)
4888 :
sizeof(QRhiIndirectDrawCommand));
4892 id<MTLComputeCommandEncoder> computeEncoder = [cbD->d->cb computeCommandEncoder];
4893 encodeIcbWithCompute(
d, computeEncoder, slot.icb, slot.argBuffer, slot.rangeBuffer,
4894 indexed, info.indexFormat,
4895 toMetalPrimitiveType(info.topology),
4896 srcBufMtl, info.sourceBufferOffset,
4897 indexBufMtl, info.indexBufferOffset,
4898 countBufMtl, info.countBufferOffset,
4899 icbD->commandCount(), stride);
4900 [computeEncoder endEncoding];
4904 icbD
->d->buildInfo = info;
4905 icbD->d->builtSlot = currentFrameSlot;
4912 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4913 [e.buffer.buffers[i] release];
4918 [e.renderbuffer.texture release];
4923 [e.texture.texture release];
4924 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4925 [e.texture.stagingBuffers[i] release];
4926 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
4927 [e.texture.views[i] release];
4928 [e.texture.samplingView release];
4929 [e.texture.writeView release];
4934 [e.sampler.samplerState release];
4939 qsizetype keepBegin =
d->releaseQueue.size();
4940 for (qsizetype i = keepBegin - 1; i >= 0; --i) {
4942 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
4956 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
4957 [e.stagingBuffer.buffer release];
4959 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
4960 [e.graphicsPipeline.pipelineState release];
4961 [e.graphicsPipeline.depthStencilState release];
4962 [e.graphicsPipeline.tessVertexComputeState[0] release];
4963 [e.graphicsPipeline.tessVertexComputeState[1] release];
4964 [e.graphicsPipeline.tessVertexComputeState[2] release];
4965 [e.graphicsPipeline.tessTessControlComputeState release];
4967 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
4968 [e.computePipeline.pipelineState release];
4970 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
4971 [e.shadingRateMap.rateMap release];
4973 case QRhiMetalData::DeferredReleaseEntry::StagingIcbBuffer:
4974 [e.stagingIcbBuffer.icb release];
4975 [e.stagingIcbBuffer.argBuffer release];
4980 }
else if (--keepBegin != i) {
4981 d->releaseQueue[keepBegin] =
std::move(
d->releaseQueue[i]);
4985 d->releaseQueue.remove(0, keepBegin);
4990 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
4992 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
4994 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
4995 readback.result->format = readback.format;
4996 readback.result->pixelSize = readback.pixelSize;
4997 readback.result->data.resize(
int(readback.bufSize));
4998 const void *p = [readback.buf contents];
4999 if (readback.depthPlaneByteSize) {
5007 const quint32 pixelCount = quint32(readback.pixelSize.width()) * quint32(readback.pixelSize.height());
5008 const char *depthPlane =
static_cast<
const char *>(p);
5009 const quint8 *stencilPlane =
reinterpret_cast<
const quint8 *>(depthPlane + readback.depthPlaneByteSize);
5010 char *dst = readback.result->data.data();
5011 for (quint32 px = 0; px < pixelCount; ++px) {
5012 const quint32 depthWord = qFromUnaligned<quint32>(depthPlane + px * 4);
5013 const float depthF = readback
.depthPlaneIsFloat ? qFromUnaligned<
float>(depthPlane + px * 4) : 0.0f;
5014 const quint32 stencil = readback.stencilPlaneByteSize ? stencilPlane[px] : 0;
5015 switch (readback.format) {
5016 case QRhiTexture::D16:
5017 qToUnaligned(quint16(qRound(qBound(0.0f, depthF, 1.0f) * 65535.0f)), dst + px * 2);
5019 case QRhiTexture::D32FS8:
5020 qToUnaligned(depthWord, dst + px * 8);
5021 qToUnaligned(stencil, dst + px * 8 + 4);
5026 ? quint32(qBound(0.0f, depthF, 1.0f) * 16777215.0f + 0.5f)
5027 : depthWord & 0x00FFFFFFu;
5028 qToUnaligned((depth24 << 8) | stencil, dst + px * 4);
5034 memcpy(readback.result->data.data(), p, readback.bufSize);
5036 [readback.buf release];
5038 if (readback.result->completed)
5039 completedCallbacks.append(readback.result->completed);
5041 d->activeTextureReadbacks.remove(i);
5045 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
5047 if (forced || currentFrameSlot == readback.activeFrameSlot
5048 || readback.activeFrameSlot < 0) {
5049 readback.result->data.resize(readback.readSize);
5050 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
5052 memcpy(readback.result->data.data(), p, size_t(readback.readSize));
5053 [readback.buf release];
5055 if (readback.result->completed)
5056 completedCallbacks.append(readback.result->completed);
5058 d->activeBufferReadbacks.remove(i);
5062 for (
auto f : completedCallbacks)
5070 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
5090 e.buffer.buffers[i] =
d->buf[i];
5092 d->pendingUpdates[i].clear();
5097 rhiD
->d->releaseQueue.append(e);
5098 rhiD->unregisterResource(
this);
5107 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
5108 qWarning(
"StorageBuffer cannot be combined with Dynamic");
5112 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
5113 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
5117 MTLResourceOptions opts = MTLResourceStorageModeShared;
5121 const bool internalHostWritable = (
int(m_usage) & (WorkBufPoolUsage | InternalHostWritable)) != 0;
5123 if (m_type != Dynamic && !internalHostWritable && rhiD->caps.usePrivateStaticBuffers) {
5124 opts = MTLResourceStorageModePrivate;
5129 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
5130 opts = MTLResourceStorageModeManaged;
5137 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer)
5138 && !internalHostWritable;
5143 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
5144 if (!m_objectName.isEmpty()) {
5146 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
5148 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
5149 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
5157 rhiD->registerResource(
this);
5169 b.objects[i] = &
d->buf[i];
5178 return { { &
d->buf[0] }, 1 };
5188 Q_ASSERT(m_type == Dynamic);
5191 Q_ASSERT(rhiD->inFrame);
5192 const int slot =
d->slotted ? rhiD->currentFrameSlot : 0;
5193 void *p = [d->buf[slot] contents];
5194 return static_cast<
char *>(p);
5209 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
5211 case QRhiTexture::RGBA8:
5212 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
5213 case QRhiTexture::BGRA8:
5214 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
5215 case QRhiTexture::R8:
5217 return MTLPixelFormatR8Unorm;
5219 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
5221 case QRhiTexture::R8SI:
5222 return MTLPixelFormatR8Sint;
5223 case QRhiTexture::R8UI:
5224 return MTLPixelFormatR8Uint;
5225 case QRhiTexture::RG8:
5227 return MTLPixelFormatRG8Unorm;
5229 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
5231 case QRhiTexture::R16:
5232 return MTLPixelFormatR16Unorm;
5233 case QRhiTexture::RG16:
5234 return MTLPixelFormatRG16Unorm;
5235 case QRhiTexture::RED_OR_ALPHA8:
5236 return MTLPixelFormatR8Unorm;
5238 case QRhiTexture::RGBA16F:
5239 return MTLPixelFormatRGBA16Float;
5240 case QRhiTexture::RGBA32F:
5241 return MTLPixelFormatRGBA32Float;
5242 case QRhiTexture::R16F:
5243 return MTLPixelFormatR16Float;
5244 case QRhiTexture::R32F:
5245 return MTLPixelFormatR32Float;
5247 case QRhiTexture::RGB10A2:
5248 return MTLPixelFormatRGB10A2Unorm;
5250 case QRhiTexture::R32SI:
5251 return MTLPixelFormatR32Sint;
5252 case QRhiTexture::R32UI:
5253 return MTLPixelFormatR32Uint;
5254 case QRhiTexture::RG32SI:
5255 return MTLPixelFormatRG32Sint;
5256 case QRhiTexture::RG32UI:
5257 return MTLPixelFormatRG32Uint;
5258 case QRhiTexture::RGBA32SI:
5259 return MTLPixelFormatRGBA32Sint;
5260 case QRhiTexture::RGBA32UI:
5261 return MTLPixelFormatRGBA32Uint;
5264 case QRhiTexture::D16:
5265 return MTLPixelFormatDepth16Unorm;
5266 case QRhiTexture::D24:
5267 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
5268 case QRhiTexture::D24S8:
5269 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
5271 case QRhiTexture::D16:
5272 return MTLPixelFormatDepth32Float;
5273 case QRhiTexture::D24:
5274 return MTLPixelFormatDepth32Float;
5275 case QRhiTexture::D24S8:
5276 return MTLPixelFormatDepth32Float_Stencil8;
5278 case QRhiTexture::D32F:
5279 return MTLPixelFormatDepth32Float;
5280 case QRhiTexture::D32FS8:
5281 return MTLPixelFormatDepth32Float_Stencil8;
5284 case QRhiTexture::BC1:
5285 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
5286 case QRhiTexture::BC2:
5287 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
5288 case QRhiTexture::BC3:
5289 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
5290 case QRhiTexture::BC4:
5291 return MTLPixelFormatBC4_RUnorm;
5292 case QRhiTexture::BC5:
5293 qWarning(
"QRhiMetal does not support BC5");
5294 return MTLPixelFormatInvalid;
5295 case QRhiTexture::BC6H:
5296 return MTLPixelFormatBC6H_RGBUfloat;
5297 case QRhiTexture::BC7:
5298 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
5300 case QRhiTexture::BC1:
5301 case QRhiTexture::BC2:
5302 case QRhiTexture::BC3:
5303 case QRhiTexture::BC4:
5304 case QRhiTexture::BC5:
5305 case QRhiTexture::BC6H:
5306 case QRhiTexture::BC7:
5307 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
5308 return MTLPixelFormatInvalid;
5312 case QRhiTexture::ETC2_RGB8:
5313 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
5314 case QRhiTexture::ETC2_RGB8A1:
5315 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
5316 case QRhiTexture::ETC2_RGBA8:
5317 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
5319 case QRhiTexture::ASTC_4x4:
5320 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
5321 case QRhiTexture::ASTC_5x4:
5322 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
5323 case QRhiTexture::ASTC_5x5:
5324 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
5325 case QRhiTexture::ASTC_6x5:
5326 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
5327 case QRhiTexture::ASTC_6x6:
5328 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
5329 case QRhiTexture::ASTC_8x5:
5330 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
5331 case QRhiTexture::ASTC_8x6:
5332 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
5333 case QRhiTexture::ASTC_8x8:
5334 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
5335 case QRhiTexture::ASTC_10x5:
5336 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
5337 case QRhiTexture::ASTC_10x6:
5338 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
5339 case QRhiTexture::ASTC_10x8:
5340 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
5341 case QRhiTexture::ASTC_10x10:
5342 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
5343 case QRhiTexture::ASTC_12x10:
5344 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
5345 case QRhiTexture::ASTC_12x12:
5346 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
5348 case QRhiTexture::ETC2_RGB8:
5349 if (d->caps.isAppleGPU)
5350 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
5351 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5352 return MTLPixelFormatInvalid;
5353 case QRhiTexture::ETC2_RGB8A1:
5354 if (d->caps.isAppleGPU)
5355 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
5356 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5357 return MTLPixelFormatInvalid;
5358 case QRhiTexture::ETC2_RGBA8:
5359 if (d->caps.isAppleGPU)
5360 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
5361 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5362 return MTLPixelFormatInvalid;
5363 case QRhiTexture::ASTC_4x4:
5364 if (d->caps.isAppleGPU)
5365 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
5366 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5367 return MTLPixelFormatInvalid;
5368 case QRhiTexture::ASTC_5x4:
5369 if (d->caps.isAppleGPU)
5370 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
5371 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5372 return MTLPixelFormatInvalid;
5373 case QRhiTexture::ASTC_5x5:
5374 if (d->caps.isAppleGPU)
5375 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
5376 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5377 return MTLPixelFormatInvalid;
5378 case QRhiTexture::ASTC_6x5:
5379 if (d->caps.isAppleGPU)
5380 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
5381 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5382 return MTLPixelFormatInvalid;
5383 case QRhiTexture::ASTC_6x6:
5384 if (d->caps.isAppleGPU)
5385 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
5386 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5387 return MTLPixelFormatInvalid;
5388 case QRhiTexture::ASTC_8x5:
5389 if (d->caps.isAppleGPU)
5390 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
5391 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5392 return MTLPixelFormatInvalid;
5393 case QRhiTexture::ASTC_8x6:
5394 if (d->caps.isAppleGPU)
5395 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
5396 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5397 return MTLPixelFormatInvalid;
5398 case QRhiTexture::ASTC_8x8:
5399 if (d->caps.isAppleGPU)
5400 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
5401 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5402 return MTLPixelFormatInvalid;
5403 case QRhiTexture::ASTC_10x5:
5404 if (d->caps.isAppleGPU)
5405 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
5406 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5407 return MTLPixelFormatInvalid;
5408 case QRhiTexture::ASTC_10x6:
5409 if (d->caps.isAppleGPU)
5410 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
5411 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5412 return MTLPixelFormatInvalid;
5413 case QRhiTexture::ASTC_10x8:
5414 if (d->caps.isAppleGPU)
5415 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
5416 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5417 return MTLPixelFormatInvalid;
5418 case QRhiTexture::ASTC_10x10:
5419 if (d->caps.isAppleGPU)
5420 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
5421 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5422 return MTLPixelFormatInvalid;
5423 case QRhiTexture::ASTC_12x10:
5424 if (d->caps.isAppleGPU)
5425 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
5426 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5427 return MTLPixelFormatInvalid;
5428 case QRhiTexture::ASTC_12x12:
5429 if (d->caps.isAppleGPU)
5430 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
5431 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5432 return MTLPixelFormatInvalid;
5437 return MTLPixelFormatInvalid;
5442 int sampleCount, QRhiRenderBuffer::Flags flags,
5443 QRhiTexture::Format backingFormatHint)
5464 e.renderbuffer.texture =
d->tex;
5469 rhiD
->d->releaseQueue.append(e);
5470 rhiD->unregisterResource(
this);
5479 if (m_pixelSize.isEmpty())
5483 samples = rhiD->effectiveSampleCount(m_sampleCount);
5485 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
5486 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
5487 desc.width = NSUInteger(m_pixelSize.width());
5488 desc.height = NSUInteger(m_pixelSize.height());
5490 desc.sampleCount = NSUInteger(
samples);
5491 desc.resourceOptions = MTLResourceStorageModePrivate;
5492 desc.usage = MTLTextureUsageRenderTarget;
5497 const bool canBeMemoryless = !m_flags.testFlag(QRhiRenderBuffer::NoTransientBacking);
5502 if (rhiD->caps.isAppleGPU && canBeMemoryless) {
5503 desc.storageMode = MTLStorageModeMemoryless;
5504 d->format = MTLPixelFormatDepth32Float_Stencil8;
5506 desc.storageMode = MTLStorageModePrivate;
5507 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
5508 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
5511 desc.storageMode = canBeMemoryless ? MTLStorageModeMemoryless : MTLStorageModePrivate;
5512 d->format = MTLPixelFormatDepth32Float_Stencil8;
5514 desc.pixelFormat =
d->format;
5517 desc.storageMode = MTLStorageModePrivate;
5518 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
5519 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
5521 d->format = MTLPixelFormatRGBA8Unorm;
5522 desc.pixelFormat =
d->format;
5529 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
5532 if (!m_objectName.isEmpty())
5533 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
5537 rhiD->registerResource(
this);
5543 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
5544 return m_backingFormatHint;
5546 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
5550 int arraySize,
int sampleCount, Flags flags)
5554 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
5555 d->stagingBuf[i] = nil;
5557 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
5558 d->perLevelViews[i] = nil;
5576 e.texture.texture = d->owns ? d->tex : nil;
5580 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
5581 d->stagingBuf[i] = nil;
5584 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
5585 e.texture.views[i] =
d->perLevelViews[i];
5586 d->perLevelViews[i] = nil;
5589 e.texture.samplingView =
d->samplingView;
5590 d->samplingView = nil;
5591 e.texture.writeView =
d->writeView;
5596 rhiD
->d->releaseQueue.append(e);
5597 rhiD->unregisterResource(
this);
5606 const bool isCube = m_flags.testFlag(CubeMap);
5607 const bool is3D = m_flags.testFlag(ThreeDimensional);
5608 const bool isArray = m_flags.testFlag(TextureArray);
5609 const bool hasMipMaps = m_flags.testFlag(MipMapped);
5610 const bool is1D = m_flags.testFlag(OneDimensional);
5612 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
5613 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
5616 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
5617 if (m_writeViewFormat.format != UnknownFormat) {
5618 d->viewFormat = toMetalTextureFormat(m_writeViewFormat.format,
5619 m_writeViewFormat.srgb ? sRGB : Flags(), rhiD);
5621 d->viewFormat =
d->format;
5623 if (m_readViewFormat.format != UnknownFormat) {
5624 d->viewFormatForSampling = toMetalTextureFormat(m_readViewFormat.format,
5625 m_readViewFormat.srgb ? sRGB : Flags(), rhiD);
5627 d->viewFormatForSampling =
d->format;
5629 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
5630 samples = rhiD->effectiveSampleCount(m_sampleCount);
5633 qWarning(
"Cubemap texture cannot be multisample");
5637 qWarning(
"3D texture cannot be multisample");
5641 qWarning(
"Multisample texture cannot have mipmaps");
5645 if (isCube && is3D) {
5646 qWarning(
"Texture cannot be both cube and 3D");
5649 if (isArray && is3D) {
5650 qWarning(
"Texture cannot be both array and 3D");
5654 qWarning(
"Texture cannot be both 1D and 3D");
5657 if (is1D && isCube) {
5658 qWarning(
"Texture cannot be both 1D and cube");
5661 if (m_depth > 1 && !is3D) {
5662 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
5665 if (m_arraySize > 0 && !isArray) {
5666 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
5669 if (m_arraySize < 1 && isArray) {
5670 qWarning(
"Texture is an array but array size is %d", m_arraySize);
5674 if (!rhiD->textureFormatInfo(m_format, size,
nullptr,
nullptr,
nullptr))
5678 *adjustedSize = size;
5686 if (!prepareCreate(&size))
5689 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
5691 const bool isCube = m_flags.testFlag(CubeMap);
5692 const bool is3D = m_flags.testFlag(ThreeDimensional);
5693 const bool isArray = m_flags.testFlag(TextureArray);
5694 const bool is1D = m_flags.testFlag(OneDimensional);
5696 desc.textureType = MTLTextureTypeCube;
5698 desc.textureType = MTLTextureType3D;
5700 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
5701 }
else if (isArray) {
5702 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
5704 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
5706 desc.pixelFormat =
d->format;
5707 desc.width = NSUInteger(size.width());
5708 desc.height = NSUInteger(size.height());
5709 desc.depth = is3D ? qMax(1, m_depth) : 1;
5712 desc.sampleCount = NSUInteger(
samples);
5714 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
5715 desc.resourceOptions = MTLResourceStorageModePrivate;
5716 desc.storageMode = MTLStorageModePrivate;
5717 desc.usage = MTLTextureUsageShaderRead;
5718 if (m_flags.testFlag(RenderTarget))
5719 desc.usage |= MTLTextureUsageRenderTarget;
5720 if (m_flags.testFlag(UsedWithLoadStore))
5721 desc.usage |= MTLTextureUsageShaderWrite;
5727 const bool writeViewChangesFormat = m_writeViewFormat.format != UnknownFormat
5728 && m_writeViewFormat.format != m_format;
5729 const bool readViewChangesFormat = m_readViewFormat.format != UnknownFormat
5730 && m_readViewFormat.format != m_format;
5731 if (writeViewChangesFormat || readViewChangesFormat)
5732 desc.usage |= MTLTextureUsagePixelFormatView;
5735 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
5738 if (!m_objectName.isEmpty())
5739 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
5748 rhiD->registerResource(
this);
5754 id<MTLTexture> tex = id<MTLTexture>(src.object);
5758 if (!prepareCreate())
5771 rhiD->registerResource(
this);
5777 return {quint64(
d->tex), 0};
5782 Q_ASSERT(!samplingView && !writeView);
5784 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
5785 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
5786 const bool hasArrayRange = isArray &&
q->m_arrayRangeStart >= 0 &&
q->m_arrayRangeLength >= 0;
5787 const NSUInteger sliceCount = isCube ? 6 : (isArray ? NSUInteger(qMax(0,
q->m_arraySize)) : 1);
5789 const MTLTextureType type = [tex textureType];
5791 id<MTLTexture> newSamplingView = nil;
5792 id<MTLTexture> newWriteView = nil;
5794 if (viewFormatForSampling != format || hasArrayRange) {
5795 const NSRange slices = hasArrayRange
5796 ? NSMakeRange(NSUInteger(
q->m_arrayRangeStart), NSUInteger(
q->m_arrayRangeLength))
5797 : NSMakeRange(0, sliceCount);
5798 newSamplingView = [tex newTextureViewWithPixelFormat: viewFormatForSampling
5799 textureType: type levels: levels slices: slices];
5800 if (!newSamplingView) {
5801 qWarning(
"QRhiMetal: Failed to create texture view used for sampling");
5806 if (viewFormat != format) {
5807 newWriteView = [tex newTextureViewWithPixelFormat: viewFormat
5808 textureType: type levels: levels
5809 slices: NSMakeRange(0, sliceCount)];
5810 if (!newWriteView) {
5811 qWarning(
"QRhiMetal: Failed to create texture view used for rendering");
5812 [newSamplingView release];
5817 samplingView = newSamplingView;
5818 writeView = newWriteView;
5825 if (perLevelViews[level])
5826 return perLevelViews[level];
5828 const MTLTextureType type = [tex textureType];
5829 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
5830 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
5831 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: viewFormat textureType: type
5832 levels: NSMakeRange(NSUInteger(level), 1)
5833 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
5835 perLevelViews[level] = view;
5840 AddressMode u, AddressMode v, AddressMode w)
5854 if (!
d->samplerState)
5861 e.sampler.samplerState =
d->samplerState;
5862 d->samplerState = nil;
5866 rhiD
->d->releaseQueue.append(e);
5867 rhiD->unregisterResource(
this);
5874 case QRhiSampler::Nearest:
5875 return MTLSamplerMinMagFilterNearest;
5876 case QRhiSampler::Linear:
5877 return MTLSamplerMinMagFilterLinear;
5880 return MTLSamplerMinMagFilterNearest;
5887 case QRhiSampler::None:
5888 return MTLSamplerMipFilterNotMipmapped;
5889 case QRhiSampler::Nearest:
5890 return MTLSamplerMipFilterNearest;
5891 case QRhiSampler::Linear:
5892 return MTLSamplerMipFilterLinear;
5895 return MTLSamplerMipFilterNotMipmapped;
5902 case QRhiSampler::Repeat:
5903 return MTLSamplerAddressModeRepeat;
5904 case QRhiSampler::ClampToEdge:
5905 return MTLSamplerAddressModeClampToEdge;
5906 case QRhiSampler::Mirror:
5907 return MTLSamplerAddressModeMirrorRepeat;
5910 return MTLSamplerAddressModeClampToEdge;
5917 case QRhiSampler::Never:
5918 return MTLCompareFunctionNever;
5919 case QRhiSampler::Less:
5920 return MTLCompareFunctionLess;
5921 case QRhiSampler::Equal:
5922 return MTLCompareFunctionEqual;
5923 case QRhiSampler::LessOrEqual:
5924 return MTLCompareFunctionLessEqual;
5925 case QRhiSampler::Greater:
5926 return MTLCompareFunctionGreater;
5927 case QRhiSampler::NotEqual:
5928 return MTLCompareFunctionNotEqual;
5929 case QRhiSampler::GreaterOrEqual:
5930 return MTLCompareFunctionGreaterEqual;
5931 case QRhiSampler::Always:
5932 return MTLCompareFunctionAlways;
5935 return MTLCompareFunctionNever;
5941 if (
d->samplerState)
5944 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
5945 desc.minFilter = toMetalFilter(m_minFilter);
5946 desc.magFilter = toMetalFilter(m_magFilter);
5947 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
5948 desc.sAddressMode = toMetalAddressMode(m_addressU);
5949 desc.tAddressMode = toMetalAddressMode(m_addressV);
5950 desc.rAddressMode = toMetalAddressMode(m_addressW);
5951 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
5957 desc.supportArgumentBuffers = rhiD->caps.indirectCommandBuffers ? YES : NO;
5958 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
5960 if (!
d->samplerState) {
5964 qWarning(
"Failed to create Metal sampler state. The number of unique sampler "
5965 "states with argument buffer support may have exceeded the limit of %u.",
5966 uint(rhiD
->d->dev.maxArgumentBufferSamplerCount));
5972 rhiD->registerResource(
this);
5997 e.shadingRateMap.rateMap =
d->rateMap;
6002 rhiD
->d->releaseQueue.append(e);
6003 rhiD->unregisterResource(
this);
6012 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
6016 [d->rateMap retain];
6021 rhiD->registerResource(
this);
6030 const MTLSize screenSize = [d->rateMap screenSize];
6031 return QSize(
int(screenSize.width),
int(screenSize.height));
6039 serializedFormatData.reserve(16);
6051 rhiD->unregisterResource(
this);
6085 serializedFormatData.clear();
6086 auto p =
std::back_inserter(serializedFormatData);
6108 rhiD->registerResource(rpD,
false);
6114 return serializedFormatData;
6136 return d->pixelSize;
6150 const QRhiTextureRenderTargetDescription &desc,
6167 rhiD->unregisterResource(
this);
6172 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
6175 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
6177 for (
int i = 0; i < colorAttachmentCount; ++i) {
6178 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
6184 if (m_desc.depthTexture())
6185 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->viewFormat);
6186 else if (m_desc.depthStencilBuffer())
6187 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
6189 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
6194 rhiD->registerResource(rpD,
false);
6201 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
6202 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
6203 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
6207 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
6211 Q_ASSERT(texD || rbD);
6212 id<MTLTexture> dst = nil;
6215 dst = texD
->d->textureForWrite();
6216 if (attIndex == 0) {
6217 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
6220 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
6223 if (attIndex == 0) {
6224 d->pixelSize = rbD->pixelSize();
6231 colorAtt
.slice = is3D ? it->layer() : 0;
6232 colorAtt
.level = it->level();
6234 colorAtt.resolveTex = resTexD ? resTexD->d->textureForWrite() : nil;
6237 d->fb.colorAtt[attIndex] = colorAtt;
6241 if (hasDepthStencil) {
6242 if (m_desc.depthTexture()) {
6244 d->fb.dsTex = depthTexD
->d->textureForWrite();
6245 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
6246 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
6247 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
6249 d->pixelSize = depthTexD->pixelSize();
6254 d->fb.dsTex = depthRbD
->d->tex;
6255 d->fb.hasStencil =
true;
6256 d->fb.depthNeedsStore =
false;
6257 d->fb.preserveDs =
false;
6259 d->pixelSize = depthRbD->pixelSize();
6263 if (m_desc.depthResolveTexture()) {
6265 d->fb.dsResolveTex = depthResolveTexD
->d->textureForWrite();
6272 if (d->colorAttCount > 0)
6273 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
6275 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
6277 rhiD->registerResource(
this,
false);
6283 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
6286 return d->pixelSize;
6311 sortedBindings.clear();
6316 rhiD->unregisterResource(
this);
6321 if (!sortedBindings.isEmpty())
6325 if (!rhiD->sanityCheckShaderResourceBindings(
this))
6328 rhiD->updateLayoutDesc(
this);
6330 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
6331 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
6332 if (!sortedBindings.isEmpty())
6333 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
6337 boundResourceData.resize(sortedBindings.count());
6339 for (BoundResourceData &bd : boundResourceData)
6343 rhiD->registerResource(
this,
false);
6349 sortedBindings.clear();
6350 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
6351 if (!flags.testFlag(BindingsAreSorted))
6352 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
6354 for (BoundResourceData &bd : boundResourceData)
6381 d->tess.compVs[0].destroy();
6382 d->tess.compVs[1].destroy();
6383 d->tess.compVs[2].destroy();
6385 d->tess.compTesc.destroy();
6386 d->tess.vertTese.destroy();
6388 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
6389 d->extraBufMgr.deviceLocalWorkBuffers.clear();
6390 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
6391 d->extraBufMgr.hostVisibleWorkBuffers.clear();
6396 if (!
d->ps && !
d->ds
6397 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
6398 && !
d->tess.tessControlComputeState)
6406 e.graphicsPipeline.pipelineState =
d->ps;
6407 e.graphicsPipeline.depthStencilState =
d->ds;
6408 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
6409 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
6412 d->tess.vertexComputeState = {};
6413 d->tess.tessControlComputeState = nil;
6417 rhiD
->d->releaseQueue.append(e);
6418 rhiD->unregisterResource(
this);
6425 case QRhiVertexInputAttribute::Float4:
6426 return MTLVertexFormatFloat4;
6427 case QRhiVertexInputAttribute::Float3:
6428 return MTLVertexFormatFloat3;
6429 case QRhiVertexInputAttribute::Float2:
6430 return MTLVertexFormatFloat2;
6431 case QRhiVertexInputAttribute::Float:
6432 return MTLVertexFormatFloat;
6433 case QRhiVertexInputAttribute::UNormByte4:
6434 return MTLVertexFormatUChar4Normalized;
6435 case QRhiVertexInputAttribute::UNormByte2:
6436 return MTLVertexFormatUChar2Normalized;
6437 case QRhiVertexInputAttribute::UNormByte:
6438 return MTLVertexFormatUCharNormalized;
6439 case QRhiVertexInputAttribute::UInt4:
6440 return MTLVertexFormatUInt4;
6441 case QRhiVertexInputAttribute::UInt3:
6442 return MTLVertexFormatUInt3;
6443 case QRhiVertexInputAttribute::UInt2:
6444 return MTLVertexFormatUInt2;
6445 case QRhiVertexInputAttribute::UInt:
6446 return MTLVertexFormatUInt;
6447 case QRhiVertexInputAttribute::SInt4:
6448 return MTLVertexFormatInt4;
6449 case QRhiVertexInputAttribute::SInt3:
6450 return MTLVertexFormatInt3;
6451 case QRhiVertexInputAttribute::SInt2:
6452 return MTLVertexFormatInt2;
6453 case QRhiVertexInputAttribute::SInt:
6454 return MTLVertexFormatInt;
6455 case QRhiVertexInputAttribute::Half4:
6456 return MTLVertexFormatHalf4;
6457 case QRhiVertexInputAttribute::Half3:
6458 return MTLVertexFormatHalf3;
6459 case QRhiVertexInputAttribute::Half2:
6460 return MTLVertexFormatHalf2;
6461 case QRhiVertexInputAttribute::Half:
6462 return MTLVertexFormatHalf;
6463 case QRhiVertexInputAttribute::UShort4:
6464 return MTLVertexFormatUShort4;
6465 case QRhiVertexInputAttribute::UShort3:
6466 return MTLVertexFormatUShort3;
6467 case QRhiVertexInputAttribute::UShort2:
6468 return MTLVertexFormatUShort2;
6469 case QRhiVertexInputAttribute::UShort:
6470 return MTLVertexFormatUShort;
6471 case QRhiVertexInputAttribute::SShort4:
6472 return MTLVertexFormatShort4;
6473 case QRhiVertexInputAttribute::SShort3:
6474 return MTLVertexFormatShort3;
6475 case QRhiVertexInputAttribute::SShort2:
6476 return MTLVertexFormatShort2;
6477 case QRhiVertexInputAttribute::SShort:
6478 return MTLVertexFormatShort;
6481 return MTLVertexFormatFloat4;
6488 case QRhiGraphicsPipeline::Zero:
6489 return MTLBlendFactorZero;
6490 case QRhiGraphicsPipeline::One:
6491 return MTLBlendFactorOne;
6492 case QRhiGraphicsPipeline::SrcColor:
6493 return MTLBlendFactorSourceColor;
6494 case QRhiGraphicsPipeline::OneMinusSrcColor:
6495 return MTLBlendFactorOneMinusSourceColor;
6496 case QRhiGraphicsPipeline::DstColor:
6497 return MTLBlendFactorDestinationColor;
6498 case QRhiGraphicsPipeline::OneMinusDstColor:
6499 return MTLBlendFactorOneMinusDestinationColor;
6500 case QRhiGraphicsPipeline::SrcAlpha:
6501 return MTLBlendFactorSourceAlpha;
6502 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
6503 return MTLBlendFactorOneMinusSourceAlpha;
6504 case QRhiGraphicsPipeline::DstAlpha:
6505 return MTLBlendFactorDestinationAlpha;
6506 case QRhiGraphicsPipeline::OneMinusDstAlpha:
6507 return MTLBlendFactorOneMinusDestinationAlpha;
6508 case QRhiGraphicsPipeline::ConstantColor:
6509 return MTLBlendFactorBlendColor;
6510 case QRhiGraphicsPipeline::ConstantAlpha:
6511 return MTLBlendFactorBlendAlpha;
6512 case QRhiGraphicsPipeline::OneMinusConstantColor:
6513 return MTLBlendFactorOneMinusBlendColor;
6514 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
6515 return MTLBlendFactorOneMinusBlendAlpha;
6516 case QRhiGraphicsPipeline::SrcAlphaSaturate:
6517 return MTLBlendFactorSourceAlphaSaturated;
6518 case QRhiGraphicsPipeline::Src1Color:
6519 return MTLBlendFactorSource1Color;
6520 case QRhiGraphicsPipeline::OneMinusSrc1Color:
6521 return MTLBlendFactorOneMinusSource1Color;
6522 case QRhiGraphicsPipeline::Src1Alpha:
6523 return MTLBlendFactorSource1Alpha;
6524 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
6525 return MTLBlendFactorOneMinusSource1Alpha;
6528 return MTLBlendFactorZero;
6535 case QRhiGraphicsPipeline::Add:
6536 return MTLBlendOperationAdd;
6537 case QRhiGraphicsPipeline::Subtract:
6538 return MTLBlendOperationSubtract;
6539 case QRhiGraphicsPipeline::ReverseSubtract:
6540 return MTLBlendOperationReverseSubtract;
6541 case QRhiGraphicsPipeline::Min:
6542 return MTLBlendOperationMin;
6543 case QRhiGraphicsPipeline::Max:
6544 return MTLBlendOperationMax;
6547 return MTLBlendOperationAdd;
6554 if (c.testFlag(QRhiGraphicsPipeline::R))
6555 f |= MTLColorWriteMaskRed;
6556 if (c.testFlag(QRhiGraphicsPipeline::G))
6557 f |= MTLColorWriteMaskGreen;
6558 if (c.testFlag(QRhiGraphicsPipeline::B))
6559 f |= MTLColorWriteMaskBlue;
6560 if (c.testFlag(QRhiGraphicsPipeline::A))
6561 f |= MTLColorWriteMaskAlpha;
6568 case QRhiGraphicsPipeline::Never:
6569 return MTLCompareFunctionNever;
6570 case QRhiGraphicsPipeline::Less:
6571 return MTLCompareFunctionLess;
6572 case QRhiGraphicsPipeline::Equal:
6573 return MTLCompareFunctionEqual;
6574 case QRhiGraphicsPipeline::LessOrEqual:
6575 return MTLCompareFunctionLessEqual;
6576 case QRhiGraphicsPipeline::Greater:
6577 return MTLCompareFunctionGreater;
6578 case QRhiGraphicsPipeline::NotEqual:
6579 return MTLCompareFunctionNotEqual;
6580 case QRhiGraphicsPipeline::GreaterOrEqual:
6581 return MTLCompareFunctionGreaterEqual;
6582 case QRhiGraphicsPipeline::Always:
6583 return MTLCompareFunctionAlways;
6586 return MTLCompareFunctionAlways;
6593 case QRhiGraphicsPipeline::StencilZero:
6594 return MTLStencilOperationZero;
6595 case QRhiGraphicsPipeline::Keep:
6596 return MTLStencilOperationKeep;
6597 case QRhiGraphicsPipeline::Replace:
6598 return MTLStencilOperationReplace;
6599 case QRhiGraphicsPipeline::IncrementAndClamp:
6600 return MTLStencilOperationIncrementClamp;
6601 case QRhiGraphicsPipeline::DecrementAndClamp:
6602 return MTLStencilOperationDecrementClamp;
6603 case QRhiGraphicsPipeline::Invert:
6604 return MTLStencilOperationInvert;
6605 case QRhiGraphicsPipeline::IncrementAndWrap:
6606 return MTLStencilOperationIncrementWrap;
6607 case QRhiGraphicsPipeline::DecrementAndWrap:
6608 return MTLStencilOperationDecrementWrap;
6611 return MTLStencilOperationKeep;
6618 case QRhiGraphicsPipeline::Triangles:
6619 return MTLPrimitiveTypeTriangle;
6620 case QRhiGraphicsPipeline::TriangleStrip:
6621 return MTLPrimitiveTypeTriangleStrip;
6622 case QRhiGraphicsPipeline::Lines:
6623 return MTLPrimitiveTypeLine;
6624 case QRhiGraphicsPipeline::LineStrip:
6625 return MTLPrimitiveTypeLineStrip;
6626 case QRhiGraphicsPipeline::Points:
6627 return MTLPrimitiveTypePoint;
6630 return MTLPrimitiveTypeTriangle;
6637 case QRhiGraphicsPipeline::Triangles:
6638 case QRhiGraphicsPipeline::TriangleStrip:
6639 case QRhiGraphicsPipeline::TriangleFan:
6640 return MTLPrimitiveTopologyClassTriangle;
6641 case QRhiGraphicsPipeline::Lines:
6642 case QRhiGraphicsPipeline::LineStrip:
6643 return MTLPrimitiveTopologyClassLine;
6644 case QRhiGraphicsPipeline::Points:
6645 return MTLPrimitiveTopologyClassPoint;
6648 return MTLPrimitiveTopologyClassTriangle;
6655 case QRhiGraphicsPipeline::None:
6656 return MTLCullModeNone;
6657 case QRhiGraphicsPipeline::Front:
6658 return MTLCullModeFront;
6659 case QRhiGraphicsPipeline::Back:
6660 return MTLCullModeBack;
6663 return MTLCullModeNone;
6670 case QRhiGraphicsPipeline::Fill:
6671 return MTLTriangleFillModeFill;
6672 case QRhiGraphicsPipeline::Line:
6673 return MTLTriangleFillModeLines;
6676 return MTLTriangleFillModeFill;
6683 case QShaderDescription::CwTessellationWindingOrder:
6684 return MTLWindingClockwise;
6685 case QShaderDescription::CcwTessellationWindingOrder:
6686 return MTLWindingCounterClockwise;
6689 return MTLWindingCounterClockwise;
6696 case QShaderDescription::EqualTessellationPartitioning:
6697 return MTLTessellationPartitionModePow2;
6698 case QShaderDescription::FractionalEvenTessellationPartitioning:
6699 return MTLTessellationPartitionModeFractionalEven;
6700 case QShaderDescription::FractionalOddTessellationPartitioning:
6701 return MTLTessellationPartitionModeFractionalOdd;
6704 return MTLTessellationPartitionModePow2;
6710 int v = version.version();
6711 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
6715 int frameSlot, quint32 minBlockSize, quint32 *offset)
6718 const quint32 alignedSize = aligned<quint32>(size, alignment);
6719 if (pool.offset + alignedSize > pool.capacity) {
6729 e.stagingBuffer.buffer = pool.buf;
6730 releaseQueue.append(e);
6732 pool.capacity = qMax(pool.capacity * 2, qMax(alignedSize, minBlockSize));
6733 pool.buf = [dev newBufferWithLength: pool.capacity options: MTLResourceStorageModeShared];
6740 *offset = pool.offset;
6741 pool.offset += alignedSize;
6747 return allocFromStagingArea(&argBufPool[frameSlot], size, alignment, frameSlot, 16384, offset);
6752 id<MTLBuffer> buf = [dev newBufferWithLength: size options: MTLResourceStorageModeShared];
6758 e.stagingBuffer.buffer = buf;
6759 releaseQueue.append(e);
6765 if (size > LARGE_STAGING_ALLOC) {
6767 return newOneShotStagingBuffer(size, frameSlot);
6772 const quint32 alignedSize = aligned<quint32>(size, 4u);
6773 area.bytesNeeded += alignedSize;
6775 if (area.offset + alignedSize > area.capacity) {
6777 return newOneShotStagingBuffer(size, frameSlot);
6780 *offset = area.offset;
6781 area.offset += alignedSize;
6788 const quint32 needed = area.bytesNeeded;
6789 area.bytesNeeded = 0;
6792 bool resize =
false;
6793 quint32 newCapacity = 0;
6794 if (needed > area.capacity) {
6801 newCapacity = qMin(qNextPowerOfTwo(needed), STAGING_AREA_MAX);
6804 }
else if (needed <= area.capacity / 4 && area.capacity > 0) {
6810 newCapacity = needed ? qMax(area.capacity / 2, STAGING_AREA_MIN) : 0;
6818 if (resize && newCapacity != area.capacity) {
6823 e.stagingBuffer.buffer = area.buf;
6824 releaseQueue.append(e);
6829 area.buf = [dev newBufferWithLength: newCapacity options: MTLResourceStorageModeShared];
6831 area.capacity = newCapacity;
6837 bool preferArgumentBuffers,
6838 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
6840 QVarLengthArray<
int, 8> versions;
6841 versions << 30 << 24 << 23 << 22 << 21 << 20 << 12;
6851 QVarLengthArray<QShader::Variant, 2> variants;
6852 if (preferArgumentBuffers)
6853 variants << QShader::ArgumentBufferShader;
6854 variants << shaderVariant;
6856 const QList<QShaderKey> shaders = shader.availableShaders();
6858 auto findKey = [&shaders, &versions, &variants](QShader::Source source, QShaderKey *result) {
6859 for (
const QShader::Variant &variant : variants) {
6860 for (
const int &version : versions) {
6861 const QShaderKey key = { source, version, variant };
6862 if (shaders.contains(key)) {
6873 if (findKey(QShader::Source::MetalLibShader, &key)) {
6874 QShaderCode mtllib = shader.shader(key);
6875 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
6876 size_t(mtllib.shader().size()),
6877 dispatch_get_global_queue(0, 0),
6878 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
6880 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
6881 dispatch_release(data);
6883 *entryPoint = mtllib.entryPoint();
6887 const QString msg = QString::fromNSString(err.localizedDescription);
6888 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
6892 if (!findKey(QShader::Source::MslShader, &key)) {
6893 qWarning() <<
"No MSL code found in baked shader" << shader;
6897 QShaderCode mslSource = shader.shader(key);
6899 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
6900 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
6901 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
6903 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
6911 const QString msg = QString::fromNSString(err.localizedDescription);
6916 *entryPoint = mslSource.entryPoint();
6923 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
6928 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
6932 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
6933 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
6934 rpDesc.colorAttachments[0].blendingEnabled =
false;
6936 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
6937 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
6939 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
6940 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
6941 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
6942 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
6943 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
6944 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
6945 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
6946 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
6947 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
6948 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
6949 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
6956 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
6957 rpDesc.depthAttachmentPixelFormat = fmt;
6958#if defined(Q_OS_MACOS)
6959 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
6961 if (fmt != MTLPixelFormatDepth32Float)
6963 rpDesc.stencilAttachmentPixelFormat = fmt;
6967 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
6972 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
6974 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
6975 dsDesc.depthWriteEnabled = m_depthWrite;
6976 if (m_stencilTest) {
6977 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
6978 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
6979 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
6980 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
6981 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
6982 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
6983 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
6985 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
6986 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
6987 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
6988 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
6989 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
6990 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
6991 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
6997 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
6998 d->cullMode = toMetalCullMode(m_cullMode);
6999 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
7000 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
7001 d->depthBias =
float(m_depthBias);
7002 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
7012 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
7015 const uint loc = uint(it->location());
7016 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
7017 desc.attributes[loc].offset = NSUInteger(it->offset());
7018 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
7020 int bindingIndex = 0;
7021 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
7022 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
7023 it != itEnd; ++it, ++bindingIndex)
7025 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
7026 desc.layouts[layoutIdx].stepFunction =
7027 it->classification() == QRhiVertexInputBinding::PerInstance
7028 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
7029 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
7030 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
7031 desc.layouts[layoutIdx].stepRate *= viewCount;
7032 desc.layouts[layoutIdx].stride = it->stride();
7043 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
7046 const uint loc = uint(it->location());
7047 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
7048 desc.attributes[loc].offset = NSUInteger(it->offset());
7049 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
7051 int bindingIndex = 0;
7052 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
7053 it != itEnd; ++it, ++bindingIndex)
7055 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
7056 if (desc.indexBufferIndex) {
7057 desc.layouts[layoutIdx].stepFunction =
7058 it->classification() == QRhiVertexInputBinding::PerInstance
7059 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
7061 desc.layouts[layoutIdx].stepFunction =
7062 it->classification() == QRhiVertexInputBinding::PerInstance
7063 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
7065 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
7066 desc.layouts[layoutIdx].stride = it->stride();
7073 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
7074 rpDesc.binaryArchives = binArchArray;
7082 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
7083 const QString msg = QString::fromNSString(err.localizedDescription);
7084 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
7091 return !desc.combinedImageSamplers().isEmpty()
7092 || !desc.separateImages().isEmpty()
7093 || !desc.storageImages().isEmpty();
7098 for (
const QShaderKey &k : shader.availableShaders()) {
7099 if (k.sourceVariant() == QShader::ArgumentBufferShader)
7107 const int index = shader->nativeShaderInfo.extraBufferBindings.value(QShaderPrivate::MslArgumentBufferBinding, -1);
7110 shader->argumentEncoder = [shader->func newArgumentEncoderWithBufferIndex: NSUInteger(index)];
7121 const bool wantArgumentBuffers = m_flags.testFlag(UsesIndirectDraws) && rhiD->caps.indirectCommandBuffers;
7123 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
7124 d->setupVertexInputDescriptor(vertexDesc);
7126 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
7127 rpDesc.vertexDescriptor = vertexDesc;
7135 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7136 const QShader shader = shaderStage.shader();
7137 const bool argumentBufferBuild = wantArgumentBuffers && hasArgumentBufferVariant(shader);
7138 auto cacheIt = rhiD->d->shaderCache.constFind({ shaderStage, argumentBufferBuild });
7139 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
7140 switch (shaderStage.type()) {
7141 case QRhiShaderStage::Vertex:
7144 [d->vs.func retain];
7145 [d->vs.argumentEncoder retain];
7146 rpDesc.vertexFunction = d->vs.func;
7148 case QRhiShaderStage::Fragment:
7151 [d->fs.func retain];
7152 [d->fs.argumentEncoder retain];
7153 rpDesc.fragmentFunction = d->fs.func;
7160 QByteArray entryPoint;
7161 QShaderKey activeKey;
7162 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
7163 argumentBufferBuild,
7164 &error, &entryPoint, &activeKey);
7166 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
7169 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
7171 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7175 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
7177 for (QMetalShader &s : rhiD->d->shaderCache)
7179 rhiD->d->shaderCache.clear();
7181 switch (shaderStage.type()) {
7182 case QRhiShaderStage::Vertex:
7185 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
7186 d->vs.desc = shader.description();
7187 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
7188 setupArgumentBufferEncoder(&d->vs);
7189 rhiD->d->shaderCache.insert({ shaderStage, argumentBufferBuild }, d->vs);
7191 [d->vs.func retain];
7192 [d->vs.argumentEncoder retain];
7193 rpDesc.vertexFunction = func;
7195 case QRhiShaderStage::Fragment:
7198 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
7199 d->fs.desc = shader.description();
7200 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
7201 setupArgumentBufferEncoder(&d->fs);
7202 rhiD->d->shaderCache.insert({ shaderStage, argumentBufferBuild }, d->fs);
7204 [d->fs.func retain];
7205 [d->fs.argumentEncoder retain];
7206 rpDesc.fragmentFunction = func;
7222 return !usesTextures(s.desc) || s.argumentBufferIndex >= 0;
7224 d->icbCapable = wantArgumentBuffers && icbSafe(
d->vs) && icbSafe(
d->fs);
7226 rpDesc.supportIndirectCommandBuffers = YES;
7228 if (m_multiViewCount >= 2)
7229 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
7231 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
7233 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7234 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
7237 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
7240 const QString msg = QString::fromNSString(err.localizedDescription);
7241 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
7245 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
7247 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
7250 d->primitiveType = toMetalPrimitiveType(m_topology);
7258 switch (vertexCompVariant) {
7259 case QShader::NonIndexedVertexAsComputeShader:
7261 case QShader::UInt32IndexedVertexAsComputeShader:
7263 case QShader::UInt16IndexedVertexAsComputeShader:
7273 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
7274 if (varIndex >= 0 && vertexComputeState[varIndex])
7275 return vertexComputeState[varIndex];
7277 id<MTLFunction> func = nil;
7279 func = compVs[varIndex].func;
7282 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
7286 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
7287 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
7289 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
7290 cpDesc.computeFunction = func;
7291 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
7292 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
7293 if (indexBufferBinding >= 0) {
7294 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
7295 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
7296 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
7297 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
7298 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
7299 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
7302 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
7304 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
7306 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7307 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
7310 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
7311 options: MTLPipelineOptionNone
7316 const QString msg = QString::fromNSString(err.localizedDescription);
7317 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
7319 vertexComputeState[varIndex] = ps;
7327 if (tessControlComputeState)
7328 return tessControlComputeState;
7330 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
7331 cpDesc.computeFunction = compTesc.func;
7333 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
7335 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7336 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
7339 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
7340 options: MTLPipelineOptionNone
7345 const QString msg = QString::fromNSString(err.localizedDescription);
7346 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
7348 tessControlComputeState = ps;
7356 return (indices >> index) & 0x1;
7359static inline void takeIndex(quint32 index, quint64 &indices)
7361 indices |= 1 << index;
7370 static const int maxVertexAttributes = 31;
7372 for (
int index = 0; index < maxVertexAttributes; ++index) {
7373 if (!indexTaken(index, indices))
7377 Q_UNREACHABLE_RETURN(-1);
7380static inline int aligned(quint32 offset, quint32 alignment)
7382 return ((offset + alignment - 1) / alignment) * alignment;
7390 for (
const int dim : variable.arrayDims)
7393 if (variable.type == QShaderDescription::VariableType::Struct) {
7394 for (
int element = 0; element < elements; ++element) {
7395 for (
const auto &member : variable.structMembers) {
7396 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
7400 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
7401 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
7404 const quint32 alignment = size;
7405 vertexAlignment =
std::max(vertexAlignment, alignment);
7407 for (
int element = 0; element < elements; ++element) {
7409 offset = aligned(offset, alignment);
7416static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
7420 for (
const int dim : variable.arrayDims)
7423 if (variable.type == QShaderDescription::VariableType::Struct) {
7424 for (
int element = 0; element < elements; ++element) {
7425 for (
const auto &member : variable.structMembers) {
7426 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
7430 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
7431 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
7434 const quint32 alignment = size;
7435 vertexAlignment =
std::max(vertexAlignment, alignment);
7437 for (
int element = 0; element < elements; ++element) {
7438 Q_ASSERT(!indexTaken(index, indices));
7441 offset = aligned(offset, alignment);
7443 attributes[index].bufferIndex = binding;
7444 attributes[index].format = toMetalAttributeFormat(format);
7445 attributes[index].offset = offset;
7447 takeIndex(index, indices);
7449 if (indexTaken(index, indices))
7450 index = nextAttributeIndex(indices);
7457static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
7459 if (a.size() == b.size()) {
7461 for (
int i = 0; i < a.size() && match; ++i) {
7462 match &= a[i].type == b[i].type
7463 && a[i].arrayDims == b[i].arrayDims
7464 && matches(a[i].structMembers, b[i].structMembers);
7472static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
7474 return a.location == b.location
7476 && a.perPatch == b.perPatch
7477 && matches(a.structMembers, b.structMembers);
7526 if (pipeline
->d->ps)
7527 return pipeline
->d->ps;
7529 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
7530 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
7533 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
7534 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
7535 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
7536 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
7537 quint32 offsetInTescOutput = 0;
7538 quint32 offsetInTescPatchOutput = 0;
7539 quint32 offsetInTessFactorBuffer = 0;
7540 quint32 tescOutputAlignment = 0;
7541 quint32 tescPatchOutputAlignment = 0;
7542 quint32 tessFactorAlignment = 0;
7543 QSet<
int> usedBuffers;
7546 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
7547 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
7548 tescOutVars[tescOutVar.location] = tescOutVar;
7551 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
7552 for (
const auto &teseInVar : vertTese.desc.inputVariables())
7553 teseInVars[teseInVar.location] = teseInVar;
7556 quint64 indices = 0;
7558 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
7560 int index = tescOutVar.location;
7562 quint32 *offset =
nullptr;
7563 quint32 *alignment =
nullptr;
7565 if (tescOutVar.perPatch) {
7566 binding = tescPatchOutputBufferBinding;
7567 offset = &offsetInTescPatchOutput;
7568 alignment = &tescPatchOutputAlignment;
7570 tescOutVar.arrayDims.removeLast();
7571 binding = tescOutputBufferBinding;
7572 offset = &offsetInTescOutput;
7573 alignment = &tescOutputAlignment;
7576 if (teseInVars.contains(index)) {
7578 if (!matches(teseInVars[index], tescOutVar)) {
7579 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
7580 qWarning() <<
" tesc out:" << tescOutVar;
7581 qWarning() <<
" tese in:" << teseInVars[index];
7584 if (binding != -1) {
7585 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
7586 usedBuffers << binding;
7588 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
7589 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
7593 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
7594 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
7597 teseInVars.remove(tescOutVar.location);
7600 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
7601 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
7604 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
7605 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
7606 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
7609 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
7610 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
7611 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
7613 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
7614 bool tessLevelAdded =
false;
7616 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
7618 QShaderDescription::InOutVariable variable;
7620 quint32 *offset =
nullptr;
7621 quint32 *alignment =
nullptr;
7623 switch (builtin.type) {
7624 case QShaderDescription::BuiltinType::PositionBuiltin:
7625 variable.type = QShaderDescription::VariableType::Vec4;
7626 binding = tescOutputBufferBinding;
7627 offset = &offsetInTescOutput;
7628 alignment = &tescOutputAlignment;
7630 case QShaderDescription::BuiltinType::PointSizeBuiltin:
7631 variable.type = QShaderDescription::VariableType::Float;
7632 binding = tescOutputBufferBinding;
7633 offset = &offsetInTescOutput;
7634 alignment = &tescOutputAlignment;
7636 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
7637 variable.type = QShaderDescription::VariableType::Float;
7638 variable.arrayDims = builtin.arrayDims;
7639 binding = tescOutputBufferBinding;
7640 offset = &offsetInTescOutput;
7641 alignment = &tescOutputAlignment;
7643 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
7644 variable.type = QShaderDescription::VariableType::Half4;
7645 binding = tessFactorBufferBinding;
7646 offset = &offsetInTessFactorBuffer;
7647 tessLevelAdded = trianglesMode;
7648 alignment = &tessFactorAlignment;
7650 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
7651 if (trianglesMode) {
7652 if (!tessLevelAdded) {
7653 variable.type = QShaderDescription::VariableType::Half4;
7654 binding = tessFactorBufferBinding;
7655 offsetInTessFactorBuffer = 0;
7656 offset = &offsetInTessFactorBuffer;
7657 alignment = &tessFactorAlignment;
7658 tessLevelAdded =
true;
7660 teseInBuiltins.remove(builtin.type);
7664 variable.type = QShaderDescription::VariableType::Half2;
7665 binding = tessFactorBufferBinding;
7666 offsetInTessFactorBuffer = 8;
7667 offset = &offsetInTessFactorBuffer;
7668 alignment = &tessFactorAlignment;
7676 if (teseInBuiltins.contains(builtin.type)) {
7677 if (binding != -1) {
7678 int index = nextAttributeIndex(indices);
7679 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
7680 usedBuffers << binding;
7682 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
7683 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
7686 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
7689 teseInBuiltins.remove(builtin.type);
7692 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
7693 switch (builtin.type) {
7694 case QShaderDescription::BuiltinType::PositionBuiltin:
7695 case QShaderDescription::BuiltinType::PointSizeBuiltin:
7696 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
7697 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
7704 if (usedBuffers.contains(tescOutputBufferBinding)) {
7705 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
7706 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
7709 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
7710 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
7711 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
7714 if (usedBuffers.contains(tessFactorBufferBinding)) {
7715 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
7716 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
7719 rpDesc.vertexDescriptor = vertexDesc;
7720 rpDesc.vertexFunction = vertTese.func;
7721 rpDesc.fragmentFunction = pipeline
->d->fs.func;
7727 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
7729 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
7734 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
7736 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7737 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
7740 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
7743 const QString msg = QString::fromNSString(err.localizedDescription);
7744 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
7748 pipeline->d->ps = ps;
7755 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
7758 for (QMetalBuffer *workBuf : *workBuffers) {
7759 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
7760 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
7768 for (QMetalBuffer *workBuf : *workBuffers) {
7769 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
7770 workBuf->setSize(size);
7771 if (workBuf->create()) {
7772 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
7783 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
7786 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
7790 workBuffers->append(buf);
7794 qWarning(
"Failed to acquire work buffer of size %u", size);
7802 QByteArray entryPoint;
7803 QShaderKey activeKey;
7805 const QShaderDescription tescDesc = tesc.description();
7806 const QShaderDescription teseDesc = tese.description();
7807 d->tess.inControlPointCount = uint(m_patchControlPointCount);
7808 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
7809 if (!
d->tess.outControlPointCount)
7810 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
7812 if (!
d->tess.outControlPointCount) {
7813 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
7814 d->tess.enabled =
false;
7815 d->tess.failed =
true;
7819 if (m_multiViewCount >= 2)
7820 qWarning(
"Multiview is not supported with tessellation");
7828 bool variantsPresent[3] = {};
7829 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
7830 for (
const QShaderKey &k : tessVertKeys) {
7831 switch (k.sourceVariant()) {
7832 case QShader::NonIndexedVertexAsComputeShader:
7833 variantsPresent[0] =
true;
7835 case QShader::UInt32IndexedVertexAsComputeShader:
7836 variantsPresent[1] =
true;
7838 case QShader::UInt16IndexedVertexAsComputeShader:
7839 variantsPresent[2] =
true;
7845 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
7846 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
7847 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
7848 "Try passing --msltess to qsb.");
7849 d->tess.enabled =
false;
7850 d->tess.failed =
true;
7855 for (QShader::Variant variant : {
7856 QShader::NonIndexedVertexAsComputeShader,
7857 QShader::UInt32IndexedVertexAsComputeShader,
7858 QShader::UInt16IndexedVertexAsComputeShader })
7860 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant,
false, &error, &entryPoint, &activeKey);
7862 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
7863 d->tess.enabled =
false;
7864 d->tess.failed =
true;
7867 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
7869 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7871 d->tess.enabled =
false;
7872 d->tess.failed =
true;
7875 QMetalShader &compVs(d->tess.compVs[varIndex]);
7878 compVs.desc = tessVert.description();
7879 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
7880 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
7883 if (!d->tess.vsCompPipeline(rhiD, variant)) {
7884 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
7885 d->tess.enabled =
false;
7886 d->tess.failed =
true;
7894 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7895 if (!tessControlLib) {
7896 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
7897 d->tess.enabled =
false;
7898 d->tess.failed =
true;
7901 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
7902 if (!tessControlFunc) {
7903 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7904 [tessControlLib release];
7905 d->tess.enabled =
false;
7906 d->tess.failed =
true;
7909 d->tess.compTesc.lib = tessControlLib;
7910 d->tess.compTesc.func = tessControlFunc;
7911 d->tess.compTesc.desc = tesc.description();
7912 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
7913 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
7914 if (!
d->tess.tescCompPipeline(rhiD)) {
7915 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
7916 d->tess.enabled =
false;
7917 d->tess.failed =
true;
7922 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7924 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
7925 d->tess.enabled =
false;
7926 d->tess.failed =
true;
7929 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
7930 if (!tessEvalFunc) {
7931 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7932 [tessEvalLib release];
7933 d->tess.enabled =
false;
7934 d->tess.failed =
true;
7937 d->tess.vertTese.lib = tessEvalLib;
7938 d->tess.vertTese.func = tessEvalFunc;
7939 d->tess.vertTese.desc = tese.description();
7940 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
7941 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
7943 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7945 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
7946 d->tess.enabled =
false;
7947 d->tess.failed =
true;
7950 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
7952 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7954 d->tess.enabled =
false;
7955 d->tess.failed =
true;
7958 d->fs.lib = fragLib;
7959 d->fs.func = fragFunc;
7960 d->fs.desc = tessFrag.description();
7961 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
7962 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
7964 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
7965 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
7966 d->tess.enabled =
false;
7967 d->tess.failed =
true;
7971 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
7973 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
7987 rhiD->pipelineCreationStart();
7988 if (!rhiD->sanityCheckGraphicsPipeline(
this))
7996 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7997 switch (shaderStage.type()) {
7998 case QRhiShaderStage::Vertex:
7999 tessVert = shaderStage.shader();
8001 case QRhiShaderStage::TessellationControl:
8002 tesc = shaderStage.shader();
8004 case QRhiShaderStage::TessellationEvaluation:
8005 tese = shaderStage.shader();
8007 case QRhiShaderStage::Fragment:
8008 tessFrag = shaderStage.shader();
8014 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
8015 d->tess.failed =
false;
8017 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
8023 QVarLengthArray<QMetalShader *, 6> shaders;
8024 if (
d->tess.enabled) {
8025 shaders.append(&
d->tess.compVs[0]);
8026 shaders.append(&
d->tess.compVs[1]);
8027 shaders.append(&
d->tess.compVs[2]);
8028 shaders.append(&
d->tess.compTesc);
8029 shaders.append(&
d->tess.vertTese);
8031 shaders.append(&
d->vs);
8033 shaders.append(&
d->fs);
8035 for (QMetalShader *shader : shaders) {
8036 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
8037 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
8038 shader->nativeResourceBindingMap[binding] = {binding, -1};
8039 int maxNativeBinding = 0;
8040 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
8041 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
8045 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
8050 if (!d->bufferSizeBuffer)
8051 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static,
8052 QRhiBuffer::UsageFlags(
int(QRhiBuffer::StorageBuffer)
8053 | QMetalBuffer::InternalHostWritable),
8054 buffers *
sizeof(
int));
8060 rhiD->pipelineCreationEnd();
8063 rhiD->registerResource(
this);
8092 e.computePipeline.pipelineState =
d->ps;
8097 rhiD
->d->releaseQueue.append(e);
8098 rhiD->unregisterResource(
this);
8105 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
8106 cpDesc.binaryArchives = binArchArray;
8114 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
8115 const QString msg = QString::fromNSString(err.localizedDescription);
8116 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
8127 rhiD->pipelineCreationStart();
8129 auto cacheIt = rhiD
->d->shaderCache.constFind({ m_shaderStage,
false });
8130 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
8133 const QShader shader = m_shaderStage.shader();
8135 QByteArray entryPoint;
8136 QShaderKey activeKey;
8137 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
false,
8138 &error, &entryPoint, &activeKey);
8140 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
8143 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
8145 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
8151 d->cs.localSize = shader.description().computeShaderLocalSize();
8152 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
8153 d->cs.desc = shader.description();
8154 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
8161 setupArgumentBufferEncoder(&
d->cs);
8164 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
8165 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
8166 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
8169 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
8170 for (QMetalShader &s : rhiD->d->shaderCache)
8172 rhiD
->d->shaderCache.clear();
8174 rhiD
->d->shaderCache.insert({ m_shaderStage,
false },
d->cs);
8178 [d->cs.func retain];
8179 [d->cs.argumentEncoder retain];
8181 if (
d->cs.argumentBufferIndex >= 0 && !rhiD->caps.indirectCommandBuffers) {
8185 qWarning(
"The ArgumentBufferShader variant of a compute shader cannot be used on this device");
8189 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
8191 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
8192 cpDesc.computeFunction =
d->cs.func;
8194 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
8196 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
8197 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
8200 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
8201 options: MTLPipelineOptionNone
8206 const QString msg = QString::fromNSString(err.localizedDescription);
8207 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
8212 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
8214 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
8215 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
8219 if (!d->bufferSizeBuffer)
8220 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static,
8221 QRhiBuffer::UsageFlags(
int(QRhiBuffer::StorageBuffer)
8222 | QMetalBuffer::InternalHostWritable),
8223 buffers *
sizeof(
int));
8229 rhiD->pipelineCreationEnd();
8232 rhiD->registerResource(
this);
8256 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
8257 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
8258 return &nativeHandlesStruct;
8264 d->currentRenderPassEncoder = nil;
8265 d->currentComputePassEncoder = nil;
8266 d->tessellationComputeEncoder = nil;
8267 d->currentPassRpDesc = nil;
8274 currentTarget =
nullptr;
8275 d->openDebugGroups.clear();
8284 pushConstantData.clear();
8291 currentIndexOffset = 0;
8292 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
8297 currentDepthBiasValues = { 0.0f, 0.0f };
8299 currentScissor = {};
8300 currentViewport = {};
8302 currentBlendConstants = {};
8304 currentStencilRef = 0;
8306 d->currentShaderResourceBindingState = {};
8307 d->currentDepthStencilState = nil;
8309 d->currentVertexInputsBuffers.clear();
8310 d->currentVertexInputOffsets.clear();
8320 d->sem[i] =
nullptr;
8321 d->msaaTex[i] = nil;
8341 dispatch_release(
d->sem[i]);
8342 d->sem[i] =
nullptr;
8347 [d->msaaTex[i] release];
8348 d->msaaTex[i] = nil;
8354 [d->curDrawable release];
8355 d->curDrawable = nil;
8359 rhiD->swapchains.remove(
this);
8360 rhiD->unregisterResource(
this);
8380 CALayer *layer =
nullptr;
8382 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
8383 layer = cocoaWindow->contentLayer();
8385 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
8388 return static_cast<CAMetalLayer *>(layer);
8397 d.reserved[0] = layerForWindow(window);
8404 CAMetalLayer *layer =
d->layer;
8406 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
8409 int height = (
int)layer.bounds.size.height;
8410 int width = (
int)layer.bounds.size.width;
8411 width *= layer.contentsScale;
8412 height *= layer.contentsScale;
8413 return QSize(width, height);
8418 if (f == HDRExtendedSrgbLinear) {
8420 }
else if (f == HDR10) {
8422 }
else if (f == HDRExtendedDisplayP3Linear) {
8436 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
8442 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
8443 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
8445 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
8448 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
8452 rhiD->registerResource(rpD,
false);
8459 samples = rhiD->effectiveSampleCount(m_sampleCount);
8461 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
8462 d->colorFormat = MTLPixelFormatRGBA16Float;
8463 d->rhiColorFormat = QRhiTexture::RGBA16F;
8466 if (m_format == HDR10) {
8467 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
8468 d->rhiColorFormat = QRhiTexture::RGB10A2;
8471 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
8472 d->rhiColorFormat = QRhiTexture::BGRA8;
8481 dispatch_semaphore_t sem =
d->sem[slot];
8482 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
8483 dispatch_semaphore_signal(sem);
8490 const bool needsRegistration = !window || window != m_window;
8492 if (window && window != m_window)
8497 if (needsRegistration || !rhiD->swapchains.contains(
this))
8498 rhiD->swapchains.insert(
this);
8502 if (window->surfaceType() != QSurface::MetalSurface) {
8503 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
8507 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
8511 if (
d->colorFormat !=
d->layer.pixelFormat)
8512 d->layer.pixelFormat =
d->colorFormat;
8514 if (m_format == HDRExtendedSrgbLinear) {
8515 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
8516 d->layer.wantsExtendedDynamicRangeContent = YES;
8517 }
else if (m_format == HDR10) {
8518 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
8519 d->layer.wantsExtendedDynamicRangeContent = YES;
8520 }
else if (m_format == HDRExtendedDisplayP3Linear) {
8521 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
8522 d->layer.wantsExtendedDynamicRangeContent = YES;
8525 if (m_flags.testFlag(UsedAsTransferSource))
8526 d->layer.framebufferOnly = NO;
8529 if (m_flags.testFlag(NoVSync))
8530 d->layer.displaySyncEnabled = NO;
8533 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
8534 d->layer.opaque = NO;
8535 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
8540 d->layer.opaque = NO;
8542 d->layer.opaque = YES;
8548 int width = (
int)
d->layer.bounds.size.width;
8549 int height = (
int)
d->layer.bounds.size.height;
8550 CGSize layerSize = CGSizeMake(width, height);
8551 const float scaleFactor =
d->layer.contentsScale;
8552 layerSize.width *= scaleFactor;
8553 layerSize.height *= scaleFactor;
8554 d->layer.drawableSize = layerSize;
8556 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
8557 pixelSize = m_currentPixelSize;
8559 [d->layer setDevice: rhiD->d->dev];
8561 [d->curDrawable release];
8562 d->curDrawable = nil;
8573 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
8574 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
8575 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
8576 m_depthStencil->sampleCount(), m_sampleCount);
8578 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
8579 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
8580 m_depthStencil->setPixelSize(pixelSize);
8581 if (!m_depthStencil->create())
8582 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
8583 pixelSize.width(), pixelSize.height());
8585 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
8586 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
8587 pixelSize.width(), pixelSize.height());
8591 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
8592 rtWrapper.d->pixelSize = pixelSize;
8598 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
8599 pixelSize.width(), pixelSize.height(), scaleFactor);
8602 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
8603 desc.textureType = MTLTextureType2DMultisample;
8604 desc.pixelFormat =
d->colorFormat;
8605 desc.width = NSUInteger(pixelSize.width());
8606 desc.height = NSUInteger(pixelSize.height());
8607 desc.sampleCount = NSUInteger(
samples);
8608 desc.resourceOptions = MTLResourceStorageModePrivate;
8609 desc.storageMode = MTLStorageModePrivate;
8610 desc.usage = MTLTextureUsageRenderTarget;
8612 if (
d->msaaTex[i]) {
8616 e.renderbuffer.texture =
d->msaaTex[i];
8617 rhiD
->d->releaseQueue.append(e);
8619 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
8624 rhiD->registerResource(
this);
8640#if defined(Q_OS_MACOS)
8641 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
8642 NSScreen *screen = view.window.screen;
8643 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
8644 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
8645#elif defined(Q_OS_IOS)
8646 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
8647 UIScreen *screen = view.window.windowScene.screen;
8648 info.limits.colorComponentValue.maxColorComponentValue =
8649 view.window.windowScene.screen.currentEDRHeadroom;
8650 info.limits.colorComponentValue.maxPotentialColorComponentValue =
8651 screen.potentialEDRHeadroom;
const char * constData() const
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
id< MTLComputePipelineState > pipelineState
id< MTLTexture > samplingView
id< MTLDepthStencilState > depthStencilState
std::array< id< MTLComputePipelineState >, 3 > tessVertexComputeState
id< MTLTexture > writeView
id< MTLRasterizationRateMap > rateMap
id< MTLSamplerState > samplerState
id< MTLBuffer > argBuffer
id< MTLBuffer > stagingBuffers[QMTL_FRAMES_IN_FLIGHT]
id< MTLComputePipelineState > tessTessControlComputeState
id< MTLIndirectCommandBuffer > icb
id< MTLRenderPipelineState > pipelineState
id< MTLBuffer > buffers[QMTL_FRAMES_IN_FLIGHT]
id< MTLTexture > views[QRhi::MAX_MIP_LEVELS]
QRhiReadbackDescription desc
quint32 depthPlaneByteSize
QRhiReadbackResult * result
QRhiTexture::Format format
quint32 stencilPlaneByteSize
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
float maxPotentialColorComponentValue
LuminanceBehavior luminanceBehavior
float maxColorComponentValue
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h