8#include <QGuiApplication>
12#include <QTemporaryFile>
15#include <QOperatingSystemVersion>
17#include <QtCore/private/qcore_mac_p.h>
18#include <QtGui/private/qmetallayer_p.h>
19#include <QtGui/qpa/qplatformwindow_p.h>
22#include <AppKit/AppKit.h>
24#include <UIKit/UIKit.h>
27#include <QuartzCore/CATransaction.h>
29#include <Metal/Metal.h>
36
37
38
39
40
41
42
43
44
47#error ARC not supported
56#define QRHI_METAL_DISABLE_BINARY_ARCHIVE
61#define QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
65
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
104
105
106
107
108
109
110
111
114
115
116
117
120
121
122
123
124
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
148
149
152
153
157 id<MTLLibrary> lib = nil;
158 id<MTLFunction> func = nil;
159 std::array<uint, 3> localSize = {};
160 uint outputVertexCount = 0;
161 QShaderDescription desc;
162 QShader::NativeResourceBindingMap nativeResourceBindingMap;
163 QShader::NativeShaderInfo nativeShaderInfo;
164 id<MTLArgumentEncoder> argumentEncoder = nil;
165 int argumentBufferIndex = -1;
168 nativeResourceBindingMap.clear();
173 [argumentEncoder release];
174 argumentEncoder = nil;
175 argumentBufferIndex = -1;
190 const QColor &colorClearValue,
191 const QRhiDepthStencilClearValue &depthStencilClearValue,
193 QRhiShadingRateMap *shadingRateMap);
195 bool preferArgumentBuffers,
196 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
226 id<MTLTexture> texture;
485 return vertexOrIndexCount * instanceCount *
sizeof(
float) * 60;
494 return patchCount *
sizeof(
float) * 128;
542 if (importDevice->dev) {
543 d->dev = (id<MTLDevice>) importDevice->dev;
545 if (importedCmdQueue)
546 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
548 qWarning(
"No MTLDevice given, cannot import");
562 return (v + byteAlign - 1) & ~(byteAlign - 1);
567 QMacAutoReleasePool pool;
570 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
584 return [cmdQueue commandBufferWithUnretainedReferences];
586 return [cmdQueue commandBuffer];
597 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor
new];
598 binArchDesc.url = sourceFileUrl;
600 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
601 [binArchDesc release];
603 const QString msg = QString::fromNSString(err.localizedDescription);
604 qWarning(
"newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
617 d->dev = MTLCreateSystemDefaultDevice();
620 qWarning(
"No MTLDevice");
624 const QString deviceName = QString::fromNSString([d->dev name]);
625 qCDebug(QRHI_LOG_INFO,
"Metal device: %s", qPrintable(deviceName));
626 driverInfoStruct.deviceName = deviceName.toUtf8();
633 const MTLDeviceLocation deviceLocation = [d->dev location];
634 switch (deviceLocation) {
635 case MTLDeviceLocationBuiltIn:
636 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
638 case MTLDeviceLocationSlot:
639 driverInfoStruct.deviceType = QRhiDriverInfo::DiscreteDevice;
641 case MTLDeviceLocationExternal:
642 driverInfoStruct.deviceType = QRhiDriverInfo::ExternalDevice;
648 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
651 const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
652 osMajor = ver.majorVersion();
653 osMinor = ver.minorVersion();
655 if (importedCmdQueue)
656 [d->cmdQueue retain];
658 d->cmdQueue = [d->dev newCommandQueue];
660 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
664 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
665 const QString label = QString::asprintf(
"Qt capture scope for QRhi %p",
this);
666 d->captureScope.label = label.toNSString();
668#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
669 caps.maxTextureSize = 16384;
670 caps.baseVertexAndInstance =
true;
671 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
672 caps.maxThreadGroupSize = 1024;
673 caps.multiView =
true;
674#elif defined(Q_OS_TVOS)
675 if ([d->dev supportsFamily:MTLGPUFamilyApple3])
676 caps.maxTextureSize = 16384;
678 caps.maxTextureSize = 8192;
679 caps.baseVertexAndInstance =
false;
680 caps.isAppleGPU =
true;
681#elif defined(Q_OS_IOS)
682 if ([d->dev supportsFamily:MTLGPUFamilyApple3]) {
683 caps.maxTextureSize = 16384;
684 caps.baseVertexAndInstance =
true;
685 }
else if ([d->dev supportsFamily:MTLGPUFamilyApple2]) {
686 caps.maxTextureSize = 8192;
687 caps.baseVertexAndInstance =
false;
689 caps.maxTextureSize = 4096;
690 caps.baseVertexAndInstance =
false;
692 caps.isAppleGPU =
true;
693 if ([d->dev supportsFamily:MTLGPUFamilyApple4])
694 caps.maxThreadGroupSize = 1024;
695 if ([d->dev supportsFamily:MTLGPUFamilyApple5])
696 caps.multiView =
true;
699 caps.supportedSampleCounts = { 1 };
700 for (
int sampleCount : { 2, 4, 8 }) {
701 if ([d->dev supportsTextureSampleCount: sampleCount])
702 caps.supportedSampleCounts.append(sampleCount);
705 caps.indirectCommandBuffers = ([d->dev supportsFamily:MTLGPUFamilyApple5]
706 || [d->dev supportsFamily:MTLGPUFamilyMac2])
707 && [d->dev supportsFamily:MTLGPUFamilyMetal3];
709 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
710 if (caps.shadingRateMap && caps.multiView)
711 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
714 caps.depthClamp = [d->dev supportsFamily:MTLGPUFamilyApple3];
716 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
717 d->setupBinaryArchive();
719 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
720 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
730 for (QMetalShader &s : d->shaderCache)
732 d->shaderCache.clear();
734 [d->captureScope release];
735 d->captureScope = nil;
737 for (
auto &pool : d->argBufPool) {
744 [d->icbArgumentBuffer release];
745 d->icbArgumentBuffer = nil;
747 [d->icbRangeBuffer release];
748 d->icbRangeBuffer = nil;
750 [d->icbNoCountBuffer release];
751 d->icbNoCountBuffer = nil;
753 [d->icbEncodeFunction release];
754 d->icbEncodeFunction = nil;
756 [d->icbEncodeFunctionU32 release];
757 d->icbEncodeFunctionU32 = nil;
759 [d->icbEncodeFunctionU16 release];
760 d->icbEncodeFunctionU16 = nil;
762 [d->icbEncodePipeline release];
763 d->icbEncodePipeline = nil;
765 [d->icbEncodePipelineU32 release];
766 d->icbEncodePipelineU32 = nil;
768 [d->icbEncodePipelineU16 release];
769 d->icbEncodePipelineU16 = nil;
777 [d->binArch release];
780 [d->cmdQueue release];
781 if (!importedCmdQueue)
791 return caps.supportedSampleCounts;
796 Q_UNUSED(sampleCount);
797 return { QSize(1, 1) };
802 return new QMetalSwapChain(
this);
805QRhiBuffer *
QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
807 return new QMetalBuffer(
this, type, usage, size);
834 static constexpr QMatrix4x4 m(1.0f, 0.0f, 0.0f, 0.0f,
835 0.0f, 1.0f, 0.0f, 0.0f,
836 0.0f, 0.0f, 0.5f, 0.5f,
837 0.0f, 0.0f, 0.0f, 1.0f);
845 bool supportsFamilyMac2 =
false;
846 bool supportsFamilyApple3 =
false;
849 supportsFamilyMac2 =
true;
851 supportsFamilyApple3 =
true;
853 supportsFamilyApple3 =
true;
857 if (format == QRhiTexture::BC5)
860 if (!supportsFamilyApple3) {
861 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
863 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
867 if (!supportsFamilyMac2)
868 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
877 case QRhi::MultisampleTexture:
879 case QRhi::MultisampleRenderBuffer:
881 case QRhi::DebugMarkers:
883 case QRhi::Timestamps:
885 case QRhi::Instancing:
887 case QRhi::CustomInstanceStepRate:
889 case QRhi::PrimitiveRestart:
891 case QRhi::NonDynamicUniformBuffers:
893 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
895 case QRhi::NPOTTextureRepeat:
897 case QRhi::RedOrAlpha8IsRed:
899 case QRhi::ElementIndexUint:
903 case QRhi::WideLines:
905 case QRhi::VertexShaderPointSize:
907 case QRhi::BaseVertex:
908 return caps.baseVertexAndInstance;
909 case QRhi::BaseInstance:
910 return caps.baseVertexAndInstance;
911 case QRhi::TriangleFanTopology:
913 case QRhi::ReadBackNonUniformBuffer:
915 case QRhi::ReadBackNonBaseMipLevel:
917 case QRhi::TexelFetch:
919 case QRhi::RenderToNonBaseMipLevel:
921 case QRhi::IntAttributes:
923 case QRhi::ScreenSpaceDerivatives:
925 case QRhi::ReadBackAnyTextureFormat:
927 case QRhi::PipelineCacheDataLoadSave:
933 case QRhi::ImageDataStride:
935 case QRhi::RenderBufferImport:
937 case QRhi::ThreeDimensionalTextures:
939 case QRhi::RenderTo3DTextureSlice:
941 case QRhi::TextureArrays:
943 case QRhi::Tessellation:
945 case QRhi::GeometryShader:
947 case QRhi::TextureArrayRange:
949 case QRhi::NonFillPolygonMode:
951 case QRhi::OneDimensionalTextures:
953 case QRhi::OneDimensionalTextureMipmaps:
955 case QRhi::HalfAttributes:
957 case QRhi::RenderToOneDimensionalTexture:
959 case QRhi::ThreeDimensionalTextureMipmaps:
961 case QRhi::MultiView:
962 return caps.multiView;
963 case QRhi::TextureViewFormat:
965 case QRhi::ResolveDepthStencil:
967 case QRhi::VariableRateShading:
969 case QRhi::VariableRateShadingMap:
970 return caps.shadingRateMap;
971 case QRhi::VariableRateShadingMapWithTexture:
973 case QRhi::PerRenderTargetBlending:
974 case QRhi::SampleVariables:
976 case QRhi::InstanceIndexIncludesBaseInstance:
978 case QRhi::DepthClamp:
979 return caps.depthClamp;
980 case QRhi::DrawIndirect:
982 case QRhi::DrawIndirectMulti:
983 return caps.indirectCommandBuffers;
984 case QRhi::ShaderDrawParameters:
986 case QRhi::DrawIndirectCount:
987 return caps.indirectCommandBuffers;
988 case QRhi::DispatchIndirect:
999 case QRhi::TextureSizeMin:
1001 case QRhi::TextureSizeMax:
1002 return caps.maxTextureSize;
1003 case QRhi::MaxColorAttachments:
1005 case QRhi::FramesInFlight:
1007 case QRhi::MaxAsyncReadbackFrames:
1009 case QRhi::MaxThreadGroupsPerDimension:
1011 case QRhi::MaxThreadsPerThreadGroup:
1013 case QRhi::MaxThreadGroupX:
1015 case QRhi::MaxThreadGroupY:
1017 case QRhi::MaxThreadGroupZ:
1018 return caps.maxThreadGroupSize;
1019 case QRhi::TextureArraySizeMax:
1021 case QRhi::MaxUniformBufferRange:
1023 case QRhi::MaxVertexInputs:
1025 case QRhi::MaxVertexOutputs:
1027 case QRhi::MaxVertexStorageBuffers:
1028 case QRhi::MaxFragmentStorageBuffers:
1030 case QRhi::ShadingRateImageTileSize:
1040 return &nativeHandlesStruct;
1045 return driverInfoStruct;
1051 result.totalPipelineCreationTime = totalPipelineCreationTime();
1068 for (QMetalShader &s : d->shaderCache)
1071 d->shaderCache.clear();
1093 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
1098 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1103 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1104 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1106 if (![d->binArch serializeToURL: url error: &err]) {
1107 const QString msg = QString::fromNSString(err.localizedDescription);
1109 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
1114 if (!f.open(QIODevice::ReadOnly)) {
1115 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1118 const QByteArray blob = f.readAll();
1122 const quint32 dataSize = quint32(blob.size());
1124 data.resize(headerSize + dataSize);
1127 header.rhiId = pipelineCacheRhiId();
1128 header.arch = quint32(
sizeof(
void*));
1129 header.dataSize = quint32(dataSize);
1130 header.osMajor = osMajor;
1131 header.osMinor = osMinor;
1132 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1134 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1135 header.driver[driverStrLen] =
'\0';
1137 memcpy(data.data(), &header, headerSize);
1138 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1148 if (data.size() < qsizetype(headerSize)) {
1149 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1153 const size_t dataOffset = headerSize;
1155 memcpy(&header, data.constData(), headerSize);
1157 const quint32 rhiId = pipelineCacheRhiId();
1158 if (header.rhiId != rhiId) {
1159 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1160 rhiId, header.rhiId);
1164 const quint32 arch = quint32(
sizeof(
void*));
1165 if (header.arch != arch) {
1166 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1171 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1172 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1173 osMajor, osMinor, header.osMajor, header.osMinor);
1177 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1178 if (strncmp(header
.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1179 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1183 if (quint64(data.size()) < quint64(dataOffset) + header.dataSize) {
1184 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1188 const char *p = data.constData() + dataOffset;
1192 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1195 tmp.write(p, header.dataSize);
1198 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1199 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1200 if (
d->setupBinaryArchive(url))
1201 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1204QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1205 int sampleCount, QRhiRenderBuffer::Flags flags,
1206 QRhiTexture::Format backingFormatHint)
1208 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1212 const QSize &pixelSize,
int depth,
int arraySize,
1213 int sampleCount, QRhiTexture::Flags flags)
1215 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1219 QRhiSampler::Filter mipmapMode,
1220 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1222 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1227 return new QMetalShadingRateMap(
this);
1231 QRhiTextureRenderTarget::Flags flags)
1238 return new QMetalGraphicsPipeline(
this);
1243 return new QMetalComputePipeline(
this);
1248 return new QMetalShaderResourceBindings(
this);
1259 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1262 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1263 if (!map || map->isEmpty())
1266 auto it = map->constFind(binding);
1267 if (it != map->cend())
1279 case QRhiShaderResourceBinding::ImageLoad:
1280 return MTLResourceUsageRead;
1281 case QRhiShaderResourceBinding::ImageStore:
1282 return MTLResourceUsageWrite;
1284 return MTLResourceUsageRead | MTLResourceUsageWrite;
1292 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : res.textures) {
1293 switch (encoderStage) {
1294 case QMetalShaderResourceBindingsData::VERTEX:
1295 [cbD->d->currentRenderPassEncoder useResource: t.mtltex usage: t.usage stages: MTLRenderStageVertex];
1297 case QMetalShaderResourceBindingsData::FRAGMENT:
1298 [cbD->d->currentRenderPassEncoder useResource: t.mtltex usage: t.usage stages: MTLRenderStageFragment];
1300 case QMetalShaderResourceBindingsData::COMPUTE:
1301 [cbD->d->currentComputePassEncoder useResource: t.mtltex usage: t.usage];
1311 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1312 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1315 case QMetalShaderResourceBindingsData::VERTEX:
1316 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1317 offsets: offsetBatch.resources.constData()
1318 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1320 case QMetalShaderResourceBindingsData::FRAGMENT:
1321 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1322 offsets: offsetBatch.resources.constData()
1323 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1325 case QMetalShaderResourceBindingsData::COMPUTE:
1326 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1327 offsets: offsetBatch.resources.constData()
1328 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1342 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1345 case QMetalShaderResourceBindingsData::VERTEX:
1346 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1347 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1349 case QMetalShaderResourceBindingsData::FRAGMENT:
1350 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1351 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1353 case QMetalShaderResourceBindingsData::COMPUTE:
1354 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1355 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1369 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1371 switch (encoderStage) {
1372 case QMetalShaderResourceBindingsData::VERTEX:
1373 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1374 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1376 case QMetalShaderResourceBindingsData::FRAGMENT:
1377 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1378 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1380 case QMetalShaderResourceBindingsData::COMPUTE:
1381 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1382 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1404 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1405 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1406 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1407 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1410 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1411 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1412 bindStageTextures(cbD, encoderStage, batch);
1415 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1416 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1417 bindStageSamplers(cbD, encoderStage, batch);
1420 if (bindingData->res[resourceStage].usesArgumentBuffer)
1421 declareStageArgumentBufferResources(cbD, encoderStage, bindingData->res[resourceStage]);
1427 case QMetalShaderResourceBindingsData::VERTEX:
1428 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1429 case QMetalShaderResourceBindingsData::TESSCTRL:
1430 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1431 case QMetalShaderResourceBindingsData::TESSEVAL:
1432 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1433 case QMetalShaderResourceBindingsData::FRAGMENT:
1434 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1435 case QMetalShaderResourceBindingsData::COMPUTE:
1436 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1439 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1444 int dynamicOffsetCount,
1445 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1446 bool offsetOnlyChange,
1447 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES],
1452 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1453 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1455 case QRhiShaderResourceBinding::UniformBuffer:
1457 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1458 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1459 quint32 offset = b->u.ubuf.offset;
1460 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1461 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1462 if (dynOfs.first == b->binding) {
1463 offset = dynOfs.second;
1468 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1469 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1470 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1471 if (nativeBinding >= 0)
1472 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1477 case QRhiShaderResourceBinding::SampledTexture:
1478 case QRhiShaderResourceBinding::Texture:
1479 case QRhiShaderResourceBinding::Sampler:
1481 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1482 for (
int elem = 0; elem < data->count; ++elem) {
1483 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1484 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1486 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1487 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1492 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1493 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1494 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1495 if (textureBinding >= 0 && texD)
1496 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex, MTLResourceUsageRead });
1497 if (samplerBinding >= 0)
1498 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1504 case QRhiShaderResourceBinding::ImageLoad:
1505 case QRhiShaderResourceBinding::ImageStore:
1506 case QRhiShaderResourceBinding::ImageLoadStore:
1508 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1509 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1511 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1512 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1513 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1514 if (nativeBinding >= 0)
1515 bindingData.res[stage].textures.append({ nativeBinding, t, storageImageUsage(b->type) });
1520 case QRhiShaderResourceBinding::BufferLoad:
1521 case QRhiShaderResourceBinding::BufferStore:
1522 case QRhiShaderResourceBinding::BufferLoadStore:
1524 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1525 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1526 quint32 offset = b->u.sbuf.offset;
1527 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1528 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1529 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1530 if (nativeBinding >= 0)
1531 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1546 const QMetalShader *shader = shaders[stage];
1547 if (!shader || !shader->argumentEncoder)
1554 if (offsetOnlyChange) {
1556 cbD
->d->currentShaderResourceBindingState.res[stage]);
1558 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &b : prev.buffers) {
1559 if (b.nativeBinding == shader->argumentBufferIndex) {
1560 res.samplers.clear();
1561 res.buffers.append(b);
1562 res.usesArgumentBuffer =
true;
1575 const quint32 argBufAlignment = qMax(quint32(shader->argumentEncoder.alignment),
1577 quint32 argBufOffset = 0;
1578 id<MTLBuffer> argBuf =
d->allocArgumentBuffer(quint32(shader->argumentEncoder.encodedLength),
1579 argBufAlignment, currentFrameSlot, &argBufOffset);
1585 qWarning(
"Failed to allocate Metal argument buffer");
1586 res.textures.clear();
1587 res.samplers.clear();
1590 [shader->argumentEncoder setArgumentBuffer: argBuf offset: argBufOffset];
1591 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(res.textures))
1592 [shader->argumentEncoder setTexture: t.mtltex atIndex: NSUInteger(t.nativeBinding)];
1593 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &sm : std::as_const(res.samplers))
1594 [shader->argumentEncoder setSamplerState: sm.mtlsampler atIndex: NSUInteger(sm.nativeBinding)];
1595 res.samplers.clear();
1596 res.buffers.append({ shader->argumentBufferIndex, argBuf, argBufOffset });
1612 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Buffer &a,
const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1613 return a.nativeBinding < b.nativeBinding;
1616 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1617 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1618 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1621 bindingData.res[stage].bufferBatches.finish();
1622 bindingData.res[stage].bufferOffsetBatches.finish();
1624 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1625 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1626 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1628 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1629 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1630 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1631 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1635 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1638 if (offsetOnlyChange)
1641 if (bindingData.res[stage].usesArgumentBuffer) {
1642 declareStageArgumentBufferResources(cbD, stage, bindingData.res[stage]);
1646 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](
const QMetalShaderResourceBindingsData::Stage::Texture &a,
const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1647 return a.nativeBinding < b.nativeBinding;
1650 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Sampler &a,
const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1651 return a.nativeBinding < b.nativeBinding;
1654 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1655 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1657 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1658 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1660 bindingData.res[stage].textureBatches.finish();
1661 bindingData.res[stage].samplerBatches.finish();
1663 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1664 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1666 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1667 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1671 bindStageTextures(cbD, stage, batch);
1674 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1675 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1677 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1678 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1682 bindStageSamplers(cbD, stage, batch);
1686 cbD
->d->currentShaderResourceBindingState = bindingData;
1697 cbD->currentPipelineGeneration = generation;
1699 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1701 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1702 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1703 cbD
->d->currentDepthStencilState =
d->ds;
1706 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1710 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1713 if (rhiD->caps.depthClamp) {
1715 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1720 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1723 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1726 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1727 slopeScale: d->slopeScaledDepthBias
1744 cbD->currentPipelineGeneration = psD->generation;
1749 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1754 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1755 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1756 workBuf->lastActiveFrameSlot = -1;
1758 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1759 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1760 workBuf->lastActiveFrameSlot = -1;
1763 psD->lastActiveFrameSlot = currentFrameSlot;
1767 int dynamicOffsetCount,
1768 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1777 srb = gfxPsD->m_shaderResourceBindings;
1779 srb = compPsD->m_shaderResourceBindings;
1783 bool hasSlottedResourceInSrb =
false;
1784 bool hasDynamicOffsetInSrb =
false;
1785 bool resNeedsRebind =
false;
1787 bool pipelineChanged =
false;
1800 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1803 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1804 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1807 case QRhiShaderResourceBinding::UniformBuffer:
1810 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1811 sanityCheckResourceOwnership(bufD);
1814 hasSlottedResourceInSrb =
true;
1815 if (b->u.ubuf.hasDynamicOffset)
1816 hasDynamicOffsetInSrb =
true;
1817 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1818 resNeedsRebind =
true;
1819 bd.ubuf.id = bufD->m_id;
1820 bd.ubuf.generation = bufD->generation;
1822 bufD->lastActiveFrameSlot = currentFrameSlot;
1825 case QRhiShaderResourceBinding::SampledTexture:
1826 case QRhiShaderResourceBinding::Texture:
1827 case QRhiShaderResourceBinding::Sampler:
1829 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1830 if (bd.stex.count != data->count) {
1831 bd.stex.count = data->count;
1832 resNeedsRebind =
true;
1834 for (
int elem = 0; elem < data->count; ++elem) {
1837 Q_ASSERT(texD || samplerD);
1838 sanityCheckResourceOwnership(texD);
1839 sanityCheckResourceOwnership(samplerD);
1840 const quint64 texId = texD ? texD->m_id : 0;
1841 const uint texGen = texD ? texD->generation : 0;
1842 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1843 const uint samplerGen = samplerD ? samplerD->generation : 0;
1844 if (texGen != bd.stex.d[elem].texGeneration
1845 || texId != bd.stex.d[elem].texId
1846 || samplerGen != bd.stex.d[elem].samplerGeneration
1847 || samplerId != bd.stex.d[elem].samplerId)
1849 resNeedsRebind =
true;
1850 bd.stex.d[elem].texId = texId;
1851 bd.stex.d[elem].texGeneration = texGen;
1852 bd.stex.d[elem].samplerId = samplerId;
1853 bd.stex.d[elem].samplerGeneration = samplerGen;
1856 texD->lastActiveFrameSlot = currentFrameSlot;
1858 samplerD->lastActiveFrameSlot = currentFrameSlot;
1862 case QRhiShaderResourceBinding::ImageLoad:
1863 case QRhiShaderResourceBinding::ImageStore:
1864 case QRhiShaderResourceBinding::ImageLoadStore:
1867 sanityCheckResourceOwnership(texD);
1868 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1869 resNeedsRebind =
true;
1870 bd.simage.id = texD->m_id;
1871 bd.simage.generation = texD->generation;
1873 texD->lastActiveFrameSlot = currentFrameSlot;
1876 case QRhiShaderResourceBinding::BufferLoad:
1877 case QRhiShaderResourceBinding::BufferStore:
1878 case QRhiShaderResourceBinding::BufferLoadStore:
1881 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1882 sanityCheckResourceOwnership(bufD);
1884 if (needsBufferSizeBuffer) {
1885 for (
int i = 0; i < 6; ++i) {
1886 const QRhiShaderResourceBinding::StageFlag stage =
1887 QRhiShaderResourceBinding::StageFlag(1 << i);
1888 if (b->stage.testFlag(stage)) {
1889 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1895 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1896 resNeedsRebind =
true;
1897 bd.sbuf.id = bufD->m_id;
1898 bd.sbuf.generation = bufD->generation;
1900 bufD->lastActiveFrameSlot = currentFrameSlot;
1909 if (needsBufferSizeBuffer) {
1911 QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1915 Q_ASSERT(compPsD
->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1916 shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage});
1919 if (gfxPsD
->d->tess.enabled) {
1929 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
1930 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
1931 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1932 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1933 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1934 == gfxPsD
->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1935 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1936 == gfxPsD
->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1937 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1938 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1939 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1940 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1942 if (gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1943 shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage});
1945 if (gfxPsD
->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1946 shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage});
1948 if (gfxPsD
->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1949 shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage});
1952 if (gfxPsD
->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1953 shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage});
1955 if (gfxPsD
->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1956 shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage});
1960 for (
const auto &shader : shaders) {
1962 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1965 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1967 int maxNativeBinding = 0;
1968 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1969 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1971 const int size = (maxNativeBinding + 1) *
sizeof(
int);
1973 Q_ASSERT(offset + size <= bufD->size());
1974 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1976 QMetalShaderResourceBindings::BoundResourceData bd;
1977 bd.sbuf.id = bufD->m_id;
1978 bd.sbuf.generation = bufD->generation;
1979 srbD->boundResourceData.append(bd);
1983 QVarLengthArray<
int, 8> bufferSizeBufferData;
1984 Q_ASSERT(storageBufferSizes.contains(shader.second));
1985 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
1986 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1987 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1993 if (bufferSizeBufferData.size() <= index)
1994 bufferSizeBufferData.resize(index + 1);
1996 Q_ASSERT(sizes.contains(block.binding));
1997 bufferSizeBufferData[index] = sizes[block.binding];
2000 QRhiBufferData data;
2001 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
2002 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
2003 Q_ASSERT(offset + size <= bufD->size());
2004 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
2007 offset += ((size + 31) / 32) * 32;
2011 bufD->lastActiveFrameSlot = currentFrameSlot;
2015 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
2017 resNeedsRebind =
true;
2020 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
2023 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
2024 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
2025 const QMetalShader *shaders[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
2029 if (gfxPsD
->d->tess.enabled) {
2032 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
2033 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
2049 cbD->currentSrbGeneration = srbD->generation;
2052 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind
2053 && !srbChanged && !srbRebuilt && !pipelineChanged;
2054 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange,
2055 resBindMaps, shaders);
2060 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
2061 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
2066 QRhiBatchedBindings<id<MTLBuffer> > buffers;
2067 QRhiBatchedBindings<NSUInteger> offsets;
2068 for (
int i = 0; i < bindingCount; ++i) {
2071 bufD->lastActiveFrameSlot = currentFrameSlot;
2072 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
2073 buffers.feed(startBinding + i, mtlbuf);
2074 offsets.feed(startBinding + i, bindings[i].second);
2089 || buffers != cbD
->d->currentVertexInputsBuffers
2090 || offsets != cbD
->d->currentVertexInputOffsets)
2093 cbD
->d->currentVertexInputsBuffers = buffers;
2094 cbD
->d->currentVertexInputOffsets = offsets;
2096 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
2097 const auto &bufferBatch(buffers.batches[i]);
2098 const auto &offsetBatch(offsets.batches[i]);
2099 [cbD->d->currentRenderPassEncoder setVertexBuffers:
2100 bufferBatch.resources.constData()
2101 offsets: offsetBatch.resources.constData()
2102 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2109 ibufD->lastActiveFrameSlot = currentFrameSlot;
2111 cbD->currentIndexOffset = indexOffset;
2112 cbD->currentIndexFormat = indexFormat;
2122 const QSize outputSize = cbD->currentTarget->pixelSize();
2123 std::array<
float, 4> vp = cbD->currentViewport.viewport();
2124 float x = 0, y = 0, w = 0, h = 0;
2126 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
2129 w = outputSize.width();
2130 h = outputSize.height();
2133 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, vp, &x, &y, &w, &h);
2137 s.x = NSUInteger(x);
2138 s.y = NSUInteger(y);
2139 s.width = NSUInteger(w);
2140 s.height = NSUInteger(h);
2141 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2148 QSize outputSize = cbD->currentTarget->pixelSize();
2154 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
2155 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(cbD->currentTarget);
2156 if (QRhiShadingRateMap *srm = rt->description().shadingRateMap()) {
2157 if (id<MTLRasterizationRateMap> rateMap =
QRHI_RES(QMetalShadingRateMap, srm)->d->rateMap) {
2158 auto screenSize = [rateMap screenSize];
2159 outputSize = QSize(screenSize.width, screenSize.height);
2166 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
2170 vp.originX =
double(x);
2171 vp.originY =
double(y);
2172 vp.width =
double(w);
2173 vp.height =
double(h);
2174 vp.znear =
double(viewport.minDepth());
2175 vp.zfar =
double(viewport.maxDepth());
2177 [cbD->d->currentRenderPassEncoder setViewport: vp];
2179 cbD->currentViewport = viewport;
2193 const QSize outputSize = cbD->currentTarget->pixelSize();
2197 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
2201 s.x = NSUInteger(x);
2202 s.y = NSUInteger(y);
2203 s.width = NSUInteger(w);
2204 s.height = NSUInteger(h);
2206 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2209 cbD->currentScissor = scissor;
2217 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
2218 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
2221 cbD->currentBlendConstants = c;
2229 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
2232 cbD->currentStencilRef = refValue;
2238 Q_UNUSED(coarsePixelSize);
2243 switch (cbD->currentTarget->resourceType()) {
2244 case QRhiResource::SwapChainRenderTarget:
2246 case QRhiResource::TextureRenderTarget:
2257 return tex && tex.storageMode != MTLStorageModeMemoryless;
2267 return finalAction == MTLStoreActionDontCare ? MTLStoreActionStore
2268 : MTLStoreActionStoreAndMultisampleResolve;
2278 for (
const auto &[index, finalAction] : std::as_const(cbD->d->deferredColorStoreActions))
2279 [cbD->d->currentRenderPassEncoder setColorStoreAction:
2280 interruptionStoreAction(finalAction, passIsEnding) atIndex: index];
2282 if (cbD->d->deferredDepthStoreAction != MTLStoreActionUnknown) {
2283 [cbD->d->currentRenderPassEncoder setDepthStoreAction:
2284 interruptionStoreAction(cbD->d->deferredDepthStoreAction, passIsEnding)];
2286 if (cbD->d->deferredStencilStoreAction != MTLStoreActionUnknown) {
2287 [cbD->d->currentRenderPassEncoder setStencilStoreAction:
2288 interruptionStoreAction(cbD->d->deferredStencilStoreAction, passIsEnding)];
2297 [cbD->d->currentRenderPassEncoder endEncoding];
2298 cbD->d->currentRenderPassEncoder = nil;
2303 id<MTLComputeCommandEncoder> maybeComputeEncoder)
2305 if (cbD
->d->currentRenderPassEncoder)
2308 if (!maybeComputeEncoder)
2309 maybeComputeEncoder = [cbD->d->cb computeCommandEncoder];
2311 return maybeComputeEncoder;
2315 id<MTLComputeCommandEncoder> computeEncoder)
2317 if (computeEncoder) {
2318 [computeEncoder endEncoding];
2319 computeEncoder = nil;
2325 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2327 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2328 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
2329 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2332 MTLLoadAction oldDepthLoad;
2333 MTLLoadAction oldStencilLoad;
2335 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2336 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
2337 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2339 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2340 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
2341 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2348 const QRhiViewport prevViewport = cbD->currentViewport;
2350 const QRhiScissor prevScissor = cbD->currentScissor;
2352 const QColor prevBlendConstants = cbD->currentBlendConstants;
2354 const quint32 prevStencilRef = cbD->currentStencilRef;
2361 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2367 if (!qFuzzyIsNull(prevViewport.viewport()[2]) || !qFuzzyIsNull(prevViewport.viewport()[3]))
2368 rhiD->setViewport(cbD, prevViewport);
2370 rhiD->setScissor(cbD, prevScissor);
2371 else if (prevHasDefaultScissor)
2373 if (prevHasBlendConstants)
2374 rhiD->setBlendConstants(cbD, prevBlendConstants);
2375 if (prevHasStencilRef)
2376 rhiD->setStencilRef(cbD, prevStencilRef);
2379 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2383 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2384 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2393 if (graphicsPipeline
->d->tess.failed)
2397 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2398 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2402 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2408 id<MTLComputeCommandEncoder> vertTescComputeEncoder
2409 = tempComputeEncoder(
this, cbD, cbD->d->tessellationComputeEncoder);
2410 cbD
->d->tessellationComputeEncoder = vertTescComputeEncoder;
2414 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2415 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2416 if (args.type == TessDrawArgs::U16Indexed)
2417 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2418 else if (args.type == TessDrawArgs::U32Indexed)
2419 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2420 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2421 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2422 [computeEncoder setComputePipelineState: computePipelineState];
2427 cbD
->d->currentComputePassEncoder = computeEncoder;
2429 cbD->d->currentComputePassEncoder = nil;
2431 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2432 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2433 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2435 if (outputBufferBinding >= 0) {
2436 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2437 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2440 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2443 if (indexBufferBinding >= 0)
2444 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2446 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2447 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2448 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2449 [computeEncoder setBuffers: bufferBatch.resources.constData()
2450 offsets: offsetBatch.resources.constData()
2451 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2455 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2456 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2458 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2459 args.draw.vertexCount, args.draw.instanceCount)];
2462 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2463 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2468 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2469 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2470 [computeEncoder setComputePipelineState: computePipelineState];
2472 cbD
->d->currentComputePassEncoder = computeEncoder;
2474 cbD->d->currentComputePassEncoder = nil;
2476 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2477 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2478 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2479 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2480 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2481 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2483 if (outputBufferBinding >= 0) {
2484 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2485 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2488 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2491 if (patchOutputBufferBinding >= 0) {
2492 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2493 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2494 if (!tescPatchOutBuf)
2496 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2499 if (tessFactorBufferBinding >= 0) {
2500 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2501 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2504 if (paramsBufferBinding >= 0) {
2506 quint32 inControlPointCount;
2513 params.patchCount = patchCount;
2514 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2515 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2516 memcpy(p, ¶ms,
sizeof(params));
2517 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2520 if (vertOutBuf && inputBufferBinding >= 0)
2521 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2523 int sgSize =
int(computePipelineState.threadExecutionWidth);
2524 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2525 while (wgSize > caps.maxThreadGroupSize) {
2527 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2529 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2530 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2538 endTempComputeEncoding(
this, cbD, cbD
->d->tessellationComputeEncoder);
2539 cbD->d->tessellationComputeEncoder = nil;
2548 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2553 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2554 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2555 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2556 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2558 if (outputBufferBinding >= 0 && tescOutBuf)
2559 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2561 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2562 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2564 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2565 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2566 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2569 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2571 patchCount: patchCount
2572 patchIndexBuffer: nil
2573 patchIndexBufferOffset: 0
2583 if (multiViewCount <= 1)
2587 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2588 if (viewMaskBufBinding == -1) {
2589 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2596 multiViewInfo.viewOffset = 0;
2597 multiViewInfo.viewCount = quint32(multiViewCount);
2601 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2602 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2603 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2604 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2608 *instanceCount *= multiViewCount;
2613 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2622 a.draw.vertexCount = vertexCount;
2623 a.draw.instanceCount = instanceCount;
2624 a.draw.firstVertex = firstVertex;
2625 a.draw.firstInstance = firstInstance;
2630 adjustForMultiViewDraw(&instanceCount, cb);
2632 if (caps.baseVertexAndInstance) {
2633 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2634 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2636 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2637 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2642 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2650 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2651 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2654 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2659 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2660 a.drawIndexed.indexCount = indexCount;
2661 a.drawIndexed.instanceCount = instanceCount;
2662 a.drawIndexed.firstIndex = firstIndex;
2663 a.drawIndexed.vertexOffset = vertexOffset;
2664 a.drawIndexed.firstInstance = firstInstance;
2665 a.drawIndexed.indexBuffer = mtlibuf;
2670 adjustForMultiViewDraw(&instanceCount, cb);
2672 if (caps.baseVertexAndInstance) {
2673 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2674 indexCount: indexCount
2675 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2676 indexBuffer: mtlibuf
2677 indexBufferOffset: indexOffset
2678 instanceCount: instanceCount
2679 baseVertex: vertexOffset
2680 baseInstance: firstInstance];
2682 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2683 indexCount: indexCount
2684 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2685 indexBuffer: mtlibuf
2686 indexBufferOffset: indexOffset
2687 instanceCount: instanceCount];
2694 if (!caps.indirectCommandBuffers)
2695 return "indirect command buffers are not supported on this device";
2699 return "the current graphics pipeline was not created with UsesIndirectDraws";
2702 return "the current graphics pipeline uses tessellation";
2704 return "the shaders of the current graphics pipeline sample textures but have no "
2705 "argument buffer variant, which Metal requires for a pipeline that supports "
2706 "indirect command buffers; rebuild them with qsb --msl-argument-buffers "
2707 "(or use MSLARGUMENTBUFFERS with qt_add_shaders)";
2717 if (!
d->icbEncodePipeline) {
2719 NSString *src = [NSString stringWithUTF8String:s_icbEncodeMsl];
2720 MTLCompileOptions *opts = [MTLCompileOptions
new];
2721 opts.languageVersion = MTLLanguageVersion2_1;
2722 id<MTLLibrary> lib = [d->dev newLibraryWithSource:src options:opts error:&err];
2725 qWarning(
"Failed to compile ICB encode kernel: %s",
2726 qPrintable(QString::fromNSString(err.localizedDescription)));
2730 d->icbEncodeFunction = [lib newFunctionWithName:@
"encode_icb"];
2731 d->icbEncodeFunctionU32 = [lib newFunctionWithName:@
"encode_icb_indexed_u32"];
2732 d->icbEncodeFunctionU16 = [lib newFunctionWithName:@
"encode_icb_indexed_u16"];
2734 if (!
d->icbEncodeFunction || !
d->icbEncodeFunctionU32 || !
d->icbEncodeFunctionU16) {
2735 qWarning(
"ICB encode kernel functions not found");
2739 NSError *errU32 = nil;
2740 NSError *errU16 = nil;
2741 d->icbEncodePipeline = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunction error:&err];
2742 d->icbEncodePipelineU32 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU32 error:&errU32];
2743 d->icbEncodePipelineU16 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU16 error:&errU16];
2744 if (!
d->icbEncodePipeline || !
d->icbEncodePipelineU32 || !
d->icbEncodePipelineU16) {
2745 NSError *firstErr = !
d->icbEncodePipeline ? err
2746 : (!
d->icbEncodePipelineU32 ? errU32 : errU16);
2747 qWarning(
"Failed to create ICB encode compute pipeline: %s",
2748 qPrintable(QString::fromNSString(firstErr.localizedDescription)));
2754 if (!
d->icbRangeBuffer) {
2755 d->icbRangeBuffer = [d->dev newBufferWithLength:
sizeof(MTLIndirectCommandBufferExecutionRange)
2756 options:MTLResourceStorageModePrivate];
2757 static constexpr quint32 noCount = 0xFFFFFFFFu;
2758 d->icbNoCountBuffer = [d->dev newBufferWithBytes:&noCount
2759 length:
sizeof(noCount)
2760 options:MTLResourceStorageModeShared];
2761 if (!
d->icbRangeBuffer || !
d->icbNoCountBuffer) {
2762 qWarning(
"Failed to create ICB helper buffers");
2776 if (!
d->icb ||
d->icbCapacity < maxDrawCount) {
2780 e.lastActiveFrameSlot = currentFrameSlot;
2781 e.stagingIcbBuffer.icb =
d->icb;
2782 e.stagingIcbBuffer.argBuffer =
d->icbArgumentBuffer;
2783 d->releaseQueue.append(e);
2786 d->icbArgumentBuffer = nil;
2788 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
2789 icbDesc.commandTypes = MTLIndirectCommandTypeDraw | MTLIndirectCommandTypeDrawIndexed;
2790 icbDesc.inheritPipelineState = YES;
2791 icbDesc.inheritBuffers = YES;
2792 icbDesc.maxVertexBufferBindCount = 0;
2793 icbDesc.maxFragmentBufferBindCount = 0;
2794 d->icb = [d->dev newIndirectCommandBufferWithDescriptor:icbDesc
2795 maxCommandCount:maxDrawCount
2796 options:MTLResourceStorageModePrivate];
2799 qWarning(
"Failed to create MTLIndirectCommandBuffer");
2803 d->icbCapacity = maxDrawCount;
2805 id<MTLArgumentEncoder> argEnc = [d->icbEncodeFunction newArgumentEncoderWithBufferIndex:1];
2806 d->icbArgumentBuffer = [d->dev newBufferWithLength:argEnc.encodedLength
2807 options:MTLResourceStorageModeShared];
2808 [argEnc setArgumentBuffer:d->icbArgumentBuffer offset:0];
2809 [argEnc setIndirectCommandBuffer:d->icb atIndex:0];
2820 id<MTLComputeCommandEncoder> computeEncoder,
2821 id<MTLIndirectCommandBuffer> targetIcb,
2822 id<MTLBuffer> targetArgBuffer,
2823 id<MTLBuffer> targetRangeBuffer,
2825 QRhiCommandBuffer::IndexFormat indexFormat,
2826 MTLPrimitiveType primitiveType,
2827 id<MTLBuffer> indirectBufMtl, quint32 indirectBufferOffset,
2828 id<MTLBuffer> indexBufMtl, quint32 indexBufferOffset,
2829 id<MTLBuffer> countBufMtl, quint32 countBufferOffset,
2830 quint32 maxDrawCount, quint32 stride)
2832 id<MTLComputePipelineState> computePipeline = d->icbEncodePipeline;
2834 computePipeline = indexFormat == QRhiCommandBuffer::IndexUInt16
2835 ? d->icbEncodePipelineU16 : d->icbEncodePipelineU32;
2837 uint32_t maxDrawCountVal = maxDrawCount;
2838 uint32_t metalPrimType = uint32_t(primitiveType);
2839 uint32_t strideVal = stride;
2841 [computeEncoder setComputePipelineState:computePipeline];
2842 [computeEncoder setBuffer:indirectBufMtl offset:indirectBufferOffset atIndex:0];
2843 [computeEncoder setBuffer:targetArgBuffer offset:0 atIndex:1];
2844 [computeEncoder setBytes:&maxDrawCountVal length:
sizeof(uint32_t) atIndex:2];
2846 [computeEncoder setBuffer:indexBufMtl offset:indexBufferOffset atIndex:3];
2847 [computeEncoder setBytes:&metalPrimType length:
sizeof(uint32_t) atIndex:4];
2848 [computeEncoder setBytes:&strideVal length:
sizeof(uint32_t) atIndex:5];
2849 [computeEncoder setBuffer:countBufMtl ? countBufMtl : d->icbNoCountBuffer
2850 offset:countBufMtl ? countBufferOffset : 0
2852 [computeEncoder setBuffer:targetRangeBuffer offset:0 atIndex:7];
2853 [computeEncoder useResource:targetIcb usage:MTLResourceUsageWrite];
2854 [computeEncoder useResource:indirectBufMtl usage:MTLResourceUsageRead];
2856 [computeEncoder useResource:indexBufMtl usage:MTLResourceUsageRead];
2858 NSUInteger tw = computePipeline.threadExecutionWidth;
2859 [computeEncoder dispatchThreads:MTLSizeMake(maxDrawCount, 1, 1)
2860 threadsPerThreadgroup:MTLSizeMake(tw, 1, 1)];
2870 QMetalBuffer *indirectBufD, quint32 indirectBufferOffset,
2872 quint32 maxDrawCount, quint32 stride)
2878 if (indexed && !indexBufD)
2881 if (!prepareIcb(maxDrawCount))
2885 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2886 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2888 id<MTLBuffer> countBufMtl = nil;
2891 countBufD->lastActiveFrameSlot = currentFrameSlot;
2892 countBufMtl = countBufD->d->buf[countBufD->d->slotted ? currentFrameSlot : 0];
2899 const auto savedVertexBuffers = cbD
->d->currentVertexInputsBuffers;
2900 const auto savedVertexOffsets = cbD
->d->currentVertexInputOffsets;
2901 const quint32 savedIndexOffset = cbD->currentIndexOffset;
2902 const QRhiCommandBuffer::IndexFormat savedIndexFormat = cbD->currentIndexFormat;
2903 id<MTLBuffer> indexBufMtl = indexed
2904 ? indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0] : nil;
2909 id<MTLComputeCommandEncoder> computeEncoder = [cbD->d->cb computeCommandEncoder];
2910 encodeIcbWithCompute(
d, computeEncoder,
d->icb,
d->icbArgumentBuffer,
d->icbRangeBuffer,
2911 indexed, savedIndexFormat, savedPipeline
->d->primitiveType,
2912 indirectBufMtl, indirectBufferOffset,
2913 indexBufMtl, savedIndexOffset,
2914 countBufMtl, countBufferOffset,
2915 maxDrawCount, stride);
2918 endTempComputeEncoding(
this, cbD, computeEncoder);
2927 if (savedFirstVertexBinding >= 0) {
2929 cbD
->d->currentVertexInputsBuffers = savedVertexBuffers;
2930 cbD
->d->currentVertexInputOffsets = savedVertexOffsets;
2931 for (
int i = 0, ie = savedVertexBuffers.batches.count(); i != ie; ++i) {
2932 const auto &bufferBatch(savedVertexBuffers.batches[i]);
2933 const auto &offsetBatch(savedVertexOffsets.batches[i]);
2934 [cbD->d->currentRenderPassEncoder setVertexBuffers:
2935 bufferBatch.resources.constData()
2936 offsets: offsetBatch.resources.constData()
2937 withRange: NSMakeRange(uint(savedFirstVertexBinding) + bufferBatch.startBinding,
2938 NSUInteger(bufferBatch.resources.count()))];
2944 cbD->currentIndexOffset = savedIndexOffset;
2945 cbD->currentIndexFormat = savedIndexFormat;
2950 [cbD->d->currentRenderPassEncoder useResource:indirectBufMtl
2951 usage:MTLResourceUsageRead
2952 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2954 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
2955 usage:MTLResourceUsageRead
2956 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2958 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:d->icb
2959 indirectBuffer:d->icbRangeBuffer
2960 indirectBufferOffset:0];
2973 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2980 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2981 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2983 if (drawCount > ICB_DRAW_COUNT_THRESHOLD && !icbUnavailableReason(cbD)
2984 && icbDraw(cbD,
false, indirectBufD, indirectBufferOffset,
nullptr, 0, drawCount, stride))
2990 NSUInteger offset = indirectBufferOffset;
2991 for (quint32 i = 0; i < drawCount; ++i) {
2992 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2993 indirectBuffer: indirectBufMtl
2994 indirectBufferOffset: offset];
3000 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
3009 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
3013 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
3014 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3016 if (drawCount > ICB_DRAW_COUNT_THRESHOLD && !icbUnavailableReason(cbD)
3017 && icbDraw(cbD,
true, indirectBufD, indirectBufferOffset,
nullptr, 0, drawCount, stride))
3023 NSUInteger offset = indirectBufferOffset;
3024 for (quint32 i = 0; i < drawCount; ++i) {
3025 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
3026 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
3027 indexBuffer: indexBufMtl
3028 indexBufferOffset: cbD->currentIndexOffset
3029 indirectBuffer: indirectBufMtl
3030 indirectBufferOffset: offset];
3040 NSString *str = [NSString stringWithUTF8String: name.constData()];
3042 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
3043 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
3045 [cbD->d->cb pushDebugGroup: str];
3054 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
3055 [cbD->d->currentRenderPassEncoder popDebugGroup];
3057 [cbD->d->cb popDebugGroup];
3066 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
3067 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
3072 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
3098 currentFrameSlot = swapChainD->currentFrameSlot;
3103 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
3111 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
3112 if (sc != swapChainD)
3113 sc->waitUntilCompleted(currentFrameSlot);
3116 [d->captureScope beginScope];
3118 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
3122 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
3129 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
3130 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
3135 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
3137 d->argBufPool[currentFrameSlot].offset = 0;
3138 d->globalFrameId += 1;
3141 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
3142 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
3145 return QRhi::FrameOpSuccess;
3154 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
3156 __block
int thisFrameSlot = currentFrameSlot;
3157 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
3158 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
3159 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
3166 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
3167 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
3168 [drawableTexture release];
3172 if (flags.testFlag(QRhi::SkipPresent)) {
3174 [commandBuffer commit];
3176 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
3178 if (swapChainD
->d->layer.presentsWithTransaction) {
3179 [commandBuffer commit];
3181 auto *metalLayer = swapChainD
->d->layer;
3182 auto presentWithTransaction = ^{
3183 [commandBuffer waitUntilScheduled];
3190 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
3191 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
3192 if (textureSize == surfaceSize) {
3195 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
3196 << textureSize <<
"not matching surface size" << surfaceSize;
3200 if (NSThread.currentThread == NSThread.mainThread) {
3201 presentWithTransaction();
3203 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
3204 Q_ASSERT(qtMetalLayer);
3206 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
3210 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
3211 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
3217 if (qtMetalLayer.displayLock.tryLockForRead()) {
3219 qtMetalLayer.displayLock.unlock();
3221 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
3222 <<
"due to" << qtMetalLayer <<
"needing display";
3228 [commandBuffer commit];
3232 [commandBuffer commit];
3239 [swapChainD->d->curDrawable release];
3240 swapChainD->d->curDrawable = nil;
3242 [d->captureScope endScope];
3246 return QRhi::FrameOpSuccess;
3253 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
3255 for (QMetalSwapChain *sc : std::as_const(swapchains))
3256 sc->waitUntilCompleted(currentFrameSlot);
3258 d->ofr.active =
true;
3259 *cb = &
d->ofr.cbWrapper;
3260 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
3262 d->argBufPool[currentFrameSlot].offset = 0;
3263 d->globalFrameId += 1;
3266 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
3267 d->ofr.lastGpuTime = 0;
3270 return QRhi::FrameOpSuccess;
3276 Q_ASSERT(
d->ofr.active);
3277 d->ofr.active =
false;
3279 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
3283 [cb waitUntilCompleted];
3285 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
3289 return QRhi::FrameOpSuccess;
3294 id<MTLCommandBuffer> cb = nil;
3297 if (
d->ofr.active) {
3300 cb =
d->ofr.cbWrapper.d->cb;
3305 cb = swapChainD->cbWrapper.d->cb;
3309 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
3310 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3311 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
3316 sc->waitUntilCompleted(i);
3322 [cb waitUntilCompleted];
3326 if (
d->ofr.active) {
3327 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
3328 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
3330 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
3331 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
3339 return QRhi::FrameOpSuccess;
3343 const QColor &colorClearValue,
3344 const QRhiDepthStencilClearValue &depthStencilClearValue,
3346 QRhiShadingRateMap *shadingRateMap)
3348 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
3349 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
3350 colorClearValue.alphaF());
3352 for (uint i = 0; i < uint(colorAttCount); ++i) {
3353 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
3354 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
3355 rp.colorAttachments[i].clearColor = c;
3358 if (hasDepthStencil) {
3359 rp.depthAttachment.loadAction = MTLLoadActionClear;
3360 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
3361 rp.stencilAttachment.loadAction = MTLLoadActionClear;
3362 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
3363 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
3364 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
3368 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
3376 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3377 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3378 if (imageSizeBytes > 0)
3379 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3384 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
3387 const QPoint dp = subresDesc.destinationTopLeft();
3388 const QByteArray rawData = subresDesc.data();
3389 QImage img = subresDesc.image();
3390 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3391 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
3393 if (!img.isNull()) {
3394 const qsizetype fullImageSizeBytes = img.sizeInBytes();
3395 QSize size = img.size();
3396 int bpl = img.bytesPerLine();
3398 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3399 const int sx = subresDesc.sourceTopLeft().x();
3400 const int sy = subresDesc.sourceTopLeft().y();
3401 if (!subresDesc.sourceSize().isEmpty())
3402 size = subresDesc.sourceSize();
3403 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3404 if (size.width() == img.width()) {
3405 const int bpc = qMax(1, img.depth() / 8);
3406 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
3407 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
3408 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
3409 size.height() * img.bytesPerLine());
3411 img = img.copy(sx, sy, size.width(), size.height());
3412 bpl = img.bytesPerLine();
3413 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
3414 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
3417 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3418 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
3421 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3422 sourceOffset: NSUInteger(*curOfs)
3423 sourceBytesPerRow: NSUInteger(bpl)
3424 sourceBytesPerImage: 0
3425 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
3426 toTexture: texD->d->tex
3427 destinationSlice: NSUInteger(is3D ? 0 : layer)
3428 destinationLevel: NSUInteger(level)
3429 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3430 options: MTLBlitOptionNone];
3432 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3433 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3434 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3435 const int subresw = subresSize.width();
3436 const int subresh = subresSize.height();
3438 if (subresDesc.sourceSize().isEmpty()) {
3442 w = subresDesc.sourceSize().width();
3443 h = subresDesc.sourceSize().height();
3448 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
3450 const int dx = aligned(dp.x(), blockDim.width());
3451 const int dy = aligned(dp.y(), blockDim.height());
3452 if (dx + w != subresw)
3453 w = aligned(w, blockDim.width());
3454 if (dy + h != subresh)
3455 h = aligned(h, blockDim.height());
3457 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3459 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3460 sourceOffset: NSUInteger(*curOfs)
3461 sourceBytesPerRow: bpl
3462 sourceBytesPerImage: 0
3463 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3464 toTexture: texD->d->tex
3465 destinationSlice: NSUInteger(is3D ? 0 : layer)
3466 destinationLevel: NSUInteger(level)
3467 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
3468 options: MTLBlitOptionNone];
3470 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3471 }
else if (!rawData.isEmpty()) {
3472 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3473 const int subresw = subresSize.width();
3474 const int subresh = subresSize.height();
3476 if (subresDesc.sourceSize().isEmpty()) {
3480 w = subresDesc.sourceSize().width();
3481 h = subresDesc.sourceSize().height();
3484 QSize size = clampedSubResourceUploadSize(QSize(w, h), dp, level, texD->m_pixelSize);
3485 quint32 bytesPerPixel = 0;
3486 textureFormatInfo(texD->m_format, size,
nullptr,
nullptr, &bytesPerPixel);
3487 size = clampedSubResourceUploadSizeForSourceData(size, subresDesc.dataStride(),
3488 bytesPerPixel, rawData.size());
3493 if (subresDesc.dataStride())
3494 bpl = subresDesc.dataStride();
3496 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
3498 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3500 if (!size.isEmpty()) {
3501 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3502 sourceOffset: NSUInteger(*curOfs)
3503 sourceBytesPerRow: bpl
3504 sourceBytesPerImage: 0
3505 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3506 toTexture: texD->d->tex
3507 destinationSlice: NSUInteger(is3D ? 0 : layer)
3508 destinationLevel: NSUInteger(level)
3509 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3510 options: MTLBlitOptionNone];
3513 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3515 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3524 id<MTLBlitCommandEncoder> blitEnc = nil;
3525 auto ensureBlit = [&blitEnc, cbD,
this]() {
3527 blitEnc = [cbD->d->cb blitCommandEncoder];
3529 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
3537 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3539 if (u.offset == 0 && u
.data.size() == bufD->m_size)
3540 bufD
->d->pendingUpdates[i].clear();
3541 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3547 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3548 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
3550 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3554 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
3555 if (bufD->m_type == QRhiBuffer::Dynamic) {
3556 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
3558 u.result->data.resize(u.readSize);
3559 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
3561 if (u.result->completed)
3562 u.result->completed();
3572 readback.activeFrameSlot = currentFrameSlot;
3573 readback.readSize = u.readSize;
3574 readback.result = u.result;
3575 readback.buf = [d->dev newBufferWithLength: u.readSize
3576 options: MTLResourceStorageModeShared];
3579 [blitEnc copyFromBuffer: bufD->d->buf[idx]
3580 sourceOffset: u.offset
3581 toBuffer: readback.buf
3582 destinationOffset: 0
3585 d->activeBufferReadbacks.append(readback);
3594 qsizetype stagingSize = 0;
3595 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3596 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3597 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3598 stagingSize += subresUploadByteSize(subresDesc);
3603 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
3604 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
3605 options: MTLResourceStorageModeShared];
3607 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
3608 qsizetype curOfs = 0;
3609 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3610 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3611 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3612 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
3616 utexD->lastActiveFrameSlot = currentFrameSlot;
3620 e.lastActiveFrameSlot = currentFrameSlot;
3621 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
3622 utexD->d->stagingBuf[currentFrameSlot] = nil;
3623 d->releaseQueue.append(e);
3628 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3629 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3630 const QPoint dp = u.desc.destinationTopLeft();
3631 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3632 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3633 const QPoint sp = u.desc.sourceTopLeft();
3636 [blitEnc copyFromTexture: srcD->d->tex
3637 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
3638 sourceLevel: NSUInteger(u.desc.sourceLevel())
3639 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
3640 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
3641 toTexture: dstD->d->tex
3642 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
3643 destinationLevel: NSUInteger(u.desc.destinationLevel())
3644 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
3646 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3649 readback.activeFrameSlot = currentFrameSlot;
3650 readback.desc = u.rb;
3651 readback.result = u.result;
3660 qWarning(
"Multisample texture cannot be read back");
3663 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3664 if (u.rb.rect().isValid())
3667 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3668 readback.format = texD->m_format;
3670 texD->lastActiveFrameSlot = currentFrameSlot;
3674 if (u.rb.rect().isValid())
3677 rect = QRect({0, 0}, swapChainD->pixelSize);
3678 readback.format = swapChainD
->d->rhiColorFormat;
3682 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
3684 readback.pixelSize = rect.size();
3687 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
3688 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
3691 [blitEnc copyFromTexture: src
3692 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
3693 sourceLevel: NSUInteger(u.rb.level())
3694 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
3695 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
3696 toBuffer: readback.buf
3697 destinationOffset: 0
3698 destinationBytesPerRow: bpl
3699 destinationBytesPerImage: 0
3700 options: MTLBlitOptionNone];
3702 d->activeTextureReadbacks.append(readback);
3706 [blitEnc generateMipmapsForTexture: utexD->d->tex];
3707 utexD->lastActiveFrameSlot = currentFrameSlot;
3713 [blitEnc popDebugGroup];
3714 [blitEnc endEncoding];
3723 if (bufD
->d->pendingUpdates[slot].isEmpty())
3726 void *p = [bufD->d->buf[slot] contents];
3727 quint32 changeBegin = UINT32_MAX;
3728 quint32 changeEnd = 0;
3729 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3730 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3731 if (u.offset < changeBegin)
3732 changeBegin = u.offset;
3733 if (u.offset + u.data.size() > changeEnd)
3734 changeEnd = u.offset + u.data.size();
3737 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3738 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3741 bufD
->d->pendingUpdates[slot].clear();
3751 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3757 QRhiRenderTarget *rt,
3758 const QColor &colorClearValue,
3759 const QRhiDepthStencilClearValue &depthStencilClearValue,
3760 QRhiResourceUpdateBatch *resourceUpdates,
3766 if (resourceUpdates)
3770 switch (rt->resourceType()) {
3771 case QRhiResource::SwapChainRenderTarget:
3775 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3778 depthStencilClearValue,
3786 if (!swapChainD
->d->curDrawable) {
3787 QMacAutoReleasePool pool;
3788 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3790 if (!swapChainD
->d->curDrawable) {
3791 qWarning(
"No drawable");
3794 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
3799 color0.resolveTex = scTex;
3805 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3808 case QRhiResource::TextureRenderTarget:
3812 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3816 depthStencilClearValue,
3818 rtTex->m_desc.shadingRateMap());
3819 if (rtD->fb.preserveColor) {
3820 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3821 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3824 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3825 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3827 int colorAttCount = 0;
3828 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3832 if (it->texture()) {
3833 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3834 if (it->multiViewCount() >= 2)
3835 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3836 }
else if (it->renderBuffer()) {
3837 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3839 if (it->resolveTexture())
3840 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3842 if (rtTex->m_desc.depthStencilBuffer())
3843 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3844 if (rtTex->m_desc.depthTexture()) {
3846 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3847 if (depthTexture->arraySize() >= 2) {
3848 const int depthLayer = rtTex->m_desc.depthLayer();
3849 if (depthLayer >= 0) {
3850 cbD
->d->currentPassRpDesc.depthAttachment.slice = NSUInteger(depthLayer);
3851 cbD
->d->currentPassRpDesc.stencilAttachment.slice = NSUInteger(depthLayer);
3852 if (colorAttCount == 0)
3853 cbD
->d->currentPassRpDesc.renderTargetArrayLength = 1;
3854 }
else if (colorAttCount == 0) {
3855 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3859 if (rtTex->m_desc.depthResolveTexture())
3860 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3861 if (rtTex->m_desc.shadingRateMap())
3862 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3870 cbD
->d->deferredColorStoreActions.clear();
3871 cbD->d->deferredDepthStoreAction = MTLStoreActionUnknown;
3872 cbD->d->deferredStencilStoreAction = MTLStoreActionUnknown;
3874 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3875 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3876 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3877 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3878 if (rtD->fb.colorAtt[i].resolveTex) {
3879 const MTLStoreAction storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3880 : MTLStoreActionMultisampleResolve;
3884 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionUnknown;
3885 cbD
->d->deferredColorStoreActions.append({ i, storeAction });
3886 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3887 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3888 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3893 Q_ASSERT(rtD->fb.dsTex);
3894 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3895 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3896 if (rtD->fb.depthNeedsStore) {
3897 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3898 }
else if (canStoreAttachment(rtD->fb.dsTex)) {
3902 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionUnknown;
3903 cbD->d->deferredDepthStoreAction = MTLStoreActionDontCare;
3904 if (rtD->fb.hasStencil) {
3905 cbD->d->currentPassRpDesc.stencilAttachment.storeAction = MTLStoreActionUnknown;
3906 cbD->d->deferredStencilStoreAction = MTLStoreActionDontCare;
3909 if (rtD->fb.dsResolveTex) {
3910 const MTLStoreAction dsStoreAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3911 : MTLStoreActionMultisampleResolve;
3917 const bool deferrable = canStoreAttachment(rtD->fb.dsTex);
3918 cbD->d->currentPassRpDesc.depthAttachment.storeAction = deferrable ? MTLStoreActionUnknown
3921 cbD
->d->deferredDepthStoreAction = dsStoreAction;
3922 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3923 if (rtD->fb.hasStencil) {
3924 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3925 cbD->d->currentPassRpDesc.stencilAttachment.storeAction = deferrable ? MTLStoreActionUnknown
3928 cbD
->d->deferredStencilStoreAction = dsStoreAction;
3933 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3938 cbD->currentTarget = rt;
3947 [cbD->d->currentRenderPassEncoder endEncoding];
3950 cbD->currentTarget =
nullptr;
3952 if (resourceUpdates)
3957 QRhiResourceUpdateBatch *resourceUpdates,
3963 if (resourceUpdates)
3966 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3976 [cbD->d->currentComputePassEncoder endEncoding];
3979 if (resourceUpdates)
3992 cbD->currentPipelineGeneration = psD->generation;
3994 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3997 psD->lastActiveFrameSlot = currentFrameSlot;
4006 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
4007 threadsPerThreadgroup: psD->d->localSize];
4011 quint32 indirectBufferOffset)
4019 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
4020 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
4027 [cbD->d->currentComputePassEncoder
4028 dispatchThreadgroupsWithIndirectBuffer: indirectBufMtl
4029 indirectBufferOffset: indirectBufferOffset
4030 threadsPerThreadgroup: psD->d->localSize];
4034 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
4035 QRhiBuffer *countBuffer, quint32 countBufferOffset,
4036 quint32 maxDrawCount, quint32 stride)
4044 qWarning(
"drawIndirectCount is not available because %s; skipping", reason);
4048 icbDraw(cbD,
false,
QRHI_RES(QMetalBuffer, indirectBuffer), indirectBufferOffset,
4049 QRHI_RES(QMetalBuffer, countBuffer), countBufferOffset, maxDrawCount, stride);
4053 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
4054 QRhiBuffer *countBuffer, quint32 countBufferOffset,
4055 quint32 maxDrawCount, quint32 stride)
4063 qWarning(
"drawIndexedIndirectCount is not available because %s; skipping", reason);
4068 qWarning(
"drawIndexedIndirectCount called without an index buffer bound; skipping");
4072 icbDraw(cbD,
true,
QRHI_RES(QMetalBuffer, indirectBuffer), indirectBufferOffset,
4073 QRHI_RES(QMetalBuffer, countBuffer), countBufferOffset, maxDrawCount, stride);
4117 e.stagingIcbBuffer.icb = slot.icb;
4118 e.stagingIcbBuffer.argBuffer = slot.argBuffer;
4119 rhiD
->d->releaseQueue.append(e);
4121 if (slot.rangeBuffer) {
4125 e.stagingBuffer.buffer = slot.rangeBuffer;
4126 rhiD
->d->releaseQueue.append(e);
4141 if (icbD->d->fill == fill)
4142 return icbD->d->frameSlots[0].icb != nil;
4150 if (!rhiD->caps.indirectCommandBuffers)
4157 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
4158 icbDesc.commandTypes = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws
4159 ? MTLIndirectCommandTypeDrawIndexed : MTLIndirectCommandTypeDraw;
4161 icbDesc.inheritPipelineState = YES;
4162 icbDesc.inheritBuffers = YES;
4163 icbDesc.maxVertexBufferBindCount = 0;
4164 icbDesc.maxFragmentBufferBindCount = 0;
4169 slot.icb = [rhiD->d->dev newIndirectCommandBufferWithDescriptor:icbDesc
4170 maxCommandCount:icbD->maxCommandCount()
4171 options:gpu ? MTLResourceStorageModePrivate
4172 : MTLResourceStorageModeShared];
4174 qWarning(
"Failed to create MTLIndirectCommandBuffer");
4179 slot.rangeBuffer = [rhiD->d->dev newBufferWithLength:
sizeof(MTLIndirectCommandBufferExecutionRange)
4180 options:MTLResourceStorageModePrivate];
4181 id<MTLArgumentEncoder> argEnc = [rhiD->d->icbEncodeFunction newArgumentEncoderWithBufferIndex:1];
4182 slot.argBuffer = [rhiD->d->dev newBufferWithLength:argEnc.encodedLength
4183 options:MTLResourceStorageModeShared];
4184 if (slot.rangeBuffer && slot.argBuffer) {
4185 [argEnc setArgumentBuffer:slot.argBuffer offset:0];
4186 [argEnc setIndirectCommandBuffer:slot.icb atIndex:0];
4188 qWarning(
"Failed to create MTLIndirectCommandBuffer helper buffers");
4200 [icbD->d->frameSlots[i].icb release];
4201 [icbD->d->frameSlots[i].rangeBuffer release];
4202 [icbD->d->frameSlots[i].argBuffer release];
4203 icbD
->d->frameSlots[i] = {};
4214 quint32 maxCommandCount)
4240 m_gpuBuiltCommandCount = 0;
4245 rhiD->unregisterResource(
this);
4256 if (!m_maxCommandCount) {
4257 qWarning(
"QRhiIndirectCommandBuffer: maxCommandCount is 0");
4264 rhiD->registerResource(
this);
4268QRhiIndirectCommandBuffer *
QRhiMetal::createIndirectCommandBuffer(QRhiIndirectCommandBuffer::Type type,
4269 quint32 maxCommandCount)
4271 return new QMetalIndirectCommandBuffer(
this, type, maxCommandCount);
4275 QRhiIndirectCommandBuffer *icb)
4287 MTLPrimitiveType primitiveType,
4288 id<MTLBuffer> indexBufMtl, quint32 indexOffset,
4289 QRhiCommandBuffer::IndexFormat indexFormat)
4291 const bool indexed = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4294 && slot.generation == icbD->contentsGeneration()
4295 && slot.primitiveType == primitiveType
4296 && (!indexed || (slot.indexBuf == indexBufMtl
4297 && slot.indexOffset == indexOffset
4298 && slot.indexFormat == indexFormat));
4304 MTLPrimitiveType primitiveType,
4305 id<MTLBuffer> indexBufMtl, quint32 indexOffset,
4306 QRhiCommandBuffer::IndexFormat indexFormat)
4308 const bool indexed = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4310 if (qrhimtl_icbSlotMatches(icbD, slot, primitiveType, indexBufMtl, indexOffset, indexFormat))
4313 const quint32 count = icbD->recordedCommandCount();
4315 const MTLIndexType indexType = indexFormat == QRhiCommandBuffer::IndexUInt16
4316 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32;
4317 const quint32 indexSize = indexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4;
4319 for (quint32 i = 0; i < count; ++i) {
4321 id<MTLIndirectRenderCommand> rc = [slot.icb indirectRenderCommandAtIndex:i];
4322 [rc drawIndexedPrimitives:primitiveType
4323 indexCount:c.indexCount
4325 indexBuffer:indexBufMtl
4326 indexBufferOffset:indexOffset + c.firstIndex * indexSize
4327 instanceCount:c.instanceCount
4328 baseVertex:c.vertexOffset
4329 baseInstance:c.firstInstance];
4332 const QRhiIndirectDrawCommand *cmds = icbD->drawCommands();
4333 for (quint32 i = 0; i < count; ++i) {
4334 const QRhiIndirectDrawCommand &c(cmds[i]);
4335 id<MTLIndirectRenderCommand> rc = [slot.icb indirectRenderCommandAtIndex:i];
4336 [rc drawPrimitives:primitiveType
4337 vertexStart:c.firstVertex
4338 vertexCount:c.vertexCount
4339 instanceCount:c.instanceCount
4340 baseInstance:c.firstInstance];
4345 if (count < icbD->maxCommandCount())
4346 [slot.icb resetWithRange:NSMakeRange(count, icbD->maxCommandCount() - count)];
4348 slot.generation = icbD->contentsGeneration();
4349 slot.primitiveType = primitiveType;
4350 slot.indexBuf = indexBufMtl;
4351 slot.indexOffset = indexOffset;
4352 slot.indexFormat = indexFormat;
4358 quint32 firstCommand, quint32 count,
4359 int currentFrameSlot)
4363 if (icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws) {
4367 id<MTLBuffer> indexBufMtl = indexBufD
->d->buf[indexBufD
->d->slotted ? currentFrameSlot : 0];
4368 const MTLIndexType indexType = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16
4369 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32;
4370 const quint32 indexSize = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4;
4372 for (quint32 i = 0; i < count; ++i) {
4374 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: primitiveType
4375 indexCount: c.indexCount
4376 indexType: indexType
4377 indexBuffer: indexBufMtl
4378 indexBufferOffset: cbD->currentIndexOffset + c.firstIndex * indexSize
4379 instanceCount: c.instanceCount
4380 baseVertex: c.vertexOffset
4381 baseInstance: c.firstInstance];
4384 const QRhiIndirectDrawCommand *cmds = icbD->drawCommands();
4385 for (quint32 i = 0; i < count; ++i) {
4386 const QRhiIndirectDrawCommand &c(cmds[firstCommand + i]);
4387 [cbD->d->currentRenderPassEncoder drawPrimitives: primitiveType
4388 vertexStart: c.firstVertex
4389 vertexCount: c.vertexCount
4390 instanceCount: c.instanceCount
4391 baseInstance: c.firstInstance];
4397 quint32 firstCommand, quint32 commandCount)
4403 icbD->lastActiveFrameSlot = currentFrameSlot;
4405 const bool indexed = icb->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4409 qWarning(
"executeIndirect: the indirect command buffer was built for a different "
4410 "topology than the current graphics pipeline uses; skipping");
4415 const quint32 total = icbD->commandCount();
4416 const bool deviceCount = icbD
->d->buildInfo.countBuffer !=
nullptr;
4417 NSRange range = NSMakeRange(0, total);
4423 if (firstCommand != 0 || commandCount < total) {
4424 qWarning(
"executeIndirect: firstCommand and commandCount cannot be honoured "
4425 "together with a device-side count; executing all %u command(s)", total);
4428 if (firstCommand >= total)
4430 const quint32 count = qMin(commandCount, total - firstCommand);
4433 range = NSMakeRange(firstCommand, count);
4436 if (indexed && icbD
->d->buildInfo.indexBuffer) {
4438 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4439 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
4440 usage:MTLResourceUsageRead
4441 stages:MTLRenderStageVertex | MTLRenderStageFragment];
4444 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb
4445 indirectBuffer:slot.rangeBuffer
4446 indirectBufferOffset:0];
4448 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb withRange:range];
4454 const QRhiIndirectCommandBufferBuildInfo &info(icbD
->d->buildInfo);
4455 if (info.countBuffer) {
4456 qWarning(
"executeIndirect: a device-side count needs an indirect command buffer, "
4457 "which is not available here; skipping");
4460 const quint32 canonicalStride = indexed ?
sizeof(QRhiIndexedIndirectDrawCommand)
4461 :
sizeof(QRhiIndirectDrawCommand);
4462 const quint32 stride = info.stride ? info.stride : canonicalStride;
4463 const quint32 total = icbD->commandCount();
4464 if (firstCommand >= total)
4466 const quint32 count = qMin(commandCount, total - firstCommand);
4469 const quint32 offset = info.sourceBufferOffset + firstCommand * stride;
4471 drawIndexedIndirect(cb, info.sourceBuffer, offset, count, stride);
4473 drawIndirect(cb, info.sourceBuffer, offset, count, stride);
4477 const quint32 total = icbD->recordedCommandCount();
4478 if (firstCommand >= total)
4480 const quint32 count = qMin(commandCount, total - firstCommand);
4487 qrhimtl_replayIcbOnCpu(cbD, icbD, firstCommand, count, currentFrameSlot);
4492 id<MTLBuffer> indexBufMtl = nil;
4493 quint32 indexOffset = 0;
4498 indexBufD->lastActiveFrameSlot = currentFrameSlot;
4499 indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4500 indexOffset = cbD->currentIndexOffset;
4504 if (slot.usedInFrameId == d->globalFrameId
4505 && !qrhimtl_icbSlotMatches(icbD, slot, primitiveType, indexBufMtl, indexOffset,
4506 cbD->currentIndexFormat))
4511 qWarning(
"executeIndirect: the same indirect command buffer is executed more than once "
4512 "in a frame, with different contents, topology or index buffer state; "
4513 "falling back to individual draw calls");
4514 qrhimtl_replayIcbOnCpu(cbD, icbD, firstCommand, count, currentFrameSlot);
4518 qrhimtl_encodeIcbFromCpu(icbD, slot, primitiveType,
4519 indexBufMtl, indexOffset, cbD->currentIndexFormat);
4524 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
4525 usage:MTLResourceUsageRead
4526 stages:MTLRenderStageVertex | MTLRenderStageFragment];
4528 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb
4529 withRange:NSMakeRange(firstCommand, count)];
4530 slot.usedInFrameId =
d->globalFrameId;
4534 const QRhiIndirectCommandBufferBuildInfo &info)
4540 icbD->lastActiveFrameSlot = currentFrameSlot;
4542 const bool indexed = icb->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4544 if (indexed && !info.indexBuffer) {
4545 qWarning(
"buildIndirect: an IndexedDraws indirect command buffer needs an "
4546 "index buffer in QRhiIndirectCommandBufferBuildInfo; skipping");
4550 quint32 count = info.commandCount ? info.commandCount : icbD->m_maxCommandCount;
4551 if (count > icbD->m_maxCommandCount) {
4552 qWarning(
"QRhiIndirectCommandBuffer: buildIndirect() with commandCount %u exceeds "
4553 "maxCommandCount %u; clamping", count, icbD->m_maxCommandCount);
4554 count = icbD->m_maxCommandCount;
4556 icbD->m_gpuBuilt =
true;
4557 icbD->m_gpuBuiltCommandCount = count;
4562 icbD
->d->buildInfo = info;
4571 srcBufD->lastActiveFrameSlot = currentFrameSlot;
4572 id<MTLBuffer> srcBufMtl = srcBufD->d->buf[srcBufD->d->slotted ? currentFrameSlot : 0];
4574 id<MTLBuffer> indexBufMtl = nil;
4577 indexBufD->lastActiveFrameSlot = currentFrameSlot;
4578 indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4581 id<MTLBuffer> countBufMtl = nil;
4582 if (info.countBuffer) {
4585 countBufD->lastActiveFrameSlot = currentFrameSlot;
4586 countBufMtl = countBufD->d->buf[countBufD->d->slotted ? currentFrameSlot : 0];
4589 const quint32 stride = info.stride ? info.stride
4590 : (indexed ?
sizeof(QRhiIndexedIndirectDrawCommand)
4591 :
sizeof(QRhiIndirectDrawCommand));
4595 id<MTLComputeCommandEncoder> computeEncoder = [cbD->d->cb computeCommandEncoder];
4596 encodeIcbWithCompute(
d, computeEncoder, slot.icb, slot.argBuffer, slot.rangeBuffer,
4597 indexed, info.indexFormat,
4598 toMetalPrimitiveType(info.topology),
4599 srcBufMtl, info.sourceBufferOffset,
4600 indexBufMtl, info.indexBufferOffset,
4601 countBufMtl, info.countBufferOffset,
4602 icbD->commandCount(), stride);
4603 [computeEncoder endEncoding];
4607 icbD
->d->buildInfo = info;
4608 icbD->d->builtSlot = currentFrameSlot;
4615 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4616 [e.buffer.buffers[i] release];
4621 [e.renderbuffer.texture release];
4626 [e.texture.texture release];
4627 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4628 [e.texture.stagingBuffers[i] release];
4629 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
4630 [e.texture.views[i] release];
4635 [e.sampler.samplerState release];
4640 for (
int i =
d->releaseQueue.count() - 1; i >= 0; --i) {
4642 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
4656 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
4657 [e.stagingBuffer.buffer release];
4659 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
4660 [e.graphicsPipeline.pipelineState release];
4661 [e.graphicsPipeline.depthStencilState release];
4662 [e.graphicsPipeline.tessVertexComputeState[0] release];
4663 [e.graphicsPipeline.tessVertexComputeState[1] release];
4664 [e.graphicsPipeline.tessVertexComputeState[2] release];
4665 [e.graphicsPipeline.tessTessControlComputeState release];
4667 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
4668 [e.computePipeline.pipelineState release];
4670 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
4671 [e.shadingRateMap.rateMap release];
4673 case QRhiMetalData::DeferredReleaseEntry::StagingIcbBuffer:
4674 [e.stagingIcbBuffer.icb release];
4675 [e.stagingIcbBuffer.argBuffer release];
4680 d->releaseQueue.removeAt(i);
4687 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
4689 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
4691 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
4692 readback.result->format = readback.format;
4693 readback.result->pixelSize = readback.pixelSize;
4694 readback.result->data.resize(
int(readback.bufSize));
4695 void *p = [readback.buf contents];
4696 memcpy(readback.result->data.data(), p, readback.bufSize);
4697 [readback.buf release];
4699 if (readback.result->completed)
4700 completedCallbacks.append(readback.result->completed);
4702 d->activeTextureReadbacks.remove(i);
4706 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
4708 if (forced || currentFrameSlot == readback.activeFrameSlot
4709 || readback.activeFrameSlot < 0) {
4710 readback.result->data.resize(readback.readSize);
4711 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
4713 memcpy(readback.result->data.data(), p, size_t(readback.readSize));
4714 [readback.buf release];
4716 if (readback.result->completed)
4717 completedCallbacks.append(readback.result->completed);
4719 d->activeBufferReadbacks.remove(i);
4723 for (
auto f : completedCallbacks)
4731 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4751 e.buffer.buffers[i] =
d->buf[i];
4753 d->pendingUpdates[i].clear();
4758 rhiD
->d->releaseQueue.append(e);
4759 rhiD->unregisterResource(
this);
4768 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
4769 qWarning(
"StorageBuffer cannot be combined with Dynamic");
4773 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
4774 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
4777 MTLResourceOptions opts = MTLResourceStorageModeShared;
4781 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
4782 opts = MTLResourceStorageModeManaged;
4791 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer);
4793 if (
int(m_usage) == WorkBufPoolUsage)
4798 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
4799 if (!m_objectName.isEmpty()) {
4801 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
4803 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
4804 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
4812 rhiD->registerResource(
this);
4824 b.objects[i] = &
d->buf[i];
4829 return { { &
d->buf[0] }, 1 };
4839 Q_ASSERT(m_type == Dynamic);
4841 Q_ASSERT(rhiD->inFrame);
4842 const int slot = rhiD->currentFrameSlot;
4843 void *p = [d->buf[slot] contents];
4844 return static_cast<
char *>(p);
4851 QRHI_RES_RHI(QRhiMetal);
4852 const int slot = rhiD->currentFrameSlot;
4853 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
4864 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
4866 case QRhiTexture::RGBA8:
4867 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
4868 case QRhiTexture::BGRA8:
4869 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
4870 case QRhiTexture::R8:
4872 return MTLPixelFormatR8Unorm;
4874 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
4876 case QRhiTexture::R8SI:
4877 return MTLPixelFormatR8Sint;
4878 case QRhiTexture::R8UI:
4879 return MTLPixelFormatR8Uint;
4880 case QRhiTexture::RG8:
4882 return MTLPixelFormatRG8Unorm;
4884 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
4886 case QRhiTexture::R16:
4887 return MTLPixelFormatR16Unorm;
4888 case QRhiTexture::RG16:
4889 return MTLPixelFormatRG16Unorm;
4890 case QRhiTexture::RED_OR_ALPHA8:
4891 return MTLPixelFormatR8Unorm;
4893 case QRhiTexture::RGBA16F:
4894 return MTLPixelFormatRGBA16Float;
4895 case QRhiTexture::RGBA32F:
4896 return MTLPixelFormatRGBA32Float;
4897 case QRhiTexture::R16F:
4898 return MTLPixelFormatR16Float;
4899 case QRhiTexture::R32F:
4900 return MTLPixelFormatR32Float;
4902 case QRhiTexture::RGB10A2:
4903 return MTLPixelFormatRGB10A2Unorm;
4905 case QRhiTexture::R32SI:
4906 return MTLPixelFormatR32Sint;
4907 case QRhiTexture::R32UI:
4908 return MTLPixelFormatR32Uint;
4909 case QRhiTexture::RG32SI:
4910 return MTLPixelFormatRG32Sint;
4911 case QRhiTexture::RG32UI:
4912 return MTLPixelFormatRG32Uint;
4913 case QRhiTexture::RGBA32SI:
4914 return MTLPixelFormatRGBA32Sint;
4915 case QRhiTexture::RGBA32UI:
4916 return MTLPixelFormatRGBA32Uint;
4919 case QRhiTexture::D16:
4920 return MTLPixelFormatDepth16Unorm;
4921 case QRhiTexture::D24:
4922 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
4923 case QRhiTexture::D24S8:
4924 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
4926 case QRhiTexture::D16:
4927 return MTLPixelFormatDepth32Float;
4928 case QRhiTexture::D24:
4929 return MTLPixelFormatDepth32Float;
4930 case QRhiTexture::D24S8:
4931 return MTLPixelFormatDepth32Float_Stencil8;
4933 case QRhiTexture::D32F:
4934 return MTLPixelFormatDepth32Float;
4935 case QRhiTexture::D32FS8:
4936 return MTLPixelFormatDepth32Float_Stencil8;
4939 case QRhiTexture::BC1:
4940 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
4941 case QRhiTexture::BC2:
4942 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
4943 case QRhiTexture::BC3:
4944 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
4945 case QRhiTexture::BC4:
4946 return MTLPixelFormatBC4_RUnorm;
4947 case QRhiTexture::BC5:
4948 qWarning(
"QRhiMetal does not support BC5");
4949 return MTLPixelFormatInvalid;
4950 case QRhiTexture::BC6H:
4951 return MTLPixelFormatBC6H_RGBUfloat;
4952 case QRhiTexture::BC7:
4953 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
4955 case QRhiTexture::BC1:
4956 case QRhiTexture::BC2:
4957 case QRhiTexture::BC3:
4958 case QRhiTexture::BC4:
4959 case QRhiTexture::BC5:
4960 case QRhiTexture::BC6H:
4961 case QRhiTexture::BC7:
4962 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
4963 return MTLPixelFormatInvalid;
4967 case QRhiTexture::ETC2_RGB8:
4968 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
4969 case QRhiTexture::ETC2_RGB8A1:
4970 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
4971 case QRhiTexture::ETC2_RGBA8:
4972 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
4974 case QRhiTexture::ASTC_4x4:
4975 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
4976 case QRhiTexture::ASTC_5x4:
4977 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
4978 case QRhiTexture::ASTC_5x5:
4979 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
4980 case QRhiTexture::ASTC_6x5:
4981 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
4982 case QRhiTexture::ASTC_6x6:
4983 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
4984 case QRhiTexture::ASTC_8x5:
4985 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
4986 case QRhiTexture::ASTC_8x6:
4987 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
4988 case QRhiTexture::ASTC_8x8:
4989 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
4990 case QRhiTexture::ASTC_10x5:
4991 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
4992 case QRhiTexture::ASTC_10x6:
4993 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
4994 case QRhiTexture::ASTC_10x8:
4995 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
4996 case QRhiTexture::ASTC_10x10:
4997 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
4998 case QRhiTexture::ASTC_12x10:
4999 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
5000 case QRhiTexture::ASTC_12x12:
5001 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
5003 case QRhiTexture::ETC2_RGB8:
5004 if (d->caps.isAppleGPU)
5005 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
5006 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5007 return MTLPixelFormatInvalid;
5008 case QRhiTexture::ETC2_RGB8A1:
5009 if (d->caps.isAppleGPU)
5010 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
5011 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5012 return MTLPixelFormatInvalid;
5013 case QRhiTexture::ETC2_RGBA8:
5014 if (d->caps.isAppleGPU)
5015 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
5016 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5017 return MTLPixelFormatInvalid;
5018 case QRhiTexture::ASTC_4x4:
5019 if (d->caps.isAppleGPU)
5020 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
5021 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5022 return MTLPixelFormatInvalid;
5023 case QRhiTexture::ASTC_5x4:
5024 if (d->caps.isAppleGPU)
5025 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
5026 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5027 return MTLPixelFormatInvalid;
5028 case QRhiTexture::ASTC_5x5:
5029 if (d->caps.isAppleGPU)
5030 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
5031 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5032 return MTLPixelFormatInvalid;
5033 case QRhiTexture::ASTC_6x5:
5034 if (d->caps.isAppleGPU)
5035 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
5036 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5037 return MTLPixelFormatInvalid;
5038 case QRhiTexture::ASTC_6x6:
5039 if (d->caps.isAppleGPU)
5040 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
5041 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5042 return MTLPixelFormatInvalid;
5043 case QRhiTexture::ASTC_8x5:
5044 if (d->caps.isAppleGPU)
5045 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
5046 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5047 return MTLPixelFormatInvalid;
5048 case QRhiTexture::ASTC_8x6:
5049 if (d->caps.isAppleGPU)
5050 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
5051 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5052 return MTLPixelFormatInvalid;
5053 case QRhiTexture::ASTC_8x8:
5054 if (d->caps.isAppleGPU)
5055 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
5056 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5057 return MTLPixelFormatInvalid;
5058 case QRhiTexture::ASTC_10x5:
5059 if (d->caps.isAppleGPU)
5060 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
5061 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5062 return MTLPixelFormatInvalid;
5063 case QRhiTexture::ASTC_10x6:
5064 if (d->caps.isAppleGPU)
5065 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
5066 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5067 return MTLPixelFormatInvalid;
5068 case QRhiTexture::ASTC_10x8:
5069 if (d->caps.isAppleGPU)
5070 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
5071 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5072 return MTLPixelFormatInvalid;
5073 case QRhiTexture::ASTC_10x10:
5074 if (d->caps.isAppleGPU)
5075 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
5076 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5077 return MTLPixelFormatInvalid;
5078 case QRhiTexture::ASTC_12x10:
5079 if (d->caps.isAppleGPU)
5080 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
5081 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5082 return MTLPixelFormatInvalid;
5083 case QRhiTexture::ASTC_12x12:
5084 if (d->caps.isAppleGPU)
5085 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
5086 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5087 return MTLPixelFormatInvalid;
5092 return MTLPixelFormatInvalid;
5097 int sampleCount, QRhiRenderBuffer::Flags flags,
5098 QRhiTexture::Format backingFormatHint)
5119 e.renderbuffer.texture =
d->tex;
5124 rhiD
->d->releaseQueue.append(e);
5125 rhiD->unregisterResource(
this);
5134 if (m_pixelSize.isEmpty())
5138 samples = rhiD->effectiveSampleCount(m_sampleCount);
5140 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
5141 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
5142 desc.width = NSUInteger(m_pixelSize.width());
5143 desc.height = NSUInteger(m_pixelSize.height());
5145 desc.sampleCount = NSUInteger(
samples);
5146 desc.resourceOptions = MTLResourceStorageModePrivate;
5147 desc.usage = MTLTextureUsageRenderTarget;
5152 const bool canBeMemoryless = !m_flags.testFlag(QRhiRenderBuffer::NoTransientBacking);
5157 if (rhiD->caps.isAppleGPU && canBeMemoryless) {
5158 desc.storageMode = MTLStorageModeMemoryless;
5159 d->format = MTLPixelFormatDepth32Float_Stencil8;
5161 desc.storageMode = MTLStorageModePrivate;
5162 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
5163 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
5166 desc.storageMode = canBeMemoryless ? MTLStorageModeMemoryless : MTLStorageModePrivate;
5167 d->format = MTLPixelFormatDepth32Float_Stencil8;
5169 desc.pixelFormat =
d->format;
5172 desc.storageMode = MTLStorageModePrivate;
5173 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
5174 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
5176 d->format = MTLPixelFormatRGBA8Unorm;
5177 desc.pixelFormat =
d->format;
5184 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
5187 if (!m_objectName.isEmpty())
5188 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
5192 rhiD->registerResource(
this);
5198 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
5199 return m_backingFormatHint;
5201 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
5205 int arraySize,
int sampleCount, Flags flags)
5209 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
5210 d->stagingBuf[i] = nil;
5212 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
5213 d->perLevelViews[i] = nil;
5231 e.texture.texture = d->owns ? d->tex : nil;
5235 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
5236 d->stagingBuf[i] = nil;
5239 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
5240 e.texture.views[i] =
d->perLevelViews[i];
5241 d->perLevelViews[i] = nil;
5246 rhiD
->d->releaseQueue.append(e);
5247 rhiD->unregisterResource(
this);
5256 const bool isCube = m_flags.testFlag(CubeMap);
5257 const bool is3D = m_flags.testFlag(ThreeDimensional);
5258 const bool isArray = m_flags.testFlag(TextureArray);
5259 const bool hasMipMaps = m_flags.testFlag(MipMapped);
5260 const bool is1D = m_flags.testFlag(OneDimensional);
5262 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
5263 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
5266 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
5267 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
5268 samples = rhiD->effectiveSampleCount(m_sampleCount);
5271 qWarning(
"Cubemap texture cannot be multisample");
5275 qWarning(
"3D texture cannot be multisample");
5279 qWarning(
"Multisample texture cannot have mipmaps");
5283 if (isCube && is3D) {
5284 qWarning(
"Texture cannot be both cube and 3D");
5287 if (isArray && is3D) {
5288 qWarning(
"Texture cannot be both array and 3D");
5292 qWarning(
"Texture cannot be both 1D and 3D");
5295 if (is1D && isCube) {
5296 qWarning(
"Texture cannot be both 1D and cube");
5299 if (m_depth > 1 && !is3D) {
5300 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
5303 if (m_arraySize > 0 && !isArray) {
5304 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
5307 if (m_arraySize < 1 && isArray) {
5308 qWarning(
"Texture is an array but array size is %d", m_arraySize);
5312 if (!rhiD->textureFormatInfo(m_format, size,
nullptr,
nullptr,
nullptr))
5316 *adjustedSize = size;
5324 if (!prepareCreate(&size))
5327 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
5329 const bool isCube = m_flags.testFlag(CubeMap);
5330 const bool is3D = m_flags.testFlag(ThreeDimensional);
5331 const bool isArray = m_flags.testFlag(TextureArray);
5332 const bool is1D = m_flags.testFlag(OneDimensional);
5334 desc.textureType = MTLTextureTypeCube;
5336 desc.textureType = MTLTextureType3D;
5338 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
5339 }
else if (isArray) {
5340 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
5342 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
5344 desc.pixelFormat =
d->format;
5345 desc.width = NSUInteger(size.width());
5346 desc.height = NSUInteger(size.height());
5347 desc.depth = is3D ? qMax(1, m_depth) : 1;
5350 desc.sampleCount = NSUInteger(
samples);
5352 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
5353 desc.resourceOptions = MTLResourceStorageModePrivate;
5354 desc.storageMode = MTLStorageModePrivate;
5355 desc.usage = MTLTextureUsageShaderRead;
5356 if (m_flags.testFlag(RenderTarget))
5357 desc.usage |= MTLTextureUsageRenderTarget;
5358 if (m_flags.testFlag(UsedWithLoadStore))
5359 desc.usage |= MTLTextureUsageShaderWrite;
5362 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
5365 if (!m_objectName.isEmpty())
5366 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
5372 rhiD->registerResource(
this);
5378 id<MTLTexture> tex = id<MTLTexture>(src.object);
5382 if (!prepareCreate())
5392 rhiD->registerResource(
this);
5398 return {quint64(
d->tex), 0};
5404 if (perLevelViews[level])
5405 return perLevelViews[level];
5407 const MTLTextureType type = [tex textureType];
5408 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
5409 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
5410 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
5411 levels: NSMakeRange(NSUInteger(level), 1)
5412 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
5414 perLevelViews[level] = view;
5419 AddressMode u, AddressMode v, AddressMode w)
5433 if (!
d->samplerState)
5440 e.sampler.samplerState =
d->samplerState;
5441 d->samplerState = nil;
5445 rhiD
->d->releaseQueue.append(e);
5446 rhiD->unregisterResource(
this);
5453 case QRhiSampler::Nearest:
5454 return MTLSamplerMinMagFilterNearest;
5455 case QRhiSampler::Linear:
5456 return MTLSamplerMinMagFilterLinear;
5459 return MTLSamplerMinMagFilterNearest;
5466 case QRhiSampler::None:
5467 return MTLSamplerMipFilterNotMipmapped;
5468 case QRhiSampler::Nearest:
5469 return MTLSamplerMipFilterNearest;
5470 case QRhiSampler::Linear:
5471 return MTLSamplerMipFilterLinear;
5474 return MTLSamplerMipFilterNotMipmapped;
5481 case QRhiSampler::Repeat:
5482 return MTLSamplerAddressModeRepeat;
5483 case QRhiSampler::ClampToEdge:
5484 return MTLSamplerAddressModeClampToEdge;
5485 case QRhiSampler::Mirror:
5486 return MTLSamplerAddressModeMirrorRepeat;
5489 return MTLSamplerAddressModeClampToEdge;
5496 case QRhiSampler::Never:
5497 return MTLCompareFunctionNever;
5498 case QRhiSampler::Less:
5499 return MTLCompareFunctionLess;
5500 case QRhiSampler::Equal:
5501 return MTLCompareFunctionEqual;
5502 case QRhiSampler::LessOrEqual:
5503 return MTLCompareFunctionLessEqual;
5504 case QRhiSampler::Greater:
5505 return MTLCompareFunctionGreater;
5506 case QRhiSampler::NotEqual:
5507 return MTLCompareFunctionNotEqual;
5508 case QRhiSampler::GreaterOrEqual:
5509 return MTLCompareFunctionGreaterEqual;
5510 case QRhiSampler::Always:
5511 return MTLCompareFunctionAlways;
5514 return MTLCompareFunctionNever;
5520 if (
d->samplerState)
5523 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
5524 desc.minFilter = toMetalFilter(m_minFilter);
5525 desc.magFilter = toMetalFilter(m_magFilter);
5526 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
5527 desc.sAddressMode = toMetalAddressMode(m_addressU);
5528 desc.tAddressMode = toMetalAddressMode(m_addressV);
5529 desc.rAddressMode = toMetalAddressMode(m_addressW);
5530 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
5536 desc.supportArgumentBuffers = rhiD->caps.indirectCommandBuffers ? YES : NO;
5537 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
5539 if (!
d->samplerState) {
5543 qWarning(
"Failed to create Metal sampler state. The number of unique sampler "
5544 "states with argument buffer support may have exceeded the limit of %u.",
5545 uint(rhiD
->d->dev.maxArgumentBufferSamplerCount));
5551 rhiD->registerResource(
this);
5576 e.shadingRateMap.rateMap =
d->rateMap;
5581 rhiD
->d->releaseQueue.append(e);
5582 rhiD->unregisterResource(
this);
5591 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
5595 [d->rateMap retain];
5600 rhiD->registerResource(
this);
5609 serializedFormatData.reserve(16);
5621 rhiD->unregisterResource(
this);
5655 serializedFormatData.clear();
5656 auto p =
std::back_inserter(serializedFormatData);
5678 rhiD->registerResource(rpD,
false);
5684 return serializedFormatData;
5706 return d->pixelSize;
5720 const QRhiTextureRenderTargetDescription &desc,
5737 rhiD->unregisterResource(
this);
5742 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
5745 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
5747 for (
int i = 0; i < colorAttachmentCount; ++i) {
5748 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
5754 if (m_desc.depthTexture())
5755 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
5756 else if (m_desc.depthStencilBuffer())
5757 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
5759 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
5764 rhiD->registerResource(rpD,
false);
5771 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
5772 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
5773 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
5777 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
5781 Q_ASSERT(texD || rbD);
5782 id<MTLTexture> dst = nil;
5786 if (attIndex == 0) {
5787 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
5790 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
5793 if (attIndex == 0) {
5794 d->pixelSize = rbD->pixelSize();
5801 colorAtt
.slice = is3D ? it->layer() : 0;
5802 colorAtt
.level = it->level();
5804 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
5807 d->fb.colorAtt[attIndex] = colorAtt;
5811 if (hasDepthStencil) {
5812 if (m_desc.depthTexture()) {
5814 d->fb.dsTex = depthTexD
->d->tex;
5815 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
5816 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
5817 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
5819 d->pixelSize = depthTexD->pixelSize();
5824 d->fb.dsTex = depthRbD
->d->tex;
5825 d->fb.hasStencil =
true;
5826 d->fb.depthNeedsStore =
false;
5827 d->fb.preserveDs =
false;
5829 d->pixelSize = depthRbD->pixelSize();
5833 if (m_desc.depthResolveTexture()) {
5835 d->fb.dsResolveTex = depthResolveTexD
->d->tex;
5842 if (d->colorAttCount > 0)
5843 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
5845 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
5847 rhiD->registerResource(
this,
false);
5853 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
5856 return d->pixelSize;
5881 sortedBindings.clear();
5886 rhiD->unregisterResource(
this);
5891 if (!sortedBindings.isEmpty())
5895 if (!rhiD->sanityCheckShaderResourceBindings(
this))
5898 rhiD->updateLayoutDesc(
this);
5900 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
5901 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
5902 if (!sortedBindings.isEmpty())
5903 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
5907 boundResourceData.resize(sortedBindings.count());
5909 for (BoundResourceData &bd : boundResourceData)
5910 memset(&bd, 0,
sizeof(BoundResourceData));
5913 rhiD->registerResource(
this,
false);
5919 sortedBindings.clear();
5920 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
5921 if (!flags.testFlag(BindingsAreSorted))
5922 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
5924 for (BoundResourceData &bd : boundResourceData)
5925 memset(&bd, 0,
sizeof(BoundResourceData));
5951 d->tess.compVs[0].destroy();
5952 d->tess.compVs[1].destroy();
5953 d->tess.compVs[2].destroy();
5955 d->tess.compTesc.destroy();
5956 d->tess.vertTese.destroy();
5958 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
5959 d->extraBufMgr.deviceLocalWorkBuffers.clear();
5960 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
5961 d->extraBufMgr.hostVisibleWorkBuffers.clear();
5966 if (!
d->ps && !
d->ds
5967 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
5968 && !
d->tess.tessControlComputeState)
5976 e.graphicsPipeline.pipelineState =
d->ps;
5977 e.graphicsPipeline.depthStencilState =
d->ds;
5978 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
5979 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
5982 d->tess.vertexComputeState = {};
5983 d->tess.tessControlComputeState = nil;
5987 rhiD
->d->releaseQueue.append(e);
5988 rhiD->unregisterResource(
this);
5995 case QRhiVertexInputAttribute::Float4:
5996 return MTLVertexFormatFloat4;
5997 case QRhiVertexInputAttribute::Float3:
5998 return MTLVertexFormatFloat3;
5999 case QRhiVertexInputAttribute::Float2:
6000 return MTLVertexFormatFloat2;
6001 case QRhiVertexInputAttribute::Float:
6002 return MTLVertexFormatFloat;
6003 case QRhiVertexInputAttribute::UNormByte4:
6004 return MTLVertexFormatUChar4Normalized;
6005 case QRhiVertexInputAttribute::UNormByte2:
6006 return MTLVertexFormatUChar2Normalized;
6007 case QRhiVertexInputAttribute::UNormByte:
6008 return MTLVertexFormatUCharNormalized;
6009 case QRhiVertexInputAttribute::UInt4:
6010 return MTLVertexFormatUInt4;
6011 case QRhiVertexInputAttribute::UInt3:
6012 return MTLVertexFormatUInt3;
6013 case QRhiVertexInputAttribute::UInt2:
6014 return MTLVertexFormatUInt2;
6015 case QRhiVertexInputAttribute::UInt:
6016 return MTLVertexFormatUInt;
6017 case QRhiVertexInputAttribute::SInt4:
6018 return MTLVertexFormatInt4;
6019 case QRhiVertexInputAttribute::SInt3:
6020 return MTLVertexFormatInt3;
6021 case QRhiVertexInputAttribute::SInt2:
6022 return MTLVertexFormatInt2;
6023 case QRhiVertexInputAttribute::SInt:
6024 return MTLVertexFormatInt;
6025 case QRhiVertexInputAttribute::Half4:
6026 return MTLVertexFormatHalf4;
6027 case QRhiVertexInputAttribute::Half3:
6028 return MTLVertexFormatHalf3;
6029 case QRhiVertexInputAttribute::Half2:
6030 return MTLVertexFormatHalf2;
6031 case QRhiVertexInputAttribute::Half:
6032 return MTLVertexFormatHalf;
6033 case QRhiVertexInputAttribute::UShort4:
6034 return MTLVertexFormatUShort4;
6035 case QRhiVertexInputAttribute::UShort3:
6036 return MTLVertexFormatUShort3;
6037 case QRhiVertexInputAttribute::UShort2:
6038 return MTLVertexFormatUShort2;
6039 case QRhiVertexInputAttribute::UShort:
6040 return MTLVertexFormatUShort;
6041 case QRhiVertexInputAttribute::SShort4:
6042 return MTLVertexFormatShort4;
6043 case QRhiVertexInputAttribute::SShort3:
6044 return MTLVertexFormatShort3;
6045 case QRhiVertexInputAttribute::SShort2:
6046 return MTLVertexFormatShort2;
6047 case QRhiVertexInputAttribute::SShort:
6048 return MTLVertexFormatShort;
6051 return MTLVertexFormatFloat4;
6058 case QRhiGraphicsPipeline::Zero:
6059 return MTLBlendFactorZero;
6060 case QRhiGraphicsPipeline::One:
6061 return MTLBlendFactorOne;
6062 case QRhiGraphicsPipeline::SrcColor:
6063 return MTLBlendFactorSourceColor;
6064 case QRhiGraphicsPipeline::OneMinusSrcColor:
6065 return MTLBlendFactorOneMinusSourceColor;
6066 case QRhiGraphicsPipeline::DstColor:
6067 return MTLBlendFactorDestinationColor;
6068 case QRhiGraphicsPipeline::OneMinusDstColor:
6069 return MTLBlendFactorOneMinusDestinationColor;
6070 case QRhiGraphicsPipeline::SrcAlpha:
6071 return MTLBlendFactorSourceAlpha;
6072 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
6073 return MTLBlendFactorOneMinusSourceAlpha;
6074 case QRhiGraphicsPipeline::DstAlpha:
6075 return MTLBlendFactorDestinationAlpha;
6076 case QRhiGraphicsPipeline::OneMinusDstAlpha:
6077 return MTLBlendFactorOneMinusDestinationAlpha;
6078 case QRhiGraphicsPipeline::ConstantColor:
6079 return MTLBlendFactorBlendColor;
6080 case QRhiGraphicsPipeline::ConstantAlpha:
6081 return MTLBlendFactorBlendAlpha;
6082 case QRhiGraphicsPipeline::OneMinusConstantColor:
6083 return MTLBlendFactorOneMinusBlendColor;
6084 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
6085 return MTLBlendFactorOneMinusBlendAlpha;
6086 case QRhiGraphicsPipeline::SrcAlphaSaturate:
6087 return MTLBlendFactorSourceAlphaSaturated;
6088 case QRhiGraphicsPipeline::Src1Color:
6089 return MTLBlendFactorSource1Color;
6090 case QRhiGraphicsPipeline::OneMinusSrc1Color:
6091 return MTLBlendFactorOneMinusSource1Color;
6092 case QRhiGraphicsPipeline::Src1Alpha:
6093 return MTLBlendFactorSource1Alpha;
6094 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
6095 return MTLBlendFactorOneMinusSource1Alpha;
6098 return MTLBlendFactorZero;
6105 case QRhiGraphicsPipeline::Add:
6106 return MTLBlendOperationAdd;
6107 case QRhiGraphicsPipeline::Subtract:
6108 return MTLBlendOperationSubtract;
6109 case QRhiGraphicsPipeline::ReverseSubtract:
6110 return MTLBlendOperationReverseSubtract;
6111 case QRhiGraphicsPipeline::Min:
6112 return MTLBlendOperationMin;
6113 case QRhiGraphicsPipeline::Max:
6114 return MTLBlendOperationMax;
6117 return MTLBlendOperationAdd;
6124 if (c.testFlag(QRhiGraphicsPipeline::R))
6125 f |= MTLColorWriteMaskRed;
6126 if (c.testFlag(QRhiGraphicsPipeline::G))
6127 f |= MTLColorWriteMaskGreen;
6128 if (c.testFlag(QRhiGraphicsPipeline::B))
6129 f |= MTLColorWriteMaskBlue;
6130 if (c.testFlag(QRhiGraphicsPipeline::A))
6131 f |= MTLColorWriteMaskAlpha;
6138 case QRhiGraphicsPipeline::Never:
6139 return MTLCompareFunctionNever;
6140 case QRhiGraphicsPipeline::Less:
6141 return MTLCompareFunctionLess;
6142 case QRhiGraphicsPipeline::Equal:
6143 return MTLCompareFunctionEqual;
6144 case QRhiGraphicsPipeline::LessOrEqual:
6145 return MTLCompareFunctionLessEqual;
6146 case QRhiGraphicsPipeline::Greater:
6147 return MTLCompareFunctionGreater;
6148 case QRhiGraphicsPipeline::NotEqual:
6149 return MTLCompareFunctionNotEqual;
6150 case QRhiGraphicsPipeline::GreaterOrEqual:
6151 return MTLCompareFunctionGreaterEqual;
6152 case QRhiGraphicsPipeline::Always:
6153 return MTLCompareFunctionAlways;
6156 return MTLCompareFunctionAlways;
6163 case QRhiGraphicsPipeline::StencilZero:
6164 return MTLStencilOperationZero;
6165 case QRhiGraphicsPipeline::Keep:
6166 return MTLStencilOperationKeep;
6167 case QRhiGraphicsPipeline::Replace:
6168 return MTLStencilOperationReplace;
6169 case QRhiGraphicsPipeline::IncrementAndClamp:
6170 return MTLStencilOperationIncrementClamp;
6171 case QRhiGraphicsPipeline::DecrementAndClamp:
6172 return MTLStencilOperationDecrementClamp;
6173 case QRhiGraphicsPipeline::Invert:
6174 return MTLStencilOperationInvert;
6175 case QRhiGraphicsPipeline::IncrementAndWrap:
6176 return MTLStencilOperationIncrementWrap;
6177 case QRhiGraphicsPipeline::DecrementAndWrap:
6178 return MTLStencilOperationDecrementWrap;
6181 return MTLStencilOperationKeep;
6188 case QRhiGraphicsPipeline::Triangles:
6189 return MTLPrimitiveTypeTriangle;
6190 case QRhiGraphicsPipeline::TriangleStrip:
6191 return MTLPrimitiveTypeTriangleStrip;
6192 case QRhiGraphicsPipeline::Lines:
6193 return MTLPrimitiveTypeLine;
6194 case QRhiGraphicsPipeline::LineStrip:
6195 return MTLPrimitiveTypeLineStrip;
6196 case QRhiGraphicsPipeline::Points:
6197 return MTLPrimitiveTypePoint;
6200 return MTLPrimitiveTypeTriangle;
6207 case QRhiGraphicsPipeline::Triangles:
6208 case QRhiGraphicsPipeline::TriangleStrip:
6209 case QRhiGraphicsPipeline::TriangleFan:
6210 return MTLPrimitiveTopologyClassTriangle;
6211 case QRhiGraphicsPipeline::Lines:
6212 case QRhiGraphicsPipeline::LineStrip:
6213 return MTLPrimitiveTopologyClassLine;
6214 case QRhiGraphicsPipeline::Points:
6215 return MTLPrimitiveTopologyClassPoint;
6218 return MTLPrimitiveTopologyClassTriangle;
6225 case QRhiGraphicsPipeline::None:
6226 return MTLCullModeNone;
6227 case QRhiGraphicsPipeline::Front:
6228 return MTLCullModeFront;
6229 case QRhiGraphicsPipeline::Back:
6230 return MTLCullModeBack;
6233 return MTLCullModeNone;
6240 case QRhiGraphicsPipeline::Fill:
6241 return MTLTriangleFillModeFill;
6242 case QRhiGraphicsPipeline::Line:
6243 return MTLTriangleFillModeLines;
6246 return MTLTriangleFillModeFill;
6253 case QShaderDescription::CwTessellationWindingOrder:
6254 return MTLWindingClockwise;
6255 case QShaderDescription::CcwTessellationWindingOrder:
6256 return MTLWindingCounterClockwise;
6259 return MTLWindingCounterClockwise;
6266 case QShaderDescription::EqualTessellationPartitioning:
6267 return MTLTessellationPartitionModePow2;
6268 case QShaderDescription::FractionalEvenTessellationPartitioning:
6269 return MTLTessellationPartitionModeFractionalEven;
6270 case QShaderDescription::FractionalOddTessellationPartitioning:
6271 return MTLTessellationPartitionModeFractionalOdd;
6274 return MTLTessellationPartitionModePow2;
6280 int v = version.version();
6281 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
6286 auto &pool(argBufPool[frameSlot]);
6287 const quint32 alignedSize = aligned<quint32>(size, alignment);
6288 if (pool.offset + alignedSize > pool.capacity) {
6298 e.stagingBuffer.buffer = pool.buf;
6299 releaseQueue.append(e);
6301 pool.capacity = qMax(pool.capacity * 2, qMax(alignedSize, quint32(16384)));
6302 pool.buf = [dev newBufferWithLength: pool.capacity options: MTLResourceStorageModeShared];
6309 *offset = pool.offset;
6310 pool.offset += alignedSize;
6315 bool preferArgumentBuffers,
6316 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
6318 QVarLengthArray<
int, 8> versions;
6319 versions << 30 << 24 << 23 << 22 << 21 << 20 << 12;
6329 QVarLengthArray<QShader::Variant, 2> variants;
6330 if (preferArgumentBuffers)
6331 variants << QShader::ArgumentBufferShader;
6332 variants << shaderVariant;
6334 const QList<QShaderKey> shaders = shader.availableShaders();
6336 auto findKey = [&shaders, &versions, &variants](QShader::Source source, QShaderKey *result) {
6337 for (
const QShader::Variant &variant : variants) {
6338 for (
const int &version : versions) {
6339 const QShaderKey key = { source, version, variant };
6340 if (shaders.contains(key)) {
6351 if (findKey(QShader::Source::MetalLibShader, &key)) {
6352 QShaderCode mtllib = shader.shader(key);
6353 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
6354 size_t(mtllib.shader().size()),
6355 dispatch_get_global_queue(0, 0),
6356 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
6358 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
6359 dispatch_release(data);
6361 *entryPoint = mtllib.entryPoint();
6365 const QString msg = QString::fromNSString(err.localizedDescription);
6366 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
6370 if (!findKey(QShader::Source::MslShader, &key)) {
6371 qWarning() <<
"No MSL code found in baked shader" << shader;
6375 QShaderCode mslSource = shader.shader(key);
6377 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
6378 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
6379 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
6381 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
6389 const QString msg = QString::fromNSString(err.localizedDescription);
6394 *entryPoint = mslSource.entryPoint();
6401 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
6406 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
6410 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
6411 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
6412 rpDesc.colorAttachments[0].blendingEnabled =
false;
6414 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
6415 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
6417 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
6418 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
6419 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
6420 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
6421 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
6422 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
6423 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
6424 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
6425 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
6426 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
6427 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
6434 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
6435 rpDesc.depthAttachmentPixelFormat = fmt;
6436#if defined(Q_OS_MACOS)
6437 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
6439 if (fmt != MTLPixelFormatDepth32Float)
6441 rpDesc.stencilAttachmentPixelFormat = fmt;
6445 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
6450 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
6452 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
6453 dsDesc.depthWriteEnabled = m_depthWrite;
6454 if (m_stencilTest) {
6455 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
6456 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
6457 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
6458 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
6459 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
6460 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
6461 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
6463 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
6464 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
6465 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
6466 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
6467 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
6468 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
6469 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
6475 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
6476 d->cullMode = toMetalCullMode(m_cullMode);
6477 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
6478 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
6479 d->depthBias =
float(m_depthBias);
6480 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
6490 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
6493 const uint loc = uint(it->location());
6494 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
6495 desc.attributes[loc].offset = NSUInteger(it->offset());
6496 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
6498 int bindingIndex = 0;
6499 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
6500 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
6501 it != itEnd; ++it, ++bindingIndex)
6503 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
6504 desc.layouts[layoutIdx].stepFunction =
6505 it->classification() == QRhiVertexInputBinding::PerInstance
6506 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
6507 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
6508 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
6509 desc.layouts[layoutIdx].stepRate *= viewCount;
6510 desc.layouts[layoutIdx].stride = it->stride();
6521 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
6524 const uint loc = uint(it->location());
6525 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
6526 desc.attributes[loc].offset = NSUInteger(it->offset());
6527 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
6529 int bindingIndex = 0;
6530 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
6531 it != itEnd; ++it, ++bindingIndex)
6533 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
6534 if (desc.indexBufferIndex) {
6535 desc.layouts[layoutIdx].stepFunction =
6536 it->classification() == QRhiVertexInputBinding::PerInstance
6537 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
6539 desc.layouts[layoutIdx].stepFunction =
6540 it->classification() == QRhiVertexInputBinding::PerInstance
6541 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
6543 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
6544 desc.layouts[layoutIdx].stride = it->stride();
6551 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6552 rpDesc.binaryArchives = binArchArray;
6560 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
6561 const QString msg = QString::fromNSString(err.localizedDescription);
6562 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
6569 return !desc.combinedImageSamplers().isEmpty()
6570 || !desc.separateImages().isEmpty()
6571 || !desc.storageImages().isEmpty();
6576 for (
const QShaderKey &k : shader.availableShaders()) {
6577 if (k.sourceVariant() == QShader::ArgumentBufferShader)
6585 const int index = shader->nativeShaderInfo.extraBufferBindings.value(QShaderPrivate::MslArgumentBufferBinding, -1);
6587 shader->argumentBufferIndex = index;
6588 shader->argumentEncoder = [shader->func newArgumentEncoderWithBufferIndex: NSUInteger(index)];
6599 const bool wantArgumentBuffers = m_flags.testFlag(UsesIndirectDraws) && rhiD->caps.indirectCommandBuffers;
6601 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
6602 d->setupVertexInputDescriptor(vertexDesc);
6604 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
6605 rpDesc.vertexDescriptor = vertexDesc;
6613 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6614 const QShader shader = shaderStage.shader();
6615 const bool argumentBufferBuild = wantArgumentBuffers && hasArgumentBufferVariant(shader);
6616 auto cacheIt = rhiD->d->shaderCache.constFind({ shaderStage, argumentBufferBuild });
6617 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
6618 switch (shaderStage.type()) {
6619 case QRhiShaderStage::Vertex:
6622 [d->vs.func retain];
6623 [d->vs.argumentEncoder retain];
6624 rpDesc.vertexFunction = d->vs.func;
6626 case QRhiShaderStage::Fragment:
6629 [d->fs.func retain];
6630 [d->fs.argumentEncoder retain];
6631 rpDesc.fragmentFunction = d->fs.func;
6638 QByteArray entryPoint;
6639 QShaderKey activeKey;
6640 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
6641 argumentBufferBuild,
6642 &error, &entryPoint, &activeKey);
6644 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6647 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
6649 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6653 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6655 for (QMetalShader &s : rhiD->d->shaderCache)
6657 rhiD->d->shaderCache.clear();
6659 switch (shaderStage.type()) {
6660 case QRhiShaderStage::Vertex:
6663 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6664 d->vs.desc = shader.description();
6665 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6666 setupArgumentBufferEncoder(&d->vs);
6667 rhiD->d->shaderCache.insert({ shaderStage, argumentBufferBuild }, d->vs);
6669 [d->vs.func retain];
6670 [d->vs.argumentEncoder retain];
6671 rpDesc.vertexFunction = func;
6673 case QRhiShaderStage::Fragment:
6676 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6677 d->fs.desc = shader.description();
6678 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6679 setupArgumentBufferEncoder(&d->fs);
6680 rhiD->d->shaderCache.insert({ shaderStage, argumentBufferBuild }, d->fs);
6682 [d->fs.func retain];
6683 [d->fs.argumentEncoder retain];
6684 rpDesc.fragmentFunction = func;
6699 const auto icbSafe = [](
const QMetalShader &s) {
6700 return !usesTextures(s.desc) || s.argumentBufferIndex >= 0;
6702 d->icbCapable = wantArgumentBuffers && icbSafe(
d->vs) && icbSafe(
d->fs);
6704 rpDesc.supportIndirectCommandBuffers = YES;
6706 if (m_multiViewCount >= 2)
6707 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
6709 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
6711 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6712 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
6715 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
6718 const QString msg = QString::fromNSString(err.localizedDescription);
6719 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
6723 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
6725 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
6728 d->primitiveType = toMetalPrimitiveType(m_topology);
6736 switch (vertexCompVariant) {
6737 case QShader::NonIndexedVertexAsComputeShader:
6739 case QShader::UInt32IndexedVertexAsComputeShader:
6741 case QShader::UInt16IndexedVertexAsComputeShader:
6751 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
6752 if (varIndex >= 0 && vertexComputeState[varIndex])
6753 return vertexComputeState[varIndex];
6755 id<MTLFunction> func = nil;
6757 func = compVs[varIndex].func;
6760 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
6764 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
6765 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
6767 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6768 cpDesc.computeFunction = func;
6769 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
6770 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
6771 if (indexBufferBinding >= 0) {
6772 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
6773 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
6774 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
6775 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
6776 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
6777 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
6780 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
6782 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6784 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6785 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6788 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6789 options: MTLPipelineOptionNone
6794 const QString msg = QString::fromNSString(err.localizedDescription);
6795 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6797 vertexComputeState[varIndex] = ps;
6805 if (tessControlComputeState)
6806 return tessControlComputeState;
6808 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6809 cpDesc.computeFunction = compTesc.func;
6811 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6813 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6814 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6817 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6818 options: MTLPipelineOptionNone
6823 const QString msg = QString::fromNSString(err.localizedDescription);
6824 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6826 tessControlComputeState = ps;
6834 return (indices >> index) & 0x1;
6837static inline void takeIndex(quint32 index, quint64 &indices)
6839 indices |= 1 << index;
6848 static const int maxVertexAttributes = 31;
6850 for (
int index = 0; index < maxVertexAttributes; ++index) {
6851 if (!indexTaken(index, indices))
6855 Q_UNREACHABLE_RETURN(-1);
6858static inline int aligned(quint32 offset, quint32 alignment)
6860 return ((offset + alignment - 1) / alignment) * alignment;
6868 for (
const int dim : variable.arrayDims)
6871 if (variable.type == QShaderDescription::VariableType::Struct) {
6872 for (
int element = 0; element < elements; ++element) {
6873 for (
const auto &member : variable.structMembers) {
6874 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
6878 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
6879 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
6882 const quint32 alignment = size;
6883 vertexAlignment =
std::max(vertexAlignment, alignment);
6885 for (
int element = 0; element < elements; ++element) {
6887 offset = aligned(offset, alignment);
6894static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
6898 for (
const int dim : variable.arrayDims)
6901 if (variable.type == QShaderDescription::VariableType::Struct) {
6902 for (
int element = 0; element < elements; ++element) {
6903 for (
const auto &member : variable.structMembers) {
6904 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
6908 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
6909 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
6912 const quint32 alignment = size;
6913 vertexAlignment =
std::max(vertexAlignment, alignment);
6915 for (
int element = 0; element < elements; ++element) {
6916 Q_ASSERT(!indexTaken(index, indices));
6919 offset = aligned(offset, alignment);
6921 attributes[index].bufferIndex = binding;
6922 attributes[index].format = toMetalAttributeFormat(format);
6923 attributes[index].offset = offset;
6925 takeIndex(index, indices);
6927 if (indexTaken(index, indices))
6928 index = nextAttributeIndex(indices);
6935static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
6937 if (a.size() == b.size()) {
6939 for (
int i = 0; i < a.size() && match; ++i) {
6940 match &= a[i].type == b[i].type
6941 && a[i].arrayDims == b[i].arrayDims
6942 && matches(a[i].structMembers, b[i].structMembers);
6950static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
6952 return a.location == b.location
6954 && a.perPatch == b.perPatch
6955 && matches(a.structMembers, b.structMembers);
7004 if (pipeline
->d->ps)
7005 return pipeline
->d->ps;
7007 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
7008 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
7011 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
7012 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
7013 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
7014 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
7015 quint32 offsetInTescOutput = 0;
7016 quint32 offsetInTescPatchOutput = 0;
7017 quint32 offsetInTessFactorBuffer = 0;
7018 quint32 tescOutputAlignment = 0;
7019 quint32 tescPatchOutputAlignment = 0;
7020 quint32 tessFactorAlignment = 0;
7021 QSet<
int> usedBuffers;
7024 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
7025 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
7026 tescOutVars[tescOutVar.location] = tescOutVar;
7029 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
7030 for (
const auto &teseInVar : vertTese.desc.inputVariables())
7031 teseInVars[teseInVar.location] = teseInVar;
7034 quint64 indices = 0;
7036 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
7038 int index = tescOutVar.location;
7040 quint32 *offset =
nullptr;
7041 quint32 *alignment =
nullptr;
7043 if (tescOutVar.perPatch) {
7044 binding = tescPatchOutputBufferBinding;
7045 offset = &offsetInTescPatchOutput;
7046 alignment = &tescPatchOutputAlignment;
7048 tescOutVar.arrayDims.removeLast();
7049 binding = tescOutputBufferBinding;
7050 offset = &offsetInTescOutput;
7051 alignment = &tescOutputAlignment;
7054 if (teseInVars.contains(index)) {
7056 if (!matches(teseInVars[index], tescOutVar)) {
7057 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
7058 qWarning() <<
" tesc out:" << tescOutVar;
7059 qWarning() <<
" tese in:" << teseInVars[index];
7062 if (binding != -1) {
7063 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
7064 usedBuffers << binding;
7066 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
7067 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
7071 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
7072 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
7075 teseInVars.remove(tescOutVar.location);
7078 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
7079 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
7082 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
7083 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
7084 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
7087 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
7088 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
7089 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
7091 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
7092 bool tessLevelAdded =
false;
7094 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
7096 QShaderDescription::InOutVariable variable;
7098 quint32 *offset =
nullptr;
7099 quint32 *alignment =
nullptr;
7101 switch (builtin.type) {
7102 case QShaderDescription::BuiltinType::PositionBuiltin:
7103 variable.type = QShaderDescription::VariableType::Vec4;
7104 binding = tescOutputBufferBinding;
7105 offset = &offsetInTescOutput;
7106 alignment = &tescOutputAlignment;
7108 case QShaderDescription::BuiltinType::PointSizeBuiltin:
7109 variable.type = QShaderDescription::VariableType::Float;
7110 binding = tescOutputBufferBinding;
7111 offset = &offsetInTescOutput;
7112 alignment = &tescOutputAlignment;
7114 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
7115 variable.type = QShaderDescription::VariableType::Float;
7116 variable.arrayDims = builtin.arrayDims;
7117 binding = tescOutputBufferBinding;
7118 offset = &offsetInTescOutput;
7119 alignment = &tescOutputAlignment;
7121 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
7122 variable.type = QShaderDescription::VariableType::Half4;
7123 binding = tessFactorBufferBinding;
7124 offset = &offsetInTessFactorBuffer;
7125 tessLevelAdded = trianglesMode;
7126 alignment = &tessFactorAlignment;
7128 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
7129 if (trianglesMode) {
7130 if (!tessLevelAdded) {
7131 variable.type = QShaderDescription::VariableType::Half4;
7132 binding = tessFactorBufferBinding;
7133 offsetInTessFactorBuffer = 0;
7134 offset = &offsetInTessFactorBuffer;
7135 alignment = &tessFactorAlignment;
7136 tessLevelAdded =
true;
7138 teseInBuiltins.remove(builtin.type);
7142 variable.type = QShaderDescription::VariableType::Half2;
7143 binding = tessFactorBufferBinding;
7144 offsetInTessFactorBuffer = 8;
7145 offset = &offsetInTessFactorBuffer;
7146 alignment = &tessFactorAlignment;
7154 if (teseInBuiltins.contains(builtin.type)) {
7155 if (binding != -1) {
7156 int index = nextAttributeIndex(indices);
7157 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
7158 usedBuffers << binding;
7160 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
7161 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
7164 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
7167 teseInBuiltins.remove(builtin.type);
7170 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
7171 switch (builtin.type) {
7172 case QShaderDescription::BuiltinType::PositionBuiltin:
7173 case QShaderDescription::BuiltinType::PointSizeBuiltin:
7174 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
7175 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
7182 if (usedBuffers.contains(tescOutputBufferBinding)) {
7183 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
7184 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
7187 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
7188 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
7189 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
7192 if (usedBuffers.contains(tessFactorBufferBinding)) {
7193 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
7194 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
7197 rpDesc.vertexDescriptor = vertexDesc;
7198 rpDesc.vertexFunction = vertTese.func;
7199 rpDesc.fragmentFunction = pipeline
->d->fs.func;
7205 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
7207 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
7212 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
7214 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7215 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
7218 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
7221 const QString msg = QString::fromNSString(err.localizedDescription);
7222 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
7226 pipeline->d->ps = ps;
7233 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
7236 for (QMetalBuffer *workBuf : *workBuffers) {
7237 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
7238 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
7246 for (QMetalBuffer *workBuf : *workBuffers) {
7247 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
7248 workBuf->setSize(size);
7249 if (workBuf->create()) {
7250 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
7261 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
7264 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
7268 workBuffers->append(buf);
7272 qWarning(
"Failed to acquire work buffer of size %u", size);
7280 QByteArray entryPoint;
7281 QShaderKey activeKey;
7283 const QShaderDescription tescDesc = tesc.description();
7284 const QShaderDescription teseDesc = tese.description();
7285 d->tess.inControlPointCount = uint(m_patchControlPointCount);
7286 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
7287 if (!
d->tess.outControlPointCount)
7288 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
7290 if (!
d->tess.outControlPointCount) {
7291 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
7292 d->tess.enabled =
false;
7293 d->tess.failed =
true;
7297 if (m_multiViewCount >= 2)
7298 qWarning(
"Multiview is not supported with tessellation");
7306 bool variantsPresent[3] = {};
7307 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
7308 for (
const QShaderKey &k : tessVertKeys) {
7309 switch (k.sourceVariant()) {
7310 case QShader::NonIndexedVertexAsComputeShader:
7311 variantsPresent[0] =
true;
7313 case QShader::UInt32IndexedVertexAsComputeShader:
7314 variantsPresent[1] =
true;
7316 case QShader::UInt16IndexedVertexAsComputeShader:
7317 variantsPresent[2] =
true;
7323 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
7324 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
7325 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
7326 "Try passing --msltess to qsb.");
7327 d->tess.enabled =
false;
7328 d->tess.failed =
true;
7333 for (QShader::Variant variant : {
7334 QShader::NonIndexedVertexAsComputeShader,
7335 QShader::UInt32IndexedVertexAsComputeShader,
7336 QShader::UInt16IndexedVertexAsComputeShader })
7338 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant,
false, &error, &entryPoint, &activeKey);
7340 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
7341 d->tess.enabled =
false;
7342 d->tess.failed =
true;
7345 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
7347 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7349 d->tess.enabled =
false;
7350 d->tess.failed =
true;
7353 QMetalShader &compVs(d->tess.compVs[varIndex]);
7356 compVs.desc = tessVert.description();
7357 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
7358 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
7361 if (!d->tess.vsCompPipeline(rhiD, variant)) {
7362 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
7363 d->tess.enabled =
false;
7364 d->tess.failed =
true;
7372 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7373 if (!tessControlLib) {
7374 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
7375 d->tess.enabled =
false;
7376 d->tess.failed =
true;
7379 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
7380 if (!tessControlFunc) {
7381 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7382 [tessControlLib release];
7383 d->tess.enabled =
false;
7384 d->tess.failed =
true;
7387 d->tess.compTesc.lib = tessControlLib;
7388 d->tess.compTesc.func = tessControlFunc;
7389 d->tess.compTesc.desc = tesc.description();
7390 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
7391 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
7392 if (!
d->tess.tescCompPipeline(rhiD)) {
7393 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
7394 d->tess.enabled =
false;
7395 d->tess.failed =
true;
7400 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7402 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
7403 d->tess.enabled =
false;
7404 d->tess.failed =
true;
7407 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
7408 if (!tessEvalFunc) {
7409 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7410 [tessEvalLib release];
7411 d->tess.enabled =
false;
7412 d->tess.failed =
true;
7415 d->tess.vertTese.lib = tessEvalLib;
7416 d->tess.vertTese.func = tessEvalFunc;
7417 d->tess.vertTese.desc = tese.description();
7418 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
7419 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
7421 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7423 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
7424 d->tess.enabled =
false;
7425 d->tess.failed =
true;
7428 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
7430 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7432 d->tess.enabled =
false;
7433 d->tess.failed =
true;
7436 d->fs.lib = fragLib;
7437 d->fs.func = fragFunc;
7438 d->fs.desc = tessFrag.description();
7439 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
7440 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
7442 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
7443 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
7444 d->tess.enabled =
false;
7445 d->tess.failed =
true;
7449 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
7451 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
7465 rhiD->pipelineCreationStart();
7466 if (!rhiD->sanityCheckGraphicsPipeline(
this))
7474 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7475 switch (shaderStage.type()) {
7476 case QRhiShaderStage::Vertex:
7477 tessVert = shaderStage.shader();
7479 case QRhiShaderStage::TessellationControl:
7480 tesc = shaderStage.shader();
7482 case QRhiShaderStage::TessellationEvaluation:
7483 tese = shaderStage.shader();
7485 case QRhiShaderStage::Fragment:
7486 tessFrag = shaderStage.shader();
7492 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
7493 d->tess.failed =
false;
7495 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
7501 QVarLengthArray<QMetalShader *, 6> shaders;
7502 if (
d->tess.enabled) {
7503 shaders.append(&
d->tess.compVs[0]);
7504 shaders.append(&
d->tess.compVs[1]);
7505 shaders.append(&
d->tess.compVs[2]);
7506 shaders.append(&
d->tess.compTesc);
7507 shaders.append(&
d->tess.vertTese);
7509 shaders.append(&
d->vs);
7511 shaders.append(&
d->fs);
7513 for (QMetalShader *shader : shaders) {
7514 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
7515 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
7516 shader->nativeResourceBindingMap[binding] = {binding, -1};
7517 int maxNativeBinding = 0;
7518 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
7519 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
7523 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
7528 if (!d->bufferSizeBuffer)
7529 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
7535 rhiD->pipelineCreationEnd();
7538 rhiD->registerResource(
this);
7567 e.computePipeline.pipelineState =
d->ps;
7572 rhiD
->d->releaseQueue.append(e);
7573 rhiD->unregisterResource(
this);
7580 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
7581 cpDesc.binaryArchives = binArchArray;
7589 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
7590 const QString msg = QString::fromNSString(err.localizedDescription);
7591 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
7602 rhiD->pipelineCreationStart();
7604 auto cacheIt = rhiD
->d->shaderCache.constFind({ m_shaderStage,
false });
7605 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
7608 const QShader shader = m_shaderStage.shader();
7610 QByteArray entryPoint;
7611 QShaderKey activeKey;
7612 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
false,
7613 &error, &entryPoint, &activeKey);
7615 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
7618 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
7620 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7626 d->cs.localSize = shader.description().computeShaderLocalSize();
7627 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
7628 d->cs.desc = shader.description();
7629 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
7636 setupArgumentBufferEncoder(&
d->cs);
7639 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
7640 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
7641 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
7644 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
7645 for (QMetalShader &s : rhiD->d->shaderCache)
7647 rhiD
->d->shaderCache.clear();
7649 rhiD
->d->shaderCache.insert({ m_shaderStage,
false },
d->cs);
7653 [d->cs.func retain];
7654 [d->cs.argumentEncoder retain];
7656 if (
d->cs.argumentBufferIndex >= 0 && !rhiD->caps.indirectCommandBuffers) {
7660 qWarning(
"The ArgumentBufferShader variant of a compute shader cannot be used on this device");
7664 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
7666 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
7667 cpDesc.computeFunction =
d->cs.func;
7669 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
7671 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7672 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
7675 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
7676 options: MTLPipelineOptionNone
7681 const QString msg = QString::fromNSString(err.localizedDescription);
7682 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
7687 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
7689 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
7690 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
7694 if (!d->bufferSizeBuffer)
7695 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
7701 rhiD->pipelineCreationEnd();
7704 rhiD->registerResource(
this);
7728 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
7729 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
7730 return &nativeHandlesStruct;
7736 d->currentRenderPassEncoder = nil;
7737 d->currentComputePassEncoder = nil;
7738 d->tessellationComputeEncoder = nil;
7739 d->currentPassRpDesc = nil;
7746 currentTarget =
nullptr;
7754 currentPipelineGeneration = 0;
7757 currentSrbGeneration = 0;
7760 currentIndexOffset = 0;
7761 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
7766 currentDepthBiasValues = { 0.0f, 0.0f };
7768 currentScissor = {};
7769 currentViewport = {};
7771 currentBlendConstants = {};
7773 currentStencilRef = 0;
7775 d->currentShaderResourceBindingState = {};
7776 d->currentDepthStencilState = nil;
7778 d->currentVertexInputsBuffers.clear();
7779 d->currentVertexInputOffsets.clear();
7789 d->sem[i] =
nullptr;
7790 d->msaaTex[i] = nil;
7810 dispatch_release(
d->sem[i]);
7811 d->sem[i] =
nullptr;
7816 [d->msaaTex[i] release];
7817 d->msaaTex[i] = nil;
7823 [d->curDrawable release];
7824 d->curDrawable = nil;
7828 rhiD->swapchains.remove(
this);
7829 rhiD->unregisterResource(
this);
7849 CALayer *layer =
nullptr;
7851 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
7852 layer = cocoaWindow->contentLayer();
7854 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
7857 return static_cast<CAMetalLayer *>(layer);
7866 d.reserved[0] = layerForWindow(window);
7873 CAMetalLayer *layer =
d->layer;
7875 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
7878 int height = (
int)layer.bounds.size.height;
7879 int width = (
int)layer.bounds.size.width;
7880 width *= layer.contentsScale;
7881 height *= layer.contentsScale;
7882 return QSize(width, height);
7887 if (f == HDRExtendedSrgbLinear) {
7889 }
else if (f == HDR10) {
7891 }
else if (f == HDRExtendedDisplayP3Linear) {
7905 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
7911 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
7912 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
7914 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
7917 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
7921 rhiD->registerResource(rpD,
false);
7928 samples = rhiD->effectiveSampleCount(m_sampleCount);
7930 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
7931 d->colorFormat = MTLPixelFormatRGBA16Float;
7932 d->rhiColorFormat = QRhiTexture::RGBA16F;
7935 if (m_format == HDR10) {
7936 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
7937 d->rhiColorFormat = QRhiTexture::RGB10A2;
7940 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
7941 d->rhiColorFormat = QRhiTexture::BGRA8;
7950 dispatch_semaphore_t sem =
d->sem[slot];
7951 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
7952 dispatch_semaphore_signal(sem);
7959 const bool needsRegistration = !window || window != m_window;
7961 if (window && window != m_window)
7966 if (needsRegistration || !rhiD->swapchains.contains(
this))
7967 rhiD->swapchains.insert(
this);
7971 if (window->surfaceType() != QSurface::MetalSurface) {
7972 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
7976 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
7980 if (
d->colorFormat !=
d->layer.pixelFormat)
7981 d->layer.pixelFormat =
d->colorFormat;
7983 if (m_format == HDRExtendedSrgbLinear) {
7984 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
7985 d->layer.wantsExtendedDynamicRangeContent = YES;
7986 }
else if (m_format == HDR10) {
7987 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
7988 d->layer.wantsExtendedDynamicRangeContent = YES;
7989 }
else if (m_format == HDRExtendedDisplayP3Linear) {
7990 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
7991 d->layer.wantsExtendedDynamicRangeContent = YES;
7994 if (m_flags.testFlag(UsedAsTransferSource))
7995 d->layer.framebufferOnly = NO;
7998 if (m_flags.testFlag(NoVSync))
7999 d->layer.displaySyncEnabled = NO;
8002 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
8003 d->layer.opaque = NO;
8004 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
8009 d->layer.opaque = NO;
8011 d->layer.opaque = YES;
8017 int width = (
int)
d->layer.bounds.size.width;
8018 int height = (
int)
d->layer.bounds.size.height;
8019 CGSize layerSize = CGSizeMake(width, height);
8020 const float scaleFactor =
d->layer.contentsScale;
8021 layerSize.width *= scaleFactor;
8022 layerSize.height *= scaleFactor;
8023 d->layer.drawableSize = layerSize;
8025 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
8026 pixelSize = m_currentPixelSize;
8028 [d->layer setDevice: rhiD->d->dev];
8030 [d->curDrawable release];
8031 d->curDrawable = nil;
8042 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
8043 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
8044 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
8045 m_depthStencil->sampleCount(), m_sampleCount);
8047 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
8048 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
8049 m_depthStencil->setPixelSize(pixelSize);
8050 if (!m_depthStencil->create())
8051 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
8052 pixelSize.width(), pixelSize.height());
8054 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
8055 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
8056 pixelSize.width(), pixelSize.height());
8060 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
8061 rtWrapper.d->pixelSize = pixelSize;
8067 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
8068 pixelSize.width(), pixelSize.height(), scaleFactor);
8071 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
8072 desc.textureType = MTLTextureType2DMultisample;
8073 desc.pixelFormat =
d->colorFormat;
8074 desc.width = NSUInteger(pixelSize.width());
8075 desc.height = NSUInteger(pixelSize.height());
8076 desc.sampleCount = NSUInteger(
samples);
8077 desc.resourceOptions = MTLResourceStorageModePrivate;
8078 desc.storageMode = MTLStorageModePrivate;
8079 desc.usage = MTLTextureUsageRenderTarget;
8081 if (
d->msaaTex[i]) {
8085 e.renderbuffer.texture =
d->msaaTex[i];
8086 rhiD
->d->releaseQueue.append(e);
8088 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
8093 rhiD->registerResource(
this);
8109#if defined(Q_OS_MACOS)
8110 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
8111 NSScreen *screen = view.window.screen;
8112 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
8113 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
8114#elif defined(Q_OS_IOS)
8115 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
8116 UIScreen *screen = view.window.windowScene.screen;
8117 info.limits.colorComponentValue.maxColorComponentValue =
8118 view.window.windowScene.screen.currentEDRHeadroom;
8119 info.limits.colorComponentValue.maxPotentialColorComponentValue =
8120 screen.potentialEDRHeadroom;
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Int aligned(Int v, Int byteAlign)
\variable QRhiVulkanQueueSubmitParams::waitSemaphoreCount
id< MTLTexture > viewForLevel(int level)
id< MTLTexture > perLevelViews[QRhi::MAX_MIP_LEVELS]
id< MTLBuffer > stagingBuf[QMTL_FRAMES_IN_FLIGHT]
QMetalTextureData(QMetalTexture *t)
~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< MTLDepthStencilState > depthStencilState
std::array< id< MTLComputePipelineState >, 3 > tessVertexComputeState
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
QRhiReadbackResult * result
QRhiTexture::Format format
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
float maxPotentialColorComponentValue
LuminanceBehavior luminanceBehavior
float maxColorComponentValue
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h