6#include <QGuiApplication>
10#include <QTemporaryFile>
13#include <QOperatingSystemVersion>
15#include <QtCore/private/qcore_mac_p.h>
16#include <QtGui/private/qmetallayer_p.h>
17#include <QtGui/qpa/qplatformwindow_p.h>
20#include <AppKit/AppKit.h>
22#include <UIKit/UIKit.h>
25#include <QuartzCore/CATransaction.h>
27#include <Metal/Metal.h>
32
33
34
35
36
37
38
39
40
43#error ARC not supported
52#define QRHI_METAL_DISABLE_BINARY_ARCHIVE
57#define QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
61
62
63
64
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
100
101
102
103
104
105
106
107
110
111
112
113
116
117
118
119
120
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
144
145
148
149
162 nativeResourceBindingMap.clear();
181 const QColor &colorClearValue,
182 const QRhiDepthStencilClearValue &depthStencilClearValue,
184 QRhiShadingRateMap *shadingRateMap);
186 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
215 id<MTLTexture> texture;
434 return vertexOrIndexCount * instanceCount *
sizeof(
float) * 60;
443 return patchCount *
sizeof(
float) * 128;
491 if (importDevice->dev) {
492 d->dev = (id<MTLDevice>) importDevice->dev;
494 if (importedCmdQueue)
495 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
497 qWarning(
"No MTLDevice given, cannot import");
511 return (v + byteAlign - 1) & ~(byteAlign - 1);
517 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
531 return [cmdQueue commandBufferWithUnretainedReferences];
533 return [cmdQueue commandBuffer];
544 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor
new];
545 binArchDesc.url = sourceFileUrl;
547 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
548 [binArchDesc release];
550 const QString msg = QString::fromNSString(err.localizedDescription);
551 qWarning(
"newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
564 d->dev = MTLCreateSystemDefaultDevice();
567 qWarning(
"No MTLDevice");
571 const QString deviceName = QString::fromNSString([d->dev name]);
572 qCDebug(QRHI_LOG_INFO,
"Metal device: %s", qPrintable(deviceName));
573 driverInfoStruct.deviceName = deviceName.toUtf8();
580 const MTLDeviceLocation deviceLocation = [d->dev location];
581 switch (deviceLocation) {
582 case MTLDeviceLocationBuiltIn:
583 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
585 case MTLDeviceLocationSlot:
586 driverInfoStruct.deviceType = QRhiDriverInfo::DiscreteDevice;
588 case MTLDeviceLocationExternal:
589 driverInfoStruct.deviceType = QRhiDriverInfo::ExternalDevice;
595 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
598 const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
599 osMajor = ver.majorVersion();
600 osMinor = ver.minorVersion();
602 if (importedCmdQueue)
603 [d->cmdQueue retain];
605 d->cmdQueue = [d->dev newCommandQueue];
607 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
611 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
612 const QString label = QString::asprintf(
"Qt capture scope for QRhi %p",
this);
613 d->captureScope.label = label.toNSString();
615#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
616 caps.maxTextureSize = 16384;
617 caps.baseVertexAndInstance =
true;
618 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
619 caps.maxThreadGroupSize = 1024;
620 caps.multiView =
true;
621#elif defined(Q_OS_TVOS)
622 if ([d->dev supportsFamily:MTLGPUFamilyApple3])
623 caps.maxTextureSize = 16384;
625 caps.maxTextureSize = 8192;
626 caps.baseVertexAndInstance =
false;
627 caps.isAppleGPU =
true;
628#elif defined(Q_OS_IOS)
629 if ([d->dev supportsFamily:MTLGPUFamilyApple3]) {
630 caps.maxTextureSize = 16384;
631 caps.baseVertexAndInstance =
true;
632 }
else if ([d->dev supportsFamily:MTLGPUFamilyApple2]) {
633 caps.maxTextureSize = 8192;
634 caps.baseVertexAndInstance =
false;
636 caps.maxTextureSize = 4096;
637 caps.baseVertexAndInstance =
false;
639 caps.isAppleGPU =
true;
640 if ([d->dev supportsFamily:MTLGPUFamilyApple4])
641 caps.maxThreadGroupSize = 1024;
642 if ([d->dev supportsFamily:MTLGPUFamilyApple5])
643 caps.multiView =
true;
646 caps.supportedSampleCounts = { 1 };
647 for (
int sampleCount : { 2, 4, 8 }) {
648 if ([d->dev supportsTextureSampleCount: sampleCount])
649 caps.supportedSampleCounts.append(sampleCount);
652 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
653 if (caps.shadingRateMap && caps.multiView)
654 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
656 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
657 d->setupBinaryArchive();
659 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
660 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
670 for (QMetalShader &s : d->shaderCache)
672 d->shaderCache.clear();
674 [d->captureScope release];
675 d->captureScope = nil;
677 [d->binArch release];
680 [d->cmdQueue release];
681 if (!importedCmdQueue)
691 return caps.supportedSampleCounts;
696 Q_UNUSED(sampleCount);
697 return { QSize(1, 1) };
702 return new QMetalSwapChain(
this);
705QRhiBuffer *
QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
707 return new QMetalBuffer(
this, type, usage, size);
734 if (m.isIdentity()) {
736 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
737 0.0f, 1.0f, 0.0f, 0.0f,
738 0.0f, 0.0f, 0.5f, 0.5f,
739 0.0f, 0.0f, 0.0f, 1.0f);
748 bool supportsFamilyMac2 =
false;
749 bool supportsFamilyApple3 =
false;
752 supportsFamilyMac2 =
true;
754 supportsFamilyApple3 =
true;
756 supportsFamilyApple3 =
true;
760 if (format == QRhiTexture::BC5)
763 if (!supportsFamilyApple3) {
764 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
766 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
770 if (!supportsFamilyMac2)
771 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
780 case QRhi::MultisampleTexture:
782 case QRhi::MultisampleRenderBuffer:
784 case QRhi::DebugMarkers:
786 case QRhi::Timestamps:
788 case QRhi::Instancing:
790 case QRhi::CustomInstanceStepRate:
792 case QRhi::PrimitiveRestart:
794 case QRhi::NonDynamicUniformBuffers:
796 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
798 case QRhi::NPOTTextureRepeat:
800 case QRhi::RedOrAlpha8IsRed:
802 case QRhi::ElementIndexUint:
806 case QRhi::WideLines:
808 case QRhi::VertexShaderPointSize:
810 case QRhi::BaseVertex:
811 return caps.baseVertexAndInstance;
812 case QRhi::BaseInstance:
813 return caps.baseVertexAndInstance;
814 case QRhi::TriangleFanTopology:
816 case QRhi::ReadBackNonUniformBuffer:
818 case QRhi::ReadBackNonBaseMipLevel:
820 case QRhi::TexelFetch:
822 case QRhi::RenderToNonBaseMipLevel:
824 case QRhi::IntAttributes:
826 case QRhi::ScreenSpaceDerivatives:
828 case QRhi::ReadBackAnyTextureFormat:
830 case QRhi::PipelineCacheDataLoadSave:
832 case QRhi::ImageDataStride:
834 case QRhi::RenderBufferImport:
836 case QRhi::ThreeDimensionalTextures:
838 case QRhi::RenderTo3DTextureSlice:
840 case QRhi::TextureArrays:
842 case QRhi::Tessellation:
844 case QRhi::GeometryShader:
846 case QRhi::TextureArrayRange:
848 case QRhi::NonFillPolygonMode:
850 case QRhi::OneDimensionalTextures:
852 case QRhi::OneDimensionalTextureMipmaps:
854 case QRhi::HalfAttributes:
856 case QRhi::RenderToOneDimensionalTexture:
858 case QRhi::ThreeDimensionalTextureMipmaps:
860 case QRhi::MultiView:
861 return caps.multiView;
862 case QRhi::TextureViewFormat:
864 case QRhi::ResolveDepthStencil:
866 case QRhi::VariableRateShading:
868 case QRhi::VariableRateShadingMap:
869 return caps.shadingRateMap;
870 case QRhi::VariableRateShadingMapWithTexture:
872 case QRhi::PerRenderTargetBlending:
873 case QRhi::SampleVariables:
875 case QRhi::InstanceIndexIncludesBaseInstance:
886 case QRhi::TextureSizeMin:
888 case QRhi::TextureSizeMax:
889 return caps.maxTextureSize;
890 case QRhi::MaxColorAttachments:
892 case QRhi::FramesInFlight:
894 case QRhi::MaxAsyncReadbackFrames:
896 case QRhi::MaxThreadGroupsPerDimension:
898 case QRhi::MaxThreadsPerThreadGroup:
900 case QRhi::MaxThreadGroupX:
902 case QRhi::MaxThreadGroupY:
904 case QRhi::MaxThreadGroupZ:
905 return caps.maxThreadGroupSize;
906 case QRhi::TextureArraySizeMax:
908 case QRhi::MaxUniformBufferRange:
910 case QRhi::MaxVertexInputs:
912 case QRhi::MaxVertexOutputs:
914 case QRhi::ShadingRateImageTileSize:
924 return &nativeHandlesStruct;
929 return driverInfoStruct;
935 result.totalPipelineCreationTime = totalPipelineCreationTime();
945void QRhiMetal::setQueueSubmitParams(QRhiNativeHandles *)
952 for (QMetalShader &s : d->shaderCache)
955 d->shaderCache.clear();
977 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
982 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
987 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
988 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
990 if (![d->binArch serializeToURL: url error: &err]) {
991 const QString msg = QString::fromNSString(err.localizedDescription);
993 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
998 if (!f.open(QIODevice::ReadOnly)) {
999 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1002 const QByteArray blob = f.readAll();
1006 const quint32 dataSize = quint32(blob.size());
1008 data.resize(headerSize + dataSize);
1011 header.rhiId = pipelineCacheRhiId();
1012 header.arch = quint32(
sizeof(
void*));
1013 header.dataSize = quint32(dataSize);
1014 header.osMajor = osMajor;
1015 header.osMinor = osMinor;
1016 const size_t driverStrLen = qMin(
sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1018 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1019 header.driver[driverStrLen] =
'\0';
1021 memcpy(data.data(), &header, headerSize);
1022 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1032 if (data.size() < qsizetype(headerSize)) {
1033 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1037 const size_t dataOffset = headerSize;
1039 memcpy(&header, data.constData(), headerSize);
1041 const quint32 rhiId = pipelineCacheRhiId();
1042 if (header.rhiId != rhiId) {
1043 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1044 rhiId, header.rhiId);
1048 const quint32 arch = quint32(
sizeof(
void*));
1049 if (header.arch != arch) {
1050 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1055 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1056 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1057 osMajor, osMinor, header.osMajor, header.osMinor);
1061 const size_t driverStrLen = qMin(
sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1062 if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1063 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1067 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1068 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1072 const char *p = data.constData() + dataOffset;
1076 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1079 tmp.write(p, header.dataSize);
1082 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1083 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1084 if (d->setupBinaryArchive(url))
1085 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1088QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1089 int sampleCount, QRhiRenderBuffer::Flags flags,
1090 QRhiTexture::Format backingFormatHint)
1092 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1096 const QSize &pixelSize,
int depth,
int arraySize,
1097 int sampleCount, QRhiTexture::Flags flags)
1099 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1103 QRhiSampler::Filter mipmapMode,
1104 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1106 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1111 return new QMetalShadingRateMap(
this);
1115 QRhiTextureRenderTarget::Flags flags)
1122 return new QMetalGraphicsPipeline(
this);
1127 return new QMetalComputePipeline(
this);
1132 return new QMetalShaderResourceBindings(
this);
1143 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1146 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1147 if (!map || map->isEmpty())
1150 auto it = map->constFind(binding);
1151 if (it != map->cend())
1162 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1163 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1166 case QMetalShaderResourceBindingsData::VERTEX:
1167 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1168 offsets: offsetBatch.resources.constData()
1169 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1171 case QMetalShaderResourceBindingsData::FRAGMENT:
1172 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1173 offsets: offsetBatch.resources.constData()
1174 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1176 case QMetalShaderResourceBindingsData::COMPUTE:
1177 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1178 offsets: offsetBatch.resources.constData()
1179 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1193 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1196 case QMetalShaderResourceBindingsData::VERTEX:
1197 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1198 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1200 case QMetalShaderResourceBindingsData::FRAGMENT:
1201 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1202 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1204 case QMetalShaderResourceBindingsData::COMPUTE:
1205 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1206 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1220 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1222 switch (encoderStage) {
1223 case QMetalShaderResourceBindingsData::VERTEX:
1224 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1225 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1227 case QMetalShaderResourceBindingsData::FRAGMENT:
1228 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1229 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1231 case QMetalShaderResourceBindingsData::COMPUTE:
1232 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1233 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1255 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1256 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1257 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1258 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1261 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1262 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1263 bindStageTextures(cbD, encoderStage, batch);
1266 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1267 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1268 bindStageSamplers(cbD, encoderStage, batch);
1275 case QMetalShaderResourceBindingsData::VERTEX:
1276 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1277 case QMetalShaderResourceBindingsData::TESSCTRL:
1278 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1279 case QMetalShaderResourceBindingsData::TESSEVAL:
1280 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1281 case QMetalShaderResourceBindingsData::FRAGMENT:
1282 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1283 case QMetalShaderResourceBindingsData::COMPUTE:
1284 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1287 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1292 int dynamicOffsetCount,
1293 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1294 bool offsetOnlyChange,
1295 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES])
1299 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1300 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1302 case QRhiShaderResourceBinding::UniformBuffer:
1304 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1305 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1306 quint32 offset = b->u.ubuf.offset;
1307 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1308 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1309 if (dynOfs.first == b->binding) {
1310 offset = dynOfs.second;
1315 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1316 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1317 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1318 if (nativeBinding >= 0)
1319 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1324 case QRhiShaderResourceBinding::SampledTexture:
1325 case QRhiShaderResourceBinding::Texture:
1326 case QRhiShaderResourceBinding::Sampler:
1328 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1329 for (
int elem = 0; elem < data->count; ++elem) {
1330 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1331 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1333 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1334 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1339 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1340 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1341 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1342 if (textureBinding >= 0 && texD)
1343 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1344 if (samplerBinding >= 0)
1345 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1351 case QRhiShaderResourceBinding::ImageLoad:
1352 case QRhiShaderResourceBinding::ImageStore:
1353 case QRhiShaderResourceBinding::ImageLoadStore:
1355 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1356 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1358 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1359 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1360 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1361 if (nativeBinding >= 0)
1362 bindingData.res[stage].textures.append({ nativeBinding, t });
1367 case QRhiShaderResourceBinding::BufferLoad:
1368 case QRhiShaderResourceBinding::BufferStore:
1369 case QRhiShaderResourceBinding::BufferLoadStore:
1371 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1372 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1373 quint32 offset = b->u.sbuf.offset;
1374 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1375 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1376 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1377 if (nativeBinding >= 0)
1378 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1401 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Buffer &a,
const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1402 return a.nativeBinding < b.nativeBinding;
1405 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1406 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1407 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1410 bindingData.res[stage].bufferBatches.finish();
1411 bindingData.res[stage].bufferOffsetBatches.finish();
1413 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1414 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1415 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1417 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1418 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1419 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1420 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1424 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1427 if (offsetOnlyChange)
1430 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](
const QMetalShaderResourceBindingsData::Stage::Texture &a,
const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1431 return a.nativeBinding < b.nativeBinding;
1434 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Sampler &a,
const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1435 return a.nativeBinding < b.nativeBinding;
1438 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1439 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1441 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1442 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1444 bindingData.res[stage].textureBatches.finish();
1445 bindingData.res[stage].samplerBatches.finish();
1447 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1448 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1450 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1451 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1455 bindStageTextures(cbD, stage, batch);
1458 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1459 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1461 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1462 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1466 bindStageSamplers(cbD, stage, batch);
1470 cbD
->d->currentShaderResourceBindingState = bindingData;
1475 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1477 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1478 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1479 cbD
->d->currentDepthStencilState =
d->ds;
1482 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1486 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1490 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1494 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1497 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1500 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1501 slopeScale: d->slopeScaledDepthBias
1518 cbD->currentPipelineGeneration = psD->generation;
1520 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1525 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1526 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1527 workBuf->lastActiveFrameSlot = -1;
1529 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1530 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1531 workBuf->lastActiveFrameSlot = -1;
1534 psD->lastActiveFrameSlot = currentFrameSlot;
1538 int dynamicOffsetCount,
1539 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1548 srb = gfxPsD->m_shaderResourceBindings;
1550 srb = compPsD->m_shaderResourceBindings;
1554 bool hasSlottedResourceInSrb =
false;
1555 bool hasDynamicOffsetInSrb =
false;
1556 bool resNeedsRebind =
false;
1558 bool pipelineChanged =
false;
1571 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1574 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1575 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1578 case QRhiShaderResourceBinding::UniformBuffer:
1581 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1584 hasSlottedResourceInSrb =
true;
1585 if (b->u.ubuf.hasDynamicOffset)
1586 hasDynamicOffsetInSrb =
true;
1587 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1588 resNeedsRebind =
true;
1589 bd.ubuf.id = bufD->m_id;
1590 bd.ubuf.generation = bufD->generation;
1592 bufD->lastActiveFrameSlot = currentFrameSlot;
1595 case QRhiShaderResourceBinding::SampledTexture:
1596 case QRhiShaderResourceBinding::Texture:
1597 case QRhiShaderResourceBinding::Sampler:
1599 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1600 if (bd.stex.count != data->count) {
1601 bd.stex.count = data->count;
1602 resNeedsRebind =
true;
1604 for (
int elem = 0; elem < data->count; ++elem) {
1607 Q_ASSERT(texD || samplerD);
1608 const quint64 texId = texD ? texD->m_id : 0;
1609 const uint texGen = texD ? texD->generation : 0;
1610 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1611 const uint samplerGen = samplerD ? samplerD->generation : 0;
1612 if (texGen != bd.stex.d[elem].texGeneration
1613 || texId != bd.stex.d[elem].texId
1614 || samplerGen != bd.stex.d[elem].samplerGeneration
1615 || samplerId != bd.stex.d[elem].samplerId)
1617 resNeedsRebind =
true;
1618 bd.stex.d[elem].texId = texId;
1619 bd.stex.d[elem].texGeneration = texGen;
1620 bd.stex.d[elem].samplerId = samplerId;
1621 bd.stex.d[elem].samplerGeneration = samplerGen;
1624 texD->lastActiveFrameSlot = currentFrameSlot;
1626 samplerD->lastActiveFrameSlot = currentFrameSlot;
1630 case QRhiShaderResourceBinding::ImageLoad:
1631 case QRhiShaderResourceBinding::ImageStore:
1632 case QRhiShaderResourceBinding::ImageLoadStore:
1635 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1636 resNeedsRebind =
true;
1637 bd.simage.id = texD->m_id;
1638 bd.simage.generation = texD->generation;
1640 texD->lastActiveFrameSlot = currentFrameSlot;
1643 case QRhiShaderResourceBinding::BufferLoad:
1644 case QRhiShaderResourceBinding::BufferStore:
1645 case QRhiShaderResourceBinding::BufferLoadStore:
1648 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1650 if (needsBufferSizeBuffer) {
1651 for (
int i = 0; i < 6; ++i) {
1652 const QRhiShaderResourceBinding::StageFlag stage =
1653 QRhiShaderResourceBinding::StageFlag(1 << i);
1654 if (b->stage.testFlag(stage)) {
1655 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1661 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1662 resNeedsRebind =
true;
1663 bd.sbuf.id = bufD->m_id;
1664 bd.sbuf.generation = bufD->generation;
1666 bufD->lastActiveFrameSlot = currentFrameSlot;
1675 if (needsBufferSizeBuffer) {
1677 QVarLengthArray<QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1681 Q_ASSERT(compPsD->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1682 shaders.append(qMakePair(&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage));
1685 if (gfxPsD
->d->tess.enabled) {
1695 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
1696 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
1697 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1698 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1699 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1700 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1701 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1702 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1703 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1704 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1705 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1706 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1708 if (gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1709 shaders.append(qMakePair(&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage));
1711 if (gfxPsD->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1712 shaders.append(qMakePair(&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage));
1714 if (gfxPsD->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1715 shaders.append(qMakePair(&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage));
1718 if (gfxPsD->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1719 shaders.append(qMakePair(&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage));
1721 if (gfxPsD->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1722 shaders.append(qMakePair(&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage));
1726 for (
const QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag> &shader : shaders) {
1728 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1731 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1733 int maxNativeBinding = 0;
1734 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1735 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1737 const int size = (maxNativeBinding + 1) *
sizeof(
int);
1739 Q_ASSERT(offset + size <= bufD->size());
1740 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1742 QMetalShaderResourceBindings::BoundResourceData bd;
1743 bd.sbuf.id = bufD->m_id;
1744 bd.sbuf.generation = bufD->generation;
1745 srbD->boundResourceData.append(bd);
1749 QVarLengthArray<
int, 8> bufferSizeBufferData;
1750 Q_ASSERT(storageBufferSizes.contains(shader.second));
1751 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
1752 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1753 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1759 if (bufferSizeBufferData.size() <= index)
1760 bufferSizeBufferData.resize(index + 1);
1762 Q_ASSERT(sizes.contains(block.binding));
1763 bufferSizeBufferData[index] = sizes[block.binding];
1766 QRhiBufferData data;
1767 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
1768 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
1769 Q_ASSERT(offset + size <= bufD->size());
1770 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1773 offset += ((size + 31) / 32) * 32;
1777 bufD->lastActiveFrameSlot = currentFrameSlot;
1781 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1783 resNeedsRebind =
true;
1786 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1789 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
1790 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
1794 if (gfxPsD
->d->tess.enabled) {
1797 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1798 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1811 cbD->currentSrbGeneration = srbD->generation;
1814 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
1815 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1820 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
1821 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1826 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1827 QRhiBatchedBindings<NSUInteger> offsets;
1828 for (
int i = 0; i < bindingCount; ++i) {
1831 bufD->lastActiveFrameSlot = currentFrameSlot;
1832 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1833 buffers.feed(startBinding + i, mtlbuf);
1834 offsets.feed(startBinding + i, bindings[i].second);
1849 || buffers != cbD
->d->currentVertexInputsBuffers
1850 || offsets != cbD
->d->currentVertexInputOffsets)
1853 cbD
->d->currentVertexInputsBuffers = buffers;
1854 cbD
->d->currentVertexInputOffsets = offsets;
1856 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1857 const auto &bufferBatch(buffers.batches[i]);
1858 const auto &offsetBatch(offsets.batches[i]);
1859 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1860 bufferBatch.resources.constData()
1861 offsets: offsetBatch.resources.constData()
1862 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1869 ibufD->lastActiveFrameSlot = currentFrameSlot;
1871 cbD->currentIndexOffset = indexOffset;
1872 cbD->currentIndexFormat = indexFormat;
1882 QSize outputSize = cbD->currentTarget->pixelSize();
1888 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
1889 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(cbD->currentTarget);
1890 if (QRhiShadingRateMap *srm = rt->description().shadingRateMap()) {
1891 if (id<MTLRasterizationRateMap> rateMap =
QRHI_RES(QMetalShadingRateMap, srm)->d->rateMap) {
1892 auto screenSize = [rateMap screenSize];
1893 outputSize = QSize(screenSize.width, screenSize.height);
1900 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1904 vp.originX =
double(x);
1905 vp.originY =
double(y);
1906 vp.width =
double(w);
1907 vp.height =
double(h);
1908 vp.znear =
double(viewport.minDepth());
1909 vp.zfar =
double(viewport.maxDepth());
1911 [cbD->d->currentRenderPassEncoder setViewport: vp];
1913 if (cbD->currentGraphicsPipeline
1914 && !cbD->currentGraphicsPipeline->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
1916 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
1917 s.x = NSUInteger(x);
1918 s.y = NSUInteger(y);
1919 s.width = NSUInteger(w);
1920 s.height = NSUInteger(h);
1921 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1929 Q_ASSERT(!cbD->currentGraphicsPipeline
1930 || cbD->currentGraphicsPipeline->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor));
1931 const QSize outputSize = cbD->currentTarget->pixelSize();
1935 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
1939 s.x = NSUInteger(x);
1940 s.y = NSUInteger(y);
1941 s.width = NSUInteger(w);
1942 s.height = NSUInteger(h);
1944 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1952 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
1953 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
1961 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
1967 Q_UNUSED(coarsePixelSize);
1972 if (cbD
->d->currentRenderPassEncoder) {
1973 [cbD->d->currentRenderPassEncoder endEncoding];
1974 cbD->d->currentRenderPassEncoder = nil;
1977 if (!cbD->d->tessellationComputeEncoder)
1978 cbD->d->tessellationComputeEncoder = [cbD->d->cb computeCommandEncoder];
1980 return cbD
->d->tessellationComputeEncoder;
1985 if (cbD
->d->tessellationComputeEncoder) {
1986 [cbD->d->tessellationComputeEncoder endEncoding];
1987 cbD->d->tessellationComputeEncoder = nil;
1992 switch (cbD->currentTarget->resourceType()) {
1993 case QRhiResource::SwapChainRenderTarget:
1996 case QRhiResource::TextureRenderTarget:
2005 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2007 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2008 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
2009 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2012 MTLLoadAction oldDepthLoad;
2013 MTLLoadAction oldStencilLoad;
2015 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2016 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
2017 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2019 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2020 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
2021 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2024 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2028 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2032 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2033 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2042 if (graphicsPipeline
->d->tess.failed)
2046 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2047 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2051 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2057 id<MTLComputeCommandEncoder> vertTescComputeEncoder = tessellationComputeEncoder(cbD);
2061 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2062 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2063 if (args.type == TessDrawArgs::U16Indexed)
2064 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2065 else if (args.type == TessDrawArgs::U32Indexed)
2066 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2067 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2068 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2069 [computeEncoder setComputePipelineState: computePipelineState];
2074 cbD
->d->currentComputePassEncoder = computeEncoder;
2076 cbD->d->currentComputePassEncoder = nil;
2078 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2079 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2080 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2082 if (outputBufferBinding >= 0) {
2083 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2084 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2087 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2090 if (indexBufferBinding >= 0)
2091 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2093 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2094 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2095 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2096 [computeEncoder setBuffers: bufferBatch.resources.constData()
2097 offsets: offsetBatch.resources.constData()
2098 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2102 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2103 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2105 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2106 args.draw.vertexCount, args.draw.instanceCount)];
2109 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2110 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2115 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2116 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2117 [computeEncoder setComputePipelineState: computePipelineState];
2119 cbD
->d->currentComputePassEncoder = computeEncoder;
2121 cbD->d->currentComputePassEncoder = nil;
2123 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2124 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2125 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2126 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2127 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2128 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2130 if (outputBufferBinding >= 0) {
2131 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2132 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2135 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2138 if (patchOutputBufferBinding >= 0) {
2139 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2140 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2141 if (!tescPatchOutBuf)
2143 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2146 if (tessFactorBufferBinding >= 0) {
2147 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2148 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2151 if (paramsBufferBinding >= 0) {
2153 quint32 inControlPointCount;
2160 params.patchCount = patchCount;
2161 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2162 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2163 memcpy(p, ¶ms,
sizeof(params));
2164 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2167 if (vertOutBuf && inputBufferBinding >= 0)
2168 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2170 int sgSize =
int(computePipelineState.threadExecutionWidth);
2171 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2172 while (wgSize > caps.maxThreadGroupSize) {
2174 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2176 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2177 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2194 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2199 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2200 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2201 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2202 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2204 if (outputBufferBinding >= 0 && tescOutBuf)
2205 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2207 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2208 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2210 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2211 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2212 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2215 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2217 patchCount: patchCount
2218 patchIndexBuffer: nil
2219 patchIndexBufferOffset: 0
2229 if (multiViewCount <= 1)
2233 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2234 if (viewMaskBufBinding == -1) {
2235 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2242 multiViewInfo.viewOffset = 0;
2243 multiViewInfo.viewCount = quint32(multiViewCount);
2247 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2248 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2249 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2250 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2254 *instanceCount *= multiViewCount;
2259 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2268 a.draw.vertexCount = vertexCount;
2269 a.draw.instanceCount = instanceCount;
2270 a.draw.firstVertex = firstVertex;
2271 a.draw.firstInstance = firstInstance;
2276 adjustForMultiViewDraw(&instanceCount, cb);
2278 if (caps.baseVertexAndInstance) {
2279 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2280 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2282 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2283 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2288 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2296 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2297 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2300 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2305 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2306 a.drawIndexed.indexCount = indexCount;
2307 a.drawIndexed.instanceCount = instanceCount;
2308 a.drawIndexed.firstIndex = firstIndex;
2309 a.drawIndexed.vertexOffset = vertexOffset;
2310 a.drawIndexed.firstInstance = firstInstance;
2311 a.drawIndexed.indexBuffer = mtlibuf;
2316 adjustForMultiViewDraw(&instanceCount, cb);
2318 if (caps.baseVertexAndInstance) {
2319 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2320 indexCount: indexCount
2321 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2322 indexBuffer: mtlibuf
2323 indexBufferOffset: indexOffset
2324 instanceCount: instanceCount
2325 baseVertex: vertexOffset
2326 baseInstance: firstInstance];
2328 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2329 indexCount: indexCount
2330 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2331 indexBuffer: mtlibuf
2332 indexBufferOffset: indexOffset
2333 instanceCount: instanceCount];
2342 NSString *str = [NSString stringWithUTF8String: name.constData()];
2344 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2345 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2347 [cbD->d->cb pushDebugGroup: str];
2356 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2357 [cbD->d->currentRenderPassEncoder popDebugGroup];
2359 [cbD->d->cb popDebugGroup];
2368 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2369 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2374 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
2400 currentFrameSlot = swapChainD->currentFrameSlot;
2405 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2413 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2414 if (sc != swapChainD)
2415 sc->waitUntilCompleted(currentFrameSlot);
2418 [d->captureScope beginScope];
2420 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2424 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2431 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2432 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
2437 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2440 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2441 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2444 return QRhi::FrameOpSuccess;
2453 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
2455 __block
int thisFrameSlot = currentFrameSlot;
2456 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2457 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2458 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2465 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2466 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
2467 [drawableTexture release];
2471 if (flags.testFlag(QRhi::SkipPresent)) {
2473 [commandBuffer commit];
2475 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2477 if (swapChainD
->d->layer.presentsWithTransaction) {
2478 [commandBuffer commit];
2480 auto *metalLayer = swapChainD
->d->layer;
2481 auto presentWithTransaction = ^{
2482 [commandBuffer waitUntilScheduled];
2489 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
2490 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
2491 if (textureSize == surfaceSize) {
2494 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
2495 << textureSize <<
"not matching surface size" << surfaceSize;
2499 if (NSThread.currentThread == NSThread.mainThread) {
2500 presentWithTransaction();
2502 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2503 Q_ASSERT(qtMetalLayer);
2505 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
2509 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2510 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
2516 if (qtMetalLayer.displayLock.tryLockForRead()) {
2518 qtMetalLayer.displayLock.unlock();
2520 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
2521 <<
"due to" << qtMetalLayer <<
"needing display";
2527 [commandBuffer commit];
2531 [commandBuffer commit];
2538 [swapChainD->d->curDrawable release];
2539 swapChainD->d->curDrawable = nil;
2541 [d->captureScope endScope];
2545 return QRhi::FrameOpSuccess;
2552 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2554 for (QMetalSwapChain *sc : std::as_const(swapchains))
2555 sc->waitUntilCompleted(currentFrameSlot);
2557 d->ofr.active =
true;
2558 *cb = &
d->ofr.cbWrapper;
2559 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2562 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
2563 d->ofr.lastGpuTime = 0;
2566 return QRhi::FrameOpSuccess;
2572 Q_ASSERT(
d->ofr.active);
2573 d->ofr.active =
false;
2575 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
2579 [cb waitUntilCompleted];
2581 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2585 return QRhi::FrameOpSuccess;
2590 id<MTLCommandBuffer> cb = nil;
2593 if (
d->ofr.active) {
2596 cb =
d->ofr.cbWrapper.d->cb;
2601 cb = swapChainD->cbWrapper.d->cb;
2605 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2606 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2607 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
2612 sc->waitUntilCompleted(i);
2618 [cb waitUntilCompleted];
2622 if (
d->ofr.active) {
2623 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2624 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2626 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2627 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2635 return QRhi::FrameOpSuccess;
2639 const QColor &colorClearValue,
2640 const QRhiDepthStencilClearValue &depthStencilClearValue,
2642 QRhiShadingRateMap *shadingRateMap)
2644 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2645 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2646 colorClearValue.alphaF());
2648 for (uint i = 0; i < uint(colorAttCount); ++i) {
2649 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2650 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2651 rp.colorAttachments[i].clearColor = c;
2654 if (hasDepthStencil) {
2655 rp.depthAttachment.loadAction = MTLLoadActionClear;
2656 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2657 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2658 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2659 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
2660 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2664 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
2672 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
2673 subresDesc.data().size() : subresDesc.image().sizeInBytes();
2674 if (imageSizeBytes > 0)
2675 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2680 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
2683 const QPoint dp = subresDesc.destinationTopLeft();
2684 const QByteArray rawData = subresDesc.data();
2685 QImage img = subresDesc.image();
2686 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2687 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
2689 if (!img.isNull()) {
2690 const qsizetype fullImageSizeBytes = img.sizeInBytes();
2691 int w = img.width();
2692 int h = img.height();
2693 int bpl = img.bytesPerLine();
2695 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2696 const int sx = subresDesc.sourceTopLeft().x();
2697 const int sy = subresDesc.sourceTopLeft().y();
2698 if (!subresDesc.sourceSize().isEmpty()) {
2699 w = subresDesc.sourceSize().width();
2700 h = subresDesc.sourceSize().height();
2702 if (w == img.width()) {
2703 const int bpc = qMax(1, img.depth() / 8);
2704 Q_ASSERT(h * img.bytesPerLine() <= fullImageSizeBytes);
2705 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
2706 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
2707 h * img.bytesPerLine());
2709 img = img.copy(sx, sy, w, h);
2710 bpl = img.bytesPerLine();
2711 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
2712 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
2715 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
2718 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2719 sourceOffset: NSUInteger(*curOfs)
2720 sourceBytesPerRow: NSUInteger(bpl)
2721 sourceBytesPerImage: 0
2722 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2723 toTexture: texD->d->tex
2724 destinationSlice: NSUInteger(is3D ? 0 : layer)
2725 destinationLevel: NSUInteger(level)
2726 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2727 options: MTLBlitOptionNone];
2729 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2730 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
2731 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2732 const int subresw = subresSize.width();
2733 const int subresh = subresSize.height();
2735 if (subresDesc.sourceSize().isEmpty()) {
2739 w = subresDesc.sourceSize().width();
2740 h = subresDesc.sourceSize().height();
2745 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
2747 const int dx = aligned(dp.x(), blockDim.width());
2748 const int dy = aligned(dp.y(), blockDim.height());
2749 if (dx + w != subresw)
2750 w = aligned(w, blockDim.width());
2751 if (dy + h != subresh)
2752 h = aligned(h, blockDim.height());
2754 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2756 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2757 sourceOffset: NSUInteger(*curOfs)
2758 sourceBytesPerRow: bpl
2759 sourceBytesPerImage: 0
2760 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2761 toTexture: texD->d->tex
2762 destinationSlice: NSUInteger(is3D ? 0 : layer)
2763 destinationLevel: NSUInteger(level)
2764 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
2765 options: MTLBlitOptionNone];
2767 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2768 }
else if (!rawData.isEmpty()) {
2769 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2770 const int subresw = subresSize.width();
2771 const int subresh = subresSize.height();
2773 if (subresDesc.sourceSize().isEmpty()) {
2777 w = subresDesc.sourceSize().width();
2778 h = subresDesc.sourceSize().height();
2782 if (subresDesc.dataStride())
2783 bpl = subresDesc.dataStride();
2785 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
2787 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2789 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2790 sourceOffset: NSUInteger(*curOfs)
2791 sourceBytesPerRow: bpl
2792 sourceBytesPerImage: 0
2793 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2794 toTexture: texD->d->tex
2795 destinationSlice: NSUInteger(is3D ? 0 : layer)
2796 destinationLevel: NSUInteger(level)
2797 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2798 options: MTLBlitOptionNone];
2800 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2802 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2811 id<MTLBlitCommandEncoder> blitEnc = nil;
2812 auto ensureBlit = [&blitEnc, cbD,
this]() {
2814 blitEnc = [cbD->d->cb blitCommandEncoder];
2816 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
2824 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2826 if (u.offset == 0 && u
.data.size() == bufD->m_size)
2827 bufD
->d->pendingUpdates[i].clear();
2828 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
2834 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
2835 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
2837 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
2841 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
2842 if (bufD->m_type == QRhiBuffer::Dynamic) {
2843 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
2845 u.result->data.resize(u.readSize);
2846 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
2848 if (u.result->completed)
2849 u.result->completed();
2853 readback.buf = bufD
->d->buf[idx];
2854 readback.offset = u.offset;
2855 readback.readSize = u.readSize;
2856 readback.result = u.result;
2857 d->activeBufferReadbacks.append(readback);
2859 if (bufD->d->managed) {
2862 [blitEnc synchronizeResource:readback.buf];
2873 qsizetype stagingSize = 0;
2874 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2875 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2876 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2877 stagingSize += subresUploadByteSize(subresDesc);
2882 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
2883 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
2884 options: MTLResourceStorageModeShared];
2886 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
2887 qsizetype curOfs = 0;
2888 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2889 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2890 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2891 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
2895 utexD->lastActiveFrameSlot = currentFrameSlot;
2899 e.lastActiveFrameSlot = currentFrameSlot;
2900 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
2901 utexD->d->stagingBuf[currentFrameSlot] = nil;
2902 d->releaseQueue.append(e);
2907 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2908 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2909 const QPoint dp = u.desc.destinationTopLeft();
2910 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2911 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2912 const QPoint sp = u.desc.sourceTopLeft();
2915 [blitEnc copyFromTexture: srcD->d->tex
2916 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
2917 sourceLevel: NSUInteger(u.desc.sourceLevel())
2918 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
2919 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
2920 toTexture: dstD->d->tex
2921 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
2922 destinationLevel: NSUInteger(u.desc.destinationLevel())
2923 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
2925 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
2928 readback.activeFrameSlot = currentFrameSlot;
2929 readback.desc = u.rb;
2930 readback.result = u.result;
2939 qWarning(
"Multisample texture cannot be read back");
2942 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2943 if (u.rb.rect().isValid())
2946 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
2947 readback.format = texD->m_format;
2949 texD->lastActiveFrameSlot = currentFrameSlot;
2953 if (u.rb.rect().isValid())
2956 rect = QRect({0, 0}, swapChainD->pixelSize);
2957 readback.format = swapChainD
->d->rhiColorFormat;
2961 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
2963 readback.pixelSize = rect.size();
2966 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
2967 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
2970 [blitEnc copyFromTexture: src
2971 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
2972 sourceLevel: NSUInteger(u.rb.level())
2973 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
2974 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
2975 toBuffer: readback.buf
2976 destinationOffset: 0
2977 destinationBytesPerRow: bpl
2978 destinationBytesPerImage: 0
2979 options: MTLBlitOptionNone];
2981 d->activeTextureReadbacks.append(readback);
2985 [blitEnc generateMipmapsForTexture: utexD->d->tex];
2986 utexD->lastActiveFrameSlot = currentFrameSlot;
2992 [blitEnc popDebugGroup];
2993 [blitEnc endEncoding];
3002 if (bufD
->d->pendingUpdates[slot].isEmpty())
3005 void *p = [bufD->d->buf[slot] contents];
3006 quint32 changeBegin = UINT32_MAX;
3007 quint32 changeEnd = 0;
3008 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3009 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3010 if (u.offset < changeBegin)
3011 changeBegin = u.offset;
3012 if (u.offset + u.data.size() > changeEnd)
3013 changeEnd = u.offset + u.data.size();
3016 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3017 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3020 bufD
->d->pendingUpdates[slot].clear();
3025 executeBufferHostWritesForSlot(bufD, bufD->d->slotted ? currentFrameSlot : 0);
3030 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3036 QRhiRenderTarget *rt,
3037 const QColor &colorClearValue,
3038 const QRhiDepthStencilClearValue &depthStencilClearValue,
3039 QRhiResourceUpdateBatch *resourceUpdates,
3045 if (resourceUpdates)
3049 switch (rt->resourceType()) {
3050 case QRhiResource::SwapChainRenderTarget:
3054 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3057 depthStencilClearValue,
3065 if (!swapChainD
->d->curDrawable) {
3066 QMacAutoReleasePool pool;
3067 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3069 if (!swapChainD
->d->curDrawable) {
3070 qWarning(
"No drawable");
3073 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
3078 color0.resolveTex = scTex;
3084 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3087 case QRhiResource::TextureRenderTarget:
3091 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3095 depthStencilClearValue,
3097 rtTex->m_desc.shadingRateMap());
3098 if (rtD->fb.preserveColor) {
3099 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3100 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3103 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3104 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3106 int colorAttCount = 0;
3107 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3111 if (it->texture()) {
3112 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3113 if (it->multiViewCount() >= 2)
3114 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3115 }
else if (it->renderBuffer()) {
3116 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3118 if (it->resolveTexture())
3119 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3121 if (rtTex->m_desc.depthStencilBuffer())
3122 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3123 if (rtTex->m_desc.depthTexture()) {
3125 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3126 if (colorAttCount == 0 && depthTexture->arraySize() >= 2)
3127 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3129 if (rtTex->m_desc.depthResolveTexture())
3130 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3131 if (rtTex->m_desc.shadingRateMap())
3132 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3141 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3142 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3143 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3144 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3145 if (rtD->fb.colorAtt[i].resolveTex) {
3146 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3147 : MTLStoreActionMultisampleResolve;
3148 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3149 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3150 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3155 Q_ASSERT(rtD->fb.dsTex);
3156 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3157 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3158 if (rtD->fb.depthNeedsStore)
3159 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3160 if (rtD->fb.dsResolveTex) {
3161 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3162 : MTLStoreActionMultisampleResolve;
3163 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3164 if (rtD->fb.hasStencil) {
3165 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3166 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = cbD
->d->currentPassRpDesc.depthAttachment.storeAction;
3171 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3176 cbD->currentTarget = rt;
3184 [cbD->d->currentRenderPassEncoder endEncoding];
3187 cbD->currentTarget =
nullptr;
3189 if (resourceUpdates)
3194 QRhiResourceUpdateBatch *resourceUpdates,
3200 if (resourceUpdates)
3203 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3213 [cbD->d->currentComputePassEncoder endEncoding];
3216 if (resourceUpdates)
3229 cbD->currentPipelineGeneration = psD->generation;
3231 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3234 psD->lastActiveFrameSlot = currentFrameSlot;
3243 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3244 threadsPerThreadgroup: psD->d->localSize];
3249 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3250 [e.buffer.buffers[i] release];
3255 [e.renderbuffer.texture release];
3260 [e.texture.texture release];
3261 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3262 [e.texture.stagingBuffers[i] release];
3263 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3264 [e.texture.views[i] release];
3269 [e.sampler.samplerState release];
3274 for (
int i =
d->releaseQueue.count() - 1; i >= 0; --i) {
3276 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3290 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
3291 [e.stagingBuffer.buffer release];
3293 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
3294 [e.graphicsPipeline.pipelineState release];
3295 [e.graphicsPipeline.depthStencilState release];
3296 [e.graphicsPipeline.tessVertexComputeState[0] release];
3297 [e.graphicsPipeline.tessVertexComputeState[1] release];
3298 [e.graphicsPipeline.tessVertexComputeState[2] release];
3299 [e.graphicsPipeline.tessTessControlComputeState release];
3301 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
3302 [e.computePipeline.pipelineState release];
3304 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
3305 [e.shadingRateMap.rateMap release];
3310 d->releaseQueue.removeAt(i);
3317 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
3319 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3321 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3322 readback.result->format = readback.format;
3323 readback.result->pixelSize = readback.pixelSize;
3324 readback.result->data.resize(
int(readback.bufSize));
3325 void *p = [readback.buf contents];
3326 memcpy(readback.result->data.data(), p, readback.bufSize);
3327 [readback.buf release];
3329 if (readback.result->completed)
3330 completedCallbacks.append(readback.result->completed);
3332 d->activeTextureReadbacks.remove(i);
3336 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3338 if (forced || currentFrameSlot == readback.activeFrameSlot
3339 || readback.activeFrameSlot < 0) {
3340 readback.result->data.resize(readback.readSize);
3341 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
3343 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3345 if (readback.result->completed)
3346 completedCallbacks.append(readback.result->completed);
3348 d->activeBufferReadbacks.remove(i);
3352 for (
auto f : completedCallbacks)
3360 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3380 e.buffer.buffers[i] =
d->buf[i];
3382 d->pendingUpdates[i].clear();
3387 rhiD
->d->releaseQueue.append(e);
3388 rhiD->unregisterResource(
this);
3397 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3398 qWarning(
"StorageBuffer cannot be combined with Dynamic");
3402 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3403 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3406 MTLResourceOptions opts = MTLResourceStorageModeShared;
3410 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3411 opts = MTLResourceStorageModeManaged;
3420 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer);
3422 if (
int(m_usage) == WorkBufPoolUsage)
3427 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3428 if (!m_objectName.isEmpty()) {
3430 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3432 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
3433 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3441 rhiD->registerResource(
this);
3453 b.objects[i] = &
d->buf[i];
3458 return { { &
d->buf[0] }, 1 };
3468 Q_ASSERT(m_type == Dynamic);
3470 Q_ASSERT(rhiD->inFrame);
3471 const int slot = rhiD->currentFrameSlot;
3472 void *p = [d->buf[slot] contents];
3473 return static_cast<
char *>(p);
3480 QRHI_RES_RHI(QRhiMetal);
3481 const int slot = rhiD->currentFrameSlot;
3482 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3493 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3495 case QRhiTexture::RGBA8:
3496 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3497 case QRhiTexture::BGRA8:
3498 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3499 case QRhiTexture::R8:
3501 return MTLPixelFormatR8Unorm;
3503 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3505 case QRhiTexture::R8SI:
3506 return MTLPixelFormatR8Sint;
3507 case QRhiTexture::R8UI:
3508 return MTLPixelFormatR8Uint;
3509 case QRhiTexture::RG8:
3511 return MTLPixelFormatRG8Unorm;
3513 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3515 case QRhiTexture::R16:
3516 return MTLPixelFormatR16Unorm;
3517 case QRhiTexture::RG16:
3518 return MTLPixelFormatRG16Unorm;
3519 case QRhiTexture::RED_OR_ALPHA8:
3520 return MTLPixelFormatR8Unorm;
3522 case QRhiTexture::RGBA16F:
3523 return MTLPixelFormatRGBA16Float;
3524 case QRhiTexture::RGBA32F:
3525 return MTLPixelFormatRGBA32Float;
3526 case QRhiTexture::R16F:
3527 return MTLPixelFormatR16Float;
3528 case QRhiTexture::R32F:
3529 return MTLPixelFormatR32Float;
3531 case QRhiTexture::RGB10A2:
3532 return MTLPixelFormatRGB10A2Unorm;
3534 case QRhiTexture::R32SI:
3535 return MTLPixelFormatR32Sint;
3536 case QRhiTexture::R32UI:
3537 return MTLPixelFormatR32Uint;
3538 case QRhiTexture::RG32SI:
3539 return MTLPixelFormatRG32Sint;
3540 case QRhiTexture::RG32UI:
3541 return MTLPixelFormatRG32Uint;
3542 case QRhiTexture::RGBA32SI:
3543 return MTLPixelFormatRGBA32Sint;
3544 case QRhiTexture::RGBA32UI:
3545 return MTLPixelFormatRGBA32Uint;
3548 case QRhiTexture::D16:
3549 return MTLPixelFormatDepth16Unorm;
3550 case QRhiTexture::D24:
3551 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3552 case QRhiTexture::D24S8:
3553 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3555 case QRhiTexture::D16:
3556 return MTLPixelFormatDepth32Float;
3557 case QRhiTexture::D24:
3558 return MTLPixelFormatDepth32Float;
3559 case QRhiTexture::D24S8:
3560 return MTLPixelFormatDepth32Float_Stencil8;
3562 case QRhiTexture::D32F:
3563 return MTLPixelFormatDepth32Float;
3564 case QRhiTexture::D32FS8:
3565 return MTLPixelFormatDepth32Float_Stencil8;
3568 case QRhiTexture::BC1:
3569 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3570 case QRhiTexture::BC2:
3571 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3572 case QRhiTexture::BC3:
3573 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3574 case QRhiTexture::BC4:
3575 return MTLPixelFormatBC4_RUnorm;
3576 case QRhiTexture::BC5:
3577 qWarning(
"QRhiMetal does not support BC5");
3578 return MTLPixelFormatInvalid;
3579 case QRhiTexture::BC6H:
3580 return MTLPixelFormatBC6H_RGBUfloat;
3581 case QRhiTexture::BC7:
3582 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3584 case QRhiTexture::BC1:
3585 case QRhiTexture::BC2:
3586 case QRhiTexture::BC3:
3587 case QRhiTexture::BC4:
3588 case QRhiTexture::BC5:
3589 case QRhiTexture::BC6H:
3590 case QRhiTexture::BC7:
3591 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
3592 return MTLPixelFormatInvalid;
3596 case QRhiTexture::ETC2_RGB8:
3597 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3598 case QRhiTexture::ETC2_RGB8A1:
3599 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3600 case QRhiTexture::ETC2_RGBA8:
3601 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3603 case QRhiTexture::ASTC_4x4:
3604 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3605 case QRhiTexture::ASTC_5x4:
3606 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3607 case QRhiTexture::ASTC_5x5:
3608 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3609 case QRhiTexture::ASTC_6x5:
3610 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3611 case QRhiTexture::ASTC_6x6:
3612 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3613 case QRhiTexture::ASTC_8x5:
3614 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3615 case QRhiTexture::ASTC_8x6:
3616 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3617 case QRhiTexture::ASTC_8x8:
3618 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3619 case QRhiTexture::ASTC_10x5:
3620 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3621 case QRhiTexture::ASTC_10x6:
3622 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3623 case QRhiTexture::ASTC_10x8:
3624 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3625 case QRhiTexture::ASTC_10x10:
3626 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3627 case QRhiTexture::ASTC_12x10:
3628 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3629 case QRhiTexture::ASTC_12x12:
3630 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3632 case QRhiTexture::ETC2_RGB8:
3633 if (d->caps.isAppleGPU)
3634 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3635 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3636 return MTLPixelFormatInvalid;
3637 case QRhiTexture::ETC2_RGB8A1:
3638 if (d->caps.isAppleGPU)
3639 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3640 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3641 return MTLPixelFormatInvalid;
3642 case QRhiTexture::ETC2_RGBA8:
3643 if (d->caps.isAppleGPU)
3644 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3645 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3646 return MTLPixelFormatInvalid;
3647 case QRhiTexture::ASTC_4x4:
3648 if (d->caps.isAppleGPU)
3649 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3650 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3651 return MTLPixelFormatInvalid;
3652 case QRhiTexture::ASTC_5x4:
3653 if (d->caps.isAppleGPU)
3654 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3655 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3656 return MTLPixelFormatInvalid;
3657 case QRhiTexture::ASTC_5x5:
3658 if (d->caps.isAppleGPU)
3659 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3660 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3661 return MTLPixelFormatInvalid;
3662 case QRhiTexture::ASTC_6x5:
3663 if (d->caps.isAppleGPU)
3664 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3665 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3666 return MTLPixelFormatInvalid;
3667 case QRhiTexture::ASTC_6x6:
3668 if (d->caps.isAppleGPU)
3669 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3670 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3671 return MTLPixelFormatInvalid;
3672 case QRhiTexture::ASTC_8x5:
3673 if (d->caps.isAppleGPU)
3674 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3675 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3676 return MTLPixelFormatInvalid;
3677 case QRhiTexture::ASTC_8x6:
3678 if (d->caps.isAppleGPU)
3679 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3680 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3681 return MTLPixelFormatInvalid;
3682 case QRhiTexture::ASTC_8x8:
3683 if (d->caps.isAppleGPU)
3684 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3685 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3686 return MTLPixelFormatInvalid;
3687 case QRhiTexture::ASTC_10x5:
3688 if (d->caps.isAppleGPU)
3689 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3690 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3691 return MTLPixelFormatInvalid;
3692 case QRhiTexture::ASTC_10x6:
3693 if (d->caps.isAppleGPU)
3694 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3695 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3696 return MTLPixelFormatInvalid;
3697 case QRhiTexture::ASTC_10x8:
3698 if (d->caps.isAppleGPU)
3699 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3700 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3701 return MTLPixelFormatInvalid;
3702 case QRhiTexture::ASTC_10x10:
3703 if (d->caps.isAppleGPU)
3704 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3705 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3706 return MTLPixelFormatInvalid;
3707 case QRhiTexture::ASTC_12x10:
3708 if (d->caps.isAppleGPU)
3709 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3710 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3711 return MTLPixelFormatInvalid;
3712 case QRhiTexture::ASTC_12x12:
3713 if (d->caps.isAppleGPU)
3714 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3715 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3716 return MTLPixelFormatInvalid;
3721 return MTLPixelFormatInvalid;
3726 int sampleCount, QRhiRenderBuffer::Flags flags,
3727 QRhiTexture::Format backingFormatHint)
3748 e.renderbuffer.texture =
d->tex;
3753 rhiD
->d->releaseQueue.append(e);
3754 rhiD->unregisterResource(
this);
3763 if (m_pixelSize.isEmpty())
3767 samples = rhiD->effectiveSampleCount(m_sampleCount);
3769 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3770 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3771 desc.width = NSUInteger(m_pixelSize.width());
3772 desc.height = NSUInteger(m_pixelSize.height());
3774 desc.sampleCount = NSUInteger(
samples);
3775 desc.resourceOptions = MTLResourceStorageModePrivate;
3776 desc.usage = MTLTextureUsageRenderTarget;
3781 if (rhiD->caps.isAppleGPU) {
3782 desc.storageMode = MTLStorageModeMemoryless;
3783 d->format = MTLPixelFormatDepth32Float_Stencil8;
3785 desc.storageMode = MTLStorageModePrivate;
3786 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
3787 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3790 desc.storageMode = MTLStorageModeMemoryless;
3791 d->format = MTLPixelFormatDepth32Float_Stencil8;
3793 desc.pixelFormat =
d->format;
3796 desc.storageMode = MTLStorageModePrivate;
3797 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3798 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
3800 d->format = MTLPixelFormatRGBA8Unorm;
3801 desc.pixelFormat =
d->format;
3808 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3811 if (!m_objectName.isEmpty())
3812 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3816 rhiD->registerResource(
this);
3822 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3823 return m_backingFormatHint;
3825 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
3829 int arraySize,
int sampleCount, Flags flags)
3833 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3834 d->stagingBuf[i] = nil;
3836 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3837 d->perLevelViews[i] = nil;
3855 e.texture.texture = d->owns ? d->tex : nil;
3859 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
3860 d->stagingBuf[i] = nil;
3863 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
3864 e.texture.views[i] =
d->perLevelViews[i];
3865 d->perLevelViews[i] = nil;
3870 rhiD
->d->releaseQueue.append(e);
3871 rhiD->unregisterResource(
this);
3880 const bool isCube = m_flags.testFlag(CubeMap);
3881 const bool is3D = m_flags.testFlag(ThreeDimensional);
3882 const bool isArray = m_flags.testFlag(TextureArray);
3883 const bool hasMipMaps = m_flags.testFlag(MipMapped);
3884 const bool is1D = m_flags.testFlag(OneDimensional);
3886 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
3887 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
3890 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
3891 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
3892 samples = rhiD->effectiveSampleCount(m_sampleCount);
3895 qWarning(
"Cubemap texture cannot be multisample");
3899 qWarning(
"3D texture cannot be multisample");
3903 qWarning(
"Multisample texture cannot have mipmaps");
3907 if (isCube && is3D) {
3908 qWarning(
"Texture cannot be both cube and 3D");
3911 if (isArray && is3D) {
3912 qWarning(
"Texture cannot be both array and 3D");
3916 qWarning(
"Texture cannot be both 1D and 3D");
3919 if (is1D && isCube) {
3920 qWarning(
"Texture cannot be both 1D and cube");
3923 if (m_depth > 1 && !is3D) {
3924 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
3927 if (m_arraySize > 0 && !isArray) {
3928 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
3931 if (m_arraySize < 1 && isArray) {
3932 qWarning(
"Texture is an array but array size is %d", m_arraySize);
3937 *adjustedSize = size;
3945 if (!prepareCreate(&size))
3948 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3950 const bool isCube = m_flags.testFlag(CubeMap);
3951 const bool is3D = m_flags.testFlag(ThreeDimensional);
3952 const bool isArray = m_flags.testFlag(TextureArray);
3953 const bool is1D = m_flags.testFlag(OneDimensional);
3955 desc.textureType = MTLTextureTypeCube;
3957 desc.textureType = MTLTextureType3D;
3959 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
3960 }
else if (isArray) {
3961 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
3963 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3965 desc.pixelFormat =
d->format;
3966 desc.width = NSUInteger(size.width());
3967 desc.height = NSUInteger(size.height());
3968 desc.depth = is3D ? qMax(1, m_depth) : 1;
3971 desc.sampleCount = NSUInteger(
samples);
3973 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
3974 desc.resourceOptions = MTLResourceStorageModePrivate;
3975 desc.storageMode = MTLStorageModePrivate;
3976 desc.usage = MTLTextureUsageShaderRead;
3977 if (m_flags.testFlag(RenderTarget))
3978 desc.usage |= MTLTextureUsageRenderTarget;
3979 if (m_flags.testFlag(UsedWithLoadStore))
3980 desc.usage |= MTLTextureUsageShaderWrite;
3983 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3986 if (!m_objectName.isEmpty())
3987 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3993 rhiD->registerResource(
this);
3999 id<MTLTexture> tex = id<MTLTexture>(src.object);
4003 if (!prepareCreate())
4013 rhiD->registerResource(
this);
4019 return {quint64(
d->tex), 0};
4025 if (perLevelViews[level])
4026 return perLevelViews[level];
4028 const MTLTextureType type = [tex textureType];
4029 const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap);
4030 const bool isArray = q->m_flags.testFlag(QRhiTexture::TextureArray);
4031 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
4032 levels: NSMakeRange(NSUInteger(level), 1)
4033 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
4035 perLevelViews[level] = view;
4040 AddressMode u, AddressMode v, AddressMode w)
4054 if (!
d->samplerState)
4061 e.sampler.samplerState =
d->samplerState;
4062 d->samplerState = nil;
4066 rhiD
->d->releaseQueue.append(e);
4067 rhiD->unregisterResource(
this);
4074 case QRhiSampler::Nearest:
4075 return MTLSamplerMinMagFilterNearest;
4076 case QRhiSampler::Linear:
4077 return MTLSamplerMinMagFilterLinear;
4080 return MTLSamplerMinMagFilterNearest;
4087 case QRhiSampler::None:
4088 return MTLSamplerMipFilterNotMipmapped;
4089 case QRhiSampler::Nearest:
4090 return MTLSamplerMipFilterNearest;
4091 case QRhiSampler::Linear:
4092 return MTLSamplerMipFilterLinear;
4095 return MTLSamplerMipFilterNotMipmapped;
4102 case QRhiSampler::Repeat:
4103 return MTLSamplerAddressModeRepeat;
4104 case QRhiSampler::ClampToEdge:
4105 return MTLSamplerAddressModeClampToEdge;
4106 case QRhiSampler::Mirror:
4107 return MTLSamplerAddressModeMirrorRepeat;
4110 return MTLSamplerAddressModeClampToEdge;
4117 case QRhiSampler::Never:
4118 return MTLCompareFunctionNever;
4119 case QRhiSampler::Less:
4120 return MTLCompareFunctionLess;
4121 case QRhiSampler::Equal:
4122 return MTLCompareFunctionEqual;
4123 case QRhiSampler::LessOrEqual:
4124 return MTLCompareFunctionLessEqual;
4125 case QRhiSampler::Greater:
4126 return MTLCompareFunctionGreater;
4127 case QRhiSampler::NotEqual:
4128 return MTLCompareFunctionNotEqual;
4129 case QRhiSampler::GreaterOrEqual:
4130 return MTLCompareFunctionGreaterEqual;
4131 case QRhiSampler::Always:
4132 return MTLCompareFunctionAlways;
4135 return MTLCompareFunctionNever;
4141 if (
d->samplerState)
4144 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
4145 desc.minFilter = toMetalFilter(m_minFilter);
4146 desc.magFilter = toMetalFilter(m_magFilter);
4147 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
4148 desc.sAddressMode = toMetalAddressMode(m_addressU);
4149 desc.tAddressMode = toMetalAddressMode(m_addressV);
4150 desc.rAddressMode = toMetalAddressMode(m_addressW);
4151 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
4154 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4159 rhiD->registerResource(
this);
4184 e.shadingRateMap.rateMap =
d->rateMap;
4189 rhiD
->d->releaseQueue.append(e);
4190 rhiD->unregisterResource(
this);
4199 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
4203 [d->rateMap retain];
4208 rhiD->registerResource(
this);
4217 serializedFormatData.reserve(16);
4229 rhiD->unregisterResource(
this);
4263 serializedFormatData.clear();
4264 auto p = std::back_inserter(serializedFormatData);
4286 rhiD->registerResource(rpD,
false);
4292 return serializedFormatData;
4314 return d->pixelSize;
4328 const QRhiTextureRenderTargetDescription &desc,
4345 rhiD->unregisterResource(
this);
4350 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
4353 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4355 for (
int i = 0; i < colorAttachmentCount; ++i) {
4356 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4362 if (m_desc.depthTexture())
4363 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4364 else if (m_desc.depthStencilBuffer())
4365 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
4367 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
4372 rhiD->registerResource(rpD,
false);
4379 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4380 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4381 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4385 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4389 Q_ASSERT(texD || rbD);
4390 id<MTLTexture> dst = nil;
4394 if (attIndex == 0) {
4395 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4398 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4401 if (attIndex == 0) {
4402 d->pixelSize = rbD->pixelSize();
4409 colorAtt
.slice = is3D ? it->layer() : 0;
4410 colorAtt
.level = it->level();
4412 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4415 d->fb.colorAtt[attIndex] = colorAtt;
4419 if (hasDepthStencil) {
4420 if (m_desc.depthTexture()) {
4422 d->fb.dsTex = depthTexD
->d->tex;
4423 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4424 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4425 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4427 d->pixelSize = depthTexD->pixelSize();
4432 d->fb.dsTex = depthRbD
->d->tex;
4433 d->fb.hasStencil =
true;
4434 d->fb.depthNeedsStore =
false;
4435 d->fb.preserveDs =
false;
4437 d->pixelSize = depthRbD->pixelSize();
4441 if (m_desc.depthResolveTexture()) {
4443 d->fb.dsResolveTex = depthResolveTexD
->d->tex;
4450 if (d->colorAttCount > 0)
4451 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4453 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4455 rhiD->registerResource(
this,
false);
4461 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4464 return d->pixelSize;
4489 sortedBindings.clear();
4494 rhiD->unregisterResource(
this);
4499 if (!sortedBindings.isEmpty())
4503 if (!rhiD->sanityCheckShaderResourceBindings(
this))
4506 rhiD->updateLayoutDesc(
this);
4508 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4509 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4510 if (!sortedBindings.isEmpty())
4511 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
4515 boundResourceData.resize(sortedBindings.count());
4517 for (BoundResourceData &bd : boundResourceData)
4518 memset(&bd, 0,
sizeof(BoundResourceData));
4521 rhiD->registerResource(
this,
false);
4527 sortedBindings.clear();
4528 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4529 if (!flags.testFlag(BindingsAreSorted))
4530 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4532 for (BoundResourceData &bd : boundResourceData)
4533 memset(&bd, 0,
sizeof(BoundResourceData));
4557 d->tess.compVs[0].destroy();
4558 d->tess.compVs[1].destroy();
4559 d->tess.compVs[2].destroy();
4561 d->tess.compTesc.destroy();
4562 d->tess.vertTese.destroy();
4564 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
4565 d->extraBufMgr.deviceLocalWorkBuffers.clear();
4566 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
4567 d->extraBufMgr.hostVisibleWorkBuffers.clear();
4572 if (!
d->ps && !
d->ds
4573 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
4574 && !
d->tess.tessControlComputeState)
4582 e.graphicsPipeline.pipelineState =
d->ps;
4583 e.graphicsPipeline.depthStencilState =
d->ds;
4584 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
4585 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
4588 d->tess.vertexComputeState = {};
4589 d->tess.tessControlComputeState = nil;
4593 rhiD
->d->releaseQueue.append(e);
4594 rhiD->unregisterResource(
this);
4601 case QRhiVertexInputAttribute::Float4:
4602 return MTLVertexFormatFloat4;
4603 case QRhiVertexInputAttribute::Float3:
4604 return MTLVertexFormatFloat3;
4605 case QRhiVertexInputAttribute::Float2:
4606 return MTLVertexFormatFloat2;
4607 case QRhiVertexInputAttribute::Float:
4608 return MTLVertexFormatFloat;
4609 case QRhiVertexInputAttribute::UNormByte4:
4610 return MTLVertexFormatUChar4Normalized;
4611 case QRhiVertexInputAttribute::UNormByte2:
4612 return MTLVertexFormatUChar2Normalized;
4613 case QRhiVertexInputAttribute::UNormByte:
4614 return MTLVertexFormatUCharNormalized;
4615 case QRhiVertexInputAttribute::UInt4:
4616 return MTLVertexFormatUInt4;
4617 case QRhiVertexInputAttribute::UInt3:
4618 return MTLVertexFormatUInt3;
4619 case QRhiVertexInputAttribute::UInt2:
4620 return MTLVertexFormatUInt2;
4621 case QRhiVertexInputAttribute::UInt:
4622 return MTLVertexFormatUInt;
4623 case QRhiVertexInputAttribute::SInt4:
4624 return MTLVertexFormatInt4;
4625 case QRhiVertexInputAttribute::SInt3:
4626 return MTLVertexFormatInt3;
4627 case QRhiVertexInputAttribute::SInt2:
4628 return MTLVertexFormatInt2;
4629 case QRhiVertexInputAttribute::SInt:
4630 return MTLVertexFormatInt;
4631 case QRhiVertexInputAttribute::Half4:
4632 return MTLVertexFormatHalf4;
4633 case QRhiVertexInputAttribute::Half3:
4634 return MTLVertexFormatHalf3;
4635 case QRhiVertexInputAttribute::Half2:
4636 return MTLVertexFormatHalf2;
4637 case QRhiVertexInputAttribute::Half:
4638 return MTLVertexFormatHalf;
4639 case QRhiVertexInputAttribute::UShort4:
4640 return MTLVertexFormatUShort4;
4641 case QRhiVertexInputAttribute::UShort3:
4642 return MTLVertexFormatUShort3;
4643 case QRhiVertexInputAttribute::UShort2:
4644 return MTLVertexFormatUShort2;
4645 case QRhiVertexInputAttribute::UShort:
4646 return MTLVertexFormatUShort;
4647 case QRhiVertexInputAttribute::SShort4:
4648 return MTLVertexFormatShort4;
4649 case QRhiVertexInputAttribute::SShort3:
4650 return MTLVertexFormatShort3;
4651 case QRhiVertexInputAttribute::SShort2:
4652 return MTLVertexFormatShort2;
4653 case QRhiVertexInputAttribute::SShort:
4654 return MTLVertexFormatShort;
4657 return MTLVertexFormatFloat4;
4664 case QRhiGraphicsPipeline::Zero:
4665 return MTLBlendFactorZero;
4666 case QRhiGraphicsPipeline::One:
4667 return MTLBlendFactorOne;
4668 case QRhiGraphicsPipeline::SrcColor:
4669 return MTLBlendFactorSourceColor;
4670 case QRhiGraphicsPipeline::OneMinusSrcColor:
4671 return MTLBlendFactorOneMinusSourceColor;
4672 case QRhiGraphicsPipeline::DstColor:
4673 return MTLBlendFactorDestinationColor;
4674 case QRhiGraphicsPipeline::OneMinusDstColor:
4675 return MTLBlendFactorOneMinusDestinationColor;
4676 case QRhiGraphicsPipeline::SrcAlpha:
4677 return MTLBlendFactorSourceAlpha;
4678 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
4679 return MTLBlendFactorOneMinusSourceAlpha;
4680 case QRhiGraphicsPipeline::DstAlpha:
4681 return MTLBlendFactorDestinationAlpha;
4682 case QRhiGraphicsPipeline::OneMinusDstAlpha:
4683 return MTLBlendFactorOneMinusDestinationAlpha;
4684 case QRhiGraphicsPipeline::ConstantColor:
4685 return MTLBlendFactorBlendColor;
4686 case QRhiGraphicsPipeline::ConstantAlpha:
4687 return MTLBlendFactorBlendAlpha;
4688 case QRhiGraphicsPipeline::OneMinusConstantColor:
4689 return MTLBlendFactorOneMinusBlendColor;
4690 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
4691 return MTLBlendFactorOneMinusBlendAlpha;
4692 case QRhiGraphicsPipeline::SrcAlphaSaturate:
4693 return MTLBlendFactorSourceAlphaSaturated;
4694 case QRhiGraphicsPipeline::Src1Color:
4695 return MTLBlendFactorSource1Color;
4696 case QRhiGraphicsPipeline::OneMinusSrc1Color:
4697 return MTLBlendFactorOneMinusSource1Color;
4698 case QRhiGraphicsPipeline::Src1Alpha:
4699 return MTLBlendFactorSource1Alpha;
4700 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
4701 return MTLBlendFactorOneMinusSource1Alpha;
4704 return MTLBlendFactorZero;
4711 case QRhiGraphicsPipeline::Add:
4712 return MTLBlendOperationAdd;
4713 case QRhiGraphicsPipeline::Subtract:
4714 return MTLBlendOperationSubtract;
4715 case QRhiGraphicsPipeline::ReverseSubtract:
4716 return MTLBlendOperationReverseSubtract;
4717 case QRhiGraphicsPipeline::Min:
4718 return MTLBlendOperationMin;
4719 case QRhiGraphicsPipeline::Max:
4720 return MTLBlendOperationMax;
4723 return MTLBlendOperationAdd;
4730 if (c.testFlag(QRhiGraphicsPipeline::R))
4731 f |= MTLColorWriteMaskRed;
4732 if (c.testFlag(QRhiGraphicsPipeline::G))
4733 f |= MTLColorWriteMaskGreen;
4734 if (c.testFlag(QRhiGraphicsPipeline::B))
4735 f |= MTLColorWriteMaskBlue;
4736 if (c.testFlag(QRhiGraphicsPipeline::A))
4737 f |= MTLColorWriteMaskAlpha;
4744 case QRhiGraphicsPipeline::Never:
4745 return MTLCompareFunctionNever;
4746 case QRhiGraphicsPipeline::Less:
4747 return MTLCompareFunctionLess;
4748 case QRhiGraphicsPipeline::Equal:
4749 return MTLCompareFunctionEqual;
4750 case QRhiGraphicsPipeline::LessOrEqual:
4751 return MTLCompareFunctionLessEqual;
4752 case QRhiGraphicsPipeline::Greater:
4753 return MTLCompareFunctionGreater;
4754 case QRhiGraphicsPipeline::NotEqual:
4755 return MTLCompareFunctionNotEqual;
4756 case QRhiGraphicsPipeline::GreaterOrEqual:
4757 return MTLCompareFunctionGreaterEqual;
4758 case QRhiGraphicsPipeline::Always:
4759 return MTLCompareFunctionAlways;
4762 return MTLCompareFunctionAlways;
4769 case QRhiGraphicsPipeline::StencilZero:
4770 return MTLStencilOperationZero;
4771 case QRhiGraphicsPipeline::Keep:
4772 return MTLStencilOperationKeep;
4773 case QRhiGraphicsPipeline::Replace:
4774 return MTLStencilOperationReplace;
4775 case QRhiGraphicsPipeline::IncrementAndClamp:
4776 return MTLStencilOperationIncrementClamp;
4777 case QRhiGraphicsPipeline::DecrementAndClamp:
4778 return MTLStencilOperationDecrementClamp;
4779 case QRhiGraphicsPipeline::Invert:
4780 return MTLStencilOperationInvert;
4781 case QRhiGraphicsPipeline::IncrementAndWrap:
4782 return MTLStencilOperationIncrementWrap;
4783 case QRhiGraphicsPipeline::DecrementAndWrap:
4784 return MTLStencilOperationDecrementWrap;
4787 return MTLStencilOperationKeep;
4794 case QRhiGraphicsPipeline::Triangles:
4795 return MTLPrimitiveTypeTriangle;
4796 case QRhiGraphicsPipeline::TriangleStrip:
4797 return MTLPrimitiveTypeTriangleStrip;
4798 case QRhiGraphicsPipeline::Lines:
4799 return MTLPrimitiveTypeLine;
4800 case QRhiGraphicsPipeline::LineStrip:
4801 return MTLPrimitiveTypeLineStrip;
4802 case QRhiGraphicsPipeline::Points:
4803 return MTLPrimitiveTypePoint;
4806 return MTLPrimitiveTypeTriangle;
4813 case QRhiGraphicsPipeline::Triangles:
4814 case QRhiGraphicsPipeline::TriangleStrip:
4815 case QRhiGraphicsPipeline::TriangleFan:
4816 return MTLPrimitiveTopologyClassTriangle;
4817 case QRhiGraphicsPipeline::Lines:
4818 case QRhiGraphicsPipeline::LineStrip:
4819 return MTLPrimitiveTopologyClassLine;
4820 case QRhiGraphicsPipeline::Points:
4821 return MTLPrimitiveTopologyClassPoint;
4824 return MTLPrimitiveTopologyClassTriangle;
4831 case QRhiGraphicsPipeline::None:
4832 return MTLCullModeNone;
4833 case QRhiGraphicsPipeline::Front:
4834 return MTLCullModeFront;
4835 case QRhiGraphicsPipeline::Back:
4836 return MTLCullModeBack;
4839 return MTLCullModeNone;
4846 case QRhiGraphicsPipeline::Fill:
4847 return MTLTriangleFillModeFill;
4848 case QRhiGraphicsPipeline::Line:
4849 return MTLTriangleFillModeLines;
4852 return MTLTriangleFillModeFill;
4859 case QShaderDescription::CwTessellationWindingOrder:
4860 return MTLWindingClockwise;
4861 case QShaderDescription::CcwTessellationWindingOrder:
4862 return MTLWindingCounterClockwise;
4865 return MTLWindingCounterClockwise;
4872 case QShaderDescription::EqualTessellationPartitioning:
4873 return MTLTessellationPartitionModePow2;
4874 case QShaderDescription::FractionalEvenTessellationPartitioning:
4875 return MTLTessellationPartitionModeFractionalEven;
4876 case QShaderDescription::FractionalOddTessellationPartitioning:
4877 return MTLTessellationPartitionModeFractionalOdd;
4880 return MTLTessellationPartitionModePow2;
4886 int v = version.version();
4887 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
4891 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
4893 QVarLengthArray<
int, 8> versions;
4894 if (@available(macOS 13, iOS 16, *))
4896 if (@available(macOS 12, iOS 15, *))
4898 versions << 23 << 22 << 21 << 20 << 12;
4900 const QList<QShaderKey> shaders = shader.availableShaders();
4904 for (
const int &version : versions) {
4905 key = { QShader::Source::MetalLibShader, version, shaderVariant };
4906 if (shaders.contains(key))
4910 QShaderCode mtllib = shader.shader(key);
4911 if (!mtllib.shader().isEmpty()) {
4912 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
4913 size_t(mtllib.shader().size()),
4914 dispatch_get_global_queue(0, 0),
4915 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
4917 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
4918 dispatch_release(data);
4920 *entryPoint = mtllib.entryPoint();
4924 const QString msg = QString::fromNSString(err.localizedDescription);
4925 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
4929 for (
const int &version : versions) {
4930 key = { QShader::Source::MslShader, version, shaderVariant };
4931 if (shaders.contains(key))
4935 QShaderCode mslSource = shader.shader(key);
4936 if (mslSource.shader().isEmpty()) {
4937 qWarning() <<
"No MSL 2.0 or 1.2 code found in baked shader" << shader;
4941 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
4942 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
4943 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
4945 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
4953 const QString msg = QString::fromNSString(err.localizedDescription);
4958 *entryPoint = mslSource.entryPoint();
4965 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
4970 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
4974 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
4975 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
4976 rpDesc.colorAttachments[0].blendingEnabled =
false;
4978 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
4979 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
4981 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
4982 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
4983 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
4984 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
4985 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
4986 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
4987 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
4988 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
4989 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
4990 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
4991 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
4998 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
4999 rpDesc.depthAttachmentPixelFormat = fmt;
5000#if defined(Q_OS_MACOS)
5001 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
5003 if (fmt != MTLPixelFormatDepth32Float)
5005 rpDesc.stencilAttachmentPixelFormat = fmt;
5009 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
5014 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
5016 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
5017 dsDesc.depthWriteEnabled = m_depthWrite;
5018 if (m_stencilTest) {
5019 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
5020 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
5021 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
5022 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
5023 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
5024 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
5025 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
5027 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
5028 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
5029 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
5030 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
5031 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
5032 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
5033 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
5039 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
5040 d->cullMode = toMetalCullMode(m_cullMode);
5041 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
5042 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
5043 d->depthBias =
float(m_depthBias);
5044 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
5054 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5057 const uint loc = uint(it->location());
5058 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5059 desc.attributes[loc].offset = NSUInteger(it->offset());
5060 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5062 int bindingIndex = 0;
5063 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
5064 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5065 it != itEnd; ++it, ++bindingIndex)
5067 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5068 desc.layouts[layoutIdx].stepFunction =
5069 it->classification() == QRhiVertexInputBinding::PerInstance
5070 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
5071 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5072 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
5073 desc.layouts[layoutIdx].stepRate *= viewCount;
5074 desc.layouts[layoutIdx].stride = it->stride();
5085 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5088 const uint loc = uint(it->location());
5089 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5090 desc.attributes[loc].offset = NSUInteger(it->offset());
5091 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5093 int bindingIndex = 0;
5094 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5095 it != itEnd; ++it, ++bindingIndex)
5097 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5098 if (desc.indexBufferIndex) {
5099 desc.layouts[layoutIdx].stepFunction =
5100 it->classification() == QRhiVertexInputBinding::PerInstance
5101 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
5103 desc.layouts[layoutIdx].stepFunction =
5104 it->classification() == QRhiVertexInputBinding::PerInstance
5105 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
5107 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5108 desc.layouts[layoutIdx].stride = it->stride();
5115 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5116 rpDesc.binaryArchives = binArchArray;
5124 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
5125 const QString msg = QString::fromNSString(err.localizedDescription);
5126 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
5135 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5136 d->setupVertexInputDescriptor(vertexDesc);
5138 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5139 rpDesc.vertexDescriptor = vertexDesc;
5147 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5148 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5149 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5150 switch (shaderStage.type()) {
5151 case QRhiShaderStage::Vertex:
5154 [d->vs.func retain];
5155 rpDesc.vertexFunction = d->vs.func;
5157 case QRhiShaderStage::Fragment:
5160 [d->fs.func retain];
5161 rpDesc.fragmentFunction = d->fs.func;
5167 const QShader shader = shaderStage.shader();
5169 QByteArray entryPoint;
5170 QShaderKey activeKey;
5171 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5172 &error, &entryPoint, &activeKey);
5174 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
5177 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5179 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5183 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5185 for (QMetalShader &s : rhiD->d->shaderCache)
5187 rhiD->d->shaderCache.clear();
5189 switch (shaderStage.type()) {
5190 case QRhiShaderStage::Vertex:
5193 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5194 d->vs.desc = shader.description();
5195 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5196 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5198 [d->vs.func retain];
5199 rpDesc.vertexFunction = func;
5201 case QRhiShaderStage::Fragment:
5204 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5205 d->fs.desc = shader.description();
5206 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5207 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5209 [d->fs.func retain];
5210 rpDesc.fragmentFunction = func;
5223 if (m_multiViewCount >= 2)
5224 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5226 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5228 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5229 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5232 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5235 const QString msg = QString::fromNSString(err.localizedDescription);
5236 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
5240 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5242 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5245 d->primitiveType = toMetalPrimitiveType(m_topology);
5253 switch (vertexCompVariant) {
5254 case QShader::NonIndexedVertexAsComputeShader:
5256 case QShader::UInt32IndexedVertexAsComputeShader:
5258 case QShader::UInt16IndexedVertexAsComputeShader:
5268 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5269 if (varIndex >= 0 && vertexComputeState[varIndex])
5270 return vertexComputeState[varIndex];
5272 id<MTLFunction> func = nil;
5274 func = compVs[varIndex].func;
5277 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
5281 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5282 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5284 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5285 cpDesc.computeFunction = func;
5286 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5287 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5288 if (indexBufferBinding >= 0) {
5289 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5290 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5291 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5292 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5293 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5294 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5297 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5299 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5301 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5302 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5305 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5306 options: MTLPipelineOptionNone
5311 const QString msg = QString::fromNSString(err.localizedDescription);
5312 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5314 vertexComputeState[varIndex] = ps;
5322 if (tessControlComputeState)
5323 return tessControlComputeState;
5325 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5326 cpDesc.computeFunction = compTesc.func;
5328 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5330 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5331 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5334 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5335 options: MTLPipelineOptionNone
5340 const QString msg = QString::fromNSString(err.localizedDescription);
5341 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5343 tessControlComputeState = ps;
5351 return (indices >> index) & 0x1;
5354static inline void takeIndex(quint32 index, quint64 &indices)
5356 indices |= 1 << index;
5365 static const int maxVertexAttributes = 31;
5367 for (
int index = 0; index < maxVertexAttributes; ++index) {
5368 if (!indexTaken(index, indices))
5372 Q_UNREACHABLE_RETURN(-1);
5375static inline int aligned(quint32 offset, quint32 alignment)
5377 return ((offset + alignment - 1) / alignment) * alignment;
5385 for (
const int dim : variable.arrayDims)
5388 if (variable.type == QShaderDescription::VariableType::Struct) {
5389 for (
int element = 0; element < elements; ++element) {
5390 for (
const auto &member : variable.structMembers) {
5391 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5395 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5396 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5399 const quint32 alignment = size;
5400 vertexAlignment =
std::max(vertexAlignment, alignment);
5402 for (
int element = 0; element < elements; ++element) {
5404 offset = aligned(offset, alignment);
5411static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5415 for (
const int dim : variable.arrayDims)
5418 if (variable.type == QShaderDescription::VariableType::Struct) {
5419 for (
int element = 0; element < elements; ++element) {
5420 for (
const auto &member : variable.structMembers) {
5421 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5425 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5426 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5429 const quint32 alignment = size;
5430 vertexAlignment =
std::max(vertexAlignment, alignment);
5432 for (
int element = 0; element < elements; ++element) {
5433 Q_ASSERT(!indexTaken(index, indices));
5436 offset = aligned(offset, alignment);
5438 attributes[index].bufferIndex = binding;
5439 attributes[index].format = toMetalAttributeFormat(format);
5440 attributes[index].offset = offset;
5442 takeIndex(index, indices);
5444 if (indexTaken(index, indices))
5445 index = nextAttributeIndex(indices);
5452static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
5454 if (a.size() == b.size()) {
5456 for (
int i = 0; i < a.size() && match; ++i) {
5457 match &= a[i].type == b[i].type
5458 && a[i].arrayDims == b[i].arrayDims
5459 && matches(a[i].structMembers, b[i].structMembers);
5467static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
5469 return a.location == b.location
5471 && a.perPatch == b.perPatch
5472 && matches(a.structMembers, b.structMembers);
5521 if (pipeline
->d->ps)
5522 return pipeline
->d->ps;
5524 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5525 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5528 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5529 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5530 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5531 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5532 quint32 offsetInTescOutput = 0;
5533 quint32 offsetInTescPatchOutput = 0;
5534 quint32 offsetInTessFactorBuffer = 0;
5535 quint32 tescOutputAlignment = 0;
5536 quint32 tescPatchOutputAlignment = 0;
5537 quint32 tessFactorAlignment = 0;
5538 QSet<
int> usedBuffers;
5541 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
5542 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
5543 tescOutVars[tescOutVar.location] = tescOutVar;
5546 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
5547 for (
const auto &teseInVar : vertTese.desc.inputVariables())
5548 teseInVars[teseInVar.location] = teseInVar;
5551 quint64 indices = 0;
5553 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5555 int index = tescOutVar.location;
5557 quint32 *offset =
nullptr;
5558 quint32 *alignment =
nullptr;
5560 if (tescOutVar.perPatch) {
5561 binding = tescPatchOutputBufferBinding;
5562 offset = &offsetInTescPatchOutput;
5563 alignment = &tescPatchOutputAlignment;
5565 tescOutVar.arrayDims.removeLast();
5566 binding = tescOutputBufferBinding;
5567 offset = &offsetInTescOutput;
5568 alignment = &tescOutputAlignment;
5571 if (teseInVars.contains(index)) {
5573 if (!matches(teseInVars[index], tescOutVar)) {
5574 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5575 qWarning() <<
" tesc out:" << tescOutVar;
5576 qWarning() <<
" tese in:" << teseInVars[index];
5579 if (binding != -1) {
5580 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5581 usedBuffers << binding;
5583 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5584 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5588 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5589 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5592 teseInVars.remove(tescOutVar.location);
5595 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5596 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
5599 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5600 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5601 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5604 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5605 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5606 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5608 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5609 bool tessLevelAdded =
false;
5611 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5613 QShaderDescription::InOutVariable variable;
5615 quint32 *offset =
nullptr;
5616 quint32 *alignment =
nullptr;
5618 switch (builtin.type) {
5619 case QShaderDescription::BuiltinType::PositionBuiltin:
5620 variable.type = QShaderDescription::VariableType::Vec4;
5621 binding = tescOutputBufferBinding;
5622 offset = &offsetInTescOutput;
5623 alignment = &tescOutputAlignment;
5625 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5626 variable.type = QShaderDescription::VariableType::Float;
5627 binding = tescOutputBufferBinding;
5628 offset = &offsetInTescOutput;
5629 alignment = &tescOutputAlignment;
5631 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5632 variable.type = QShaderDescription::VariableType::Float;
5633 variable.arrayDims = builtin.arrayDims;
5634 binding = tescOutputBufferBinding;
5635 offset = &offsetInTescOutput;
5636 alignment = &tescOutputAlignment;
5638 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
5639 variable.type = QShaderDescription::VariableType::Half4;
5640 binding = tessFactorBufferBinding;
5641 offset = &offsetInTessFactorBuffer;
5642 tessLevelAdded = trianglesMode;
5643 alignment = &tessFactorAlignment;
5645 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
5646 if (trianglesMode) {
5647 if (!tessLevelAdded) {
5648 variable.type = QShaderDescription::VariableType::Half4;
5649 binding = tessFactorBufferBinding;
5650 offsetInTessFactorBuffer = 0;
5651 offset = &offsetInTessFactorBuffer;
5652 alignment = &tessFactorAlignment;
5653 tessLevelAdded =
true;
5655 teseInBuiltins.remove(builtin.type);
5659 variable.type = QShaderDescription::VariableType::Half2;
5660 binding = tessFactorBufferBinding;
5661 offsetInTessFactorBuffer = 8;
5662 offset = &offsetInTessFactorBuffer;
5663 alignment = &tessFactorAlignment;
5671 if (teseInBuiltins.contains(builtin.type)) {
5672 if (binding != -1) {
5673 int index = nextAttributeIndex(indices);
5674 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5675 usedBuffers << binding;
5677 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5678 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5681 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5684 teseInBuiltins.remove(builtin.type);
5687 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
5688 switch (builtin.type) {
5689 case QShaderDescription::BuiltinType::PositionBuiltin:
5690 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5691 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5692 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
5699 if (usedBuffers.contains(tescOutputBufferBinding)) {
5700 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
5701 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
5704 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
5705 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5706 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
5709 if (usedBuffers.contains(tessFactorBufferBinding)) {
5710 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5711 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
5714 rpDesc.vertexDescriptor = vertexDesc;
5715 rpDesc.vertexFunction = vertTese.func;
5716 rpDesc.fragmentFunction = pipeline
->d->fs.func;
5722 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
5724 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
5729 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5731 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5732 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5735 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5738 const QString msg = QString::fromNSString(err.localizedDescription);
5739 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
5743 pipeline->d->ps = ps;
5750 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
5753 for (QMetalBuffer *workBuf : *workBuffers) {
5754 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
5755 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5763 for (QMetalBuffer *workBuf : *workBuffers) {
5764 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
5765 workBuf->setSize(size);
5766 if (workBuf->create()) {
5767 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5778 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5781 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5785 workBuffers->append(buf);
5789 qWarning(
"Failed to acquire work buffer of size %u", size);
5797 QByteArray entryPoint;
5798 QShaderKey activeKey;
5800 const QShaderDescription tescDesc = tesc.description();
5801 const QShaderDescription teseDesc = tese.description();
5802 d->tess.inControlPointCount = uint(m_patchControlPointCount);
5803 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
5804 if (!
d->tess.outControlPointCount)
5805 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
5807 if (!
d->tess.outControlPointCount) {
5808 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
5809 d->tess.enabled =
false;
5810 d->tess.failed =
true;
5814 if (m_multiViewCount >= 2)
5815 qWarning(
"Multiview is not supported with tessellation");
5823 bool variantsPresent[3] = {};
5824 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
5825 for (
const QShaderKey &k : tessVertKeys) {
5826 switch (k.sourceVariant()) {
5827 case QShader::NonIndexedVertexAsComputeShader:
5828 variantsPresent[0] =
true;
5830 case QShader::UInt32IndexedVertexAsComputeShader:
5831 variantsPresent[1] =
true;
5833 case QShader::UInt16IndexedVertexAsComputeShader:
5834 variantsPresent[2] =
true;
5840 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
5841 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
5842 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
5843 "Try passing --msltess to qsb.");
5844 d->tess.enabled =
false;
5845 d->tess.failed =
true;
5850 for (QShader::Variant variant : {
5851 QShader::NonIndexedVertexAsComputeShader,
5852 QShader::UInt32IndexedVertexAsComputeShader,
5853 QShader::UInt16IndexedVertexAsComputeShader })
5855 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
5857 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
5858 d->tess.enabled =
false;
5859 d->tess.failed =
true;
5862 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5864 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5866 d->tess.enabled =
false;
5867 d->tess.failed =
true;
5870 QMetalShader &compVs(d->tess.compVs[varIndex]);
5873 compVs.desc = tessVert.description();
5874 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
5875 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
5878 if (!d->tess.vsCompPipeline(rhiD, variant)) {
5879 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
5880 d->tess.enabled =
false;
5881 d->tess.failed =
true;
5889 id<MTLLibrary> tessControlLib = rhiD->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
5890 if (!tessControlLib) {
5891 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
5892 d->tess.enabled =
false;
5893 d->tess.failed =
true;
5896 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
5897 if (!tessControlFunc) {
5898 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5899 [tessControlLib release];
5900 d->tess.enabled =
false;
5901 d->tess.failed =
true;
5904 d->tess.compTesc.lib = tessControlLib;
5905 d->tess.compTesc.func = tessControlFunc;
5906 d->tess.compTesc.desc = tesc.description();
5907 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
5908 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
5909 if (!
d->tess.tescCompPipeline(rhiD)) {
5910 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
5911 d->tess.enabled =
false;
5912 d->tess.failed =
true;
5917 id<MTLLibrary> tessEvalLib = rhiD->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
5919 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
5920 d->tess.enabled =
false;
5921 d->tess.failed =
true;
5924 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
5925 if (!tessEvalFunc) {
5926 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5927 [tessEvalLib release];
5928 d->tess.enabled =
false;
5929 d->tess.failed =
true;
5932 d->tess.vertTese.lib = tessEvalLib;
5933 d->tess.vertTese.func = tessEvalFunc;
5934 d->tess.vertTese.desc = tese.description();
5935 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
5936 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
5938 id<MTLLibrary> fragLib = rhiD->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
5940 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
5941 d->tess.enabled =
false;
5942 d->tess.failed =
true;
5945 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
5947 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5949 d->tess.enabled =
false;
5950 d->tess.failed =
true;
5953 d->fs.lib = fragLib;
5954 d->fs.func = fragFunc;
5955 d->fs.desc = tessFrag.description();
5956 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
5957 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
5959 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
5960 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
5961 d->tess.enabled =
false;
5962 d->tess.failed =
true;
5966 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5968 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5982 rhiD->pipelineCreationStart();
5983 if (!rhiD->sanityCheckGraphicsPipeline(
this))
5991 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5992 switch (shaderStage.type()) {
5993 case QRhiShaderStage::Vertex:
5994 tessVert = shaderStage.shader();
5996 case QRhiShaderStage::TessellationControl:
5997 tesc = shaderStage.shader();
5999 case QRhiShaderStage::TessellationEvaluation:
6000 tese = shaderStage.shader();
6002 case QRhiShaderStage::Fragment:
6003 tessFrag = shaderStage.shader();
6009 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
6010 d->tess.failed =
false;
6012 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
6018 QVarLengthArray<QMetalShader *, 6> shaders;
6019 if (
d->tess.enabled) {
6020 shaders.append(&
d->tess.compVs[0]);
6021 shaders.append(&
d->tess.compVs[1]);
6022 shaders.append(&
d->tess.compVs[2]);
6023 shaders.append(&
d->tess.compTesc);
6024 shaders.append(&
d->tess.vertTese);
6026 shaders.append(&
d->vs);
6028 shaders.append(&
d->fs);
6030 for (QMetalShader *shader : shaders) {
6031 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6032 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6033 shader->nativeResourceBindingMap[binding] = qMakePair(binding, -1);
6034 int maxNativeBinding = 0;
6035 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
6036 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
6040 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
6045 if (!d->bufferSizeBuffer)
6046 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6052 rhiD->pipelineCreationEnd();
6055 rhiD->registerResource(
this);
6084 e.computePipeline.pipelineState =
d->ps;
6089 rhiD
->d->releaseQueue.append(e);
6090 rhiD->unregisterResource(
this);
6097 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6098 cpDesc.binaryArchives = binArchArray;
6106 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
6107 const QString msg = QString::fromNSString(err.localizedDescription);
6108 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
6119 rhiD->pipelineCreationStart();
6121 auto cacheIt = rhiD->d->shaderCache.constFind(m_shaderStage);
6122 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
6125 const QShader shader = m_shaderStage.shader();
6127 QByteArray entryPoint;
6128 QShaderKey activeKey;
6129 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
6130 &error, &entryPoint, &activeKey);
6132 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6135 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
6137 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6143 d->cs.localSize = shader.description().computeShaderLocalSize();
6144 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6145 d->cs.desc = shader.description();
6146 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6149 if (d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6150 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6151 d->cs.nativeResourceBindingMap[binding] = qMakePair(binding, -1);
6154 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6155 for (QMetalShader &s : rhiD->d->shaderCache)
6157 rhiD
->d->shaderCache.clear();
6159 rhiD->d->shaderCache.insert(m_shaderStage, d->cs);
6163 [d->cs.func retain];
6165 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
6167 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6168 cpDesc.computeFunction =
d->cs.func;
6170 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6172 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6173 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6176 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6177 options: MTLPipelineOptionNone
6182 const QString msg = QString::fromNSString(err.localizedDescription);
6183 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6188 if (d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6190 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
6191 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
6195 if (!d->bufferSizeBuffer)
6196 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6202 rhiD->pipelineCreationEnd();
6205 rhiD->registerResource(
this);
6229 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6230 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6231 return &nativeHandlesStruct;
6237 d->currentRenderPassEncoder = nil;
6238 d->currentComputePassEncoder = nil;
6239 d->tessellationComputeEncoder = nil;
6240 d->currentPassRpDesc = nil;
6247 currentTarget =
nullptr;
6255 currentPipelineGeneration = 0;
6258 currentSrbGeneration = 0;
6261 currentIndexOffset = 0;
6262 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
6267 currentDepthBiasValues = { 0.0f, 0.0f };
6269 d->currentShaderResourceBindingState = {};
6270 d->currentDepthStencilState = nil;
6272 d->currentVertexInputsBuffers.clear();
6273 d->currentVertexInputOffsets.clear();
6283 d->sem[i] =
nullptr;
6284 d->msaaTex[i] = nil;
6304 dispatch_release(
d->sem[i]);
6305 d->sem[i] =
nullptr;
6310 [d->msaaTex[i] release];
6311 d->msaaTex[i] = nil;
6317 [d->curDrawable release];
6318 d->curDrawable = nil;
6322 rhiD->swapchains.remove(
this);
6323 rhiD->unregisterResource(
this);
6343 CALayer *layer =
nullptr;
6345 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
6346 layer = cocoaWindow->contentLayer();
6348 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
6351 return static_cast<CAMetalLayer *>(layer);
6360 d.reserved[0] = layerForWindow(window);
6367 CAMetalLayer *layer =
d->layer;
6369 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6372 int height = (
int)layer.bounds.size.height;
6373 int width = (
int)layer.bounds.size.width;
6374 width *= layer.contentsScale;
6375 height *= layer.contentsScale;
6376 return QSize(width, height);
6381 if (f == HDRExtendedSrgbLinear) {
6382 if (@available(iOS 16.0, *))
6386 }
else if (f == HDR10) {
6387 if (@available(iOS 16.0, *))
6391 }
else if (f == HDRExtendedDisplayP3Linear) {
6405 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
6411 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6412 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6414 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6417 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
6421 rhiD->registerResource(rpD,
false);
6428 samples = rhiD->effectiveSampleCount(m_sampleCount);
6430 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
6431 d->colorFormat = MTLPixelFormatRGBA16Float;
6432 d->rhiColorFormat = QRhiTexture::RGBA16F;
6435 if (m_format == HDR10) {
6436 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
6437 d->rhiColorFormat = QRhiTexture::RGB10A2;
6440 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6441 d->rhiColorFormat = QRhiTexture::BGRA8;
6450 dispatch_semaphore_t sem =
d->sem[slot];
6451 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6452 dispatch_semaphore_signal(sem);
6459 const bool needsRegistration = !window || window != m_window;
6461 if (window && window != m_window)
6466 if (needsRegistration || !rhiD->swapchains.contains(
this))
6467 rhiD->swapchains.insert(
this);
6471 if (window->surfaceType() != QSurface::MetalSurface) {
6472 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
6476 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6480 if (
d->colorFormat !=
d->layer.pixelFormat)
6481 d->layer.pixelFormat =
d->colorFormat;
6483 if (m_format == HDRExtendedSrgbLinear) {
6484 if (@available(iOS 16.0, *)) {
6485 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6486 d->layer.wantsExtendedDynamicRangeContent = YES;
6488 }
else if (m_format == HDR10) {
6489 if (@available(iOS 16.0, *)) {
6490 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
6491 d->layer.wantsExtendedDynamicRangeContent = YES;
6493 }
else if (m_format == HDRExtendedDisplayP3Linear) {
6494 if (@available(iOS 16.0, *)) {
6495 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6496 d->layer.wantsExtendedDynamicRangeContent = YES;
6500 if (m_flags.testFlag(UsedAsTransferSource))
6501 d->layer.framebufferOnly = NO;
6504 if (m_flags.testFlag(NoVSync))
6505 d->layer.displaySyncEnabled = NO;
6508 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6509 d->layer.opaque = NO;
6510 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6515 d->layer.opaque = NO;
6517 d->layer.opaque = YES;
6523 int width = (
int)
d->layer.bounds.size.width;
6524 int height = (
int)
d->layer.bounds.size.height;
6525 CGSize layerSize = CGSizeMake(width, height);
6526 const float scaleFactor =
d->layer.contentsScale;
6527 layerSize.width *= scaleFactor;
6528 layerSize.height *= scaleFactor;
6529 d->layer.drawableSize = layerSize;
6531 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6532 pixelSize = m_currentPixelSize;
6534 [d->layer setDevice: rhiD->d->dev];
6536 [d->curDrawable release];
6537 d->curDrawable = nil;
6548 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
6549 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
6550 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6551 m_depthStencil->sampleCount(), m_sampleCount);
6553 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
6554 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
6555 m_depthStencil->setPixelSize(pixelSize);
6556 if (!m_depthStencil->create())
6557 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6558 pixelSize.width(), pixelSize.height());
6560 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6561 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
6562 pixelSize.width(), pixelSize.height());
6566 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
6567 rtWrapper.d->pixelSize = pixelSize;
6573 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6574 pixelSize.width(), pixelSize.height(), scaleFactor);
6577 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6578 desc.textureType = MTLTextureType2DMultisample;
6579 desc.pixelFormat =
d->colorFormat;
6580 desc.width = NSUInteger(pixelSize.width());
6581 desc.height = NSUInteger(pixelSize.height());
6582 desc.sampleCount = NSUInteger(
samples);
6583 desc.resourceOptions = MTLResourceStorageModePrivate;
6584 desc.storageMode = MTLStorageModePrivate;
6585 desc.usage = MTLTextureUsageRenderTarget;
6587 if (
d->msaaTex[i]) {
6591 e.renderbuffer.texture =
d->msaaTex[i];
6592 rhiD
->d->releaseQueue.append(e);
6594 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6599 rhiD->registerResource(
this);
6615#if defined(Q_OS_MACOS)
6616 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
6617 NSScreen *screen = view.window.screen;
6618 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6619 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6620#elif defined(Q_OS_IOS)
6621 if (@available(iOS 16.0, *)) {
6622 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
6623 UIScreen *screen = view.window.windowScene.screen;
6624 info.limits.colorComponentValue.maxColorComponentValue = view.window.windowScene.screen.currentEDRHeadroom;
6625 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.potentialEDRHeadroom;
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Combined button and popup list for selecting options.
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.
id< MTLComputePipelineState > pipelineState
id< MTLDepthStencilState > depthStencilState
std::array< id< MTLComputePipelineState >, 3 > tessVertexComputeState
id< MTLRasterizationRateMap > rateMap
id< MTLSamplerState > samplerState
id< MTLBuffer > stagingBuffers[QMTL_FRAMES_IN_FLIGHT]
id< MTLComputePipelineState > tessTessControlComputeState
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